named pipe oddity

Dan Kegel dank at kegel.com
Sat Feb 22 22:24:46 CST 2003


Mike McCormack wrote:
> Well, I'm curious to know what happens when you:
> * put a pipe into message mode, then do partial reads.
> * do TransactNamedPipe, and there is already read data
>    available in the pipe.
> * DisconnectNamedPipe on a pipe that still has messages in it
> * try reading from a pipe that is not connected (before and after 
> connecting)
> * switch pipes between message mode and byte mode with data in the pipe

Er, looks like it'll be a while before I test those,
but I do have a just-barely-nontrivial test;
wine fails it in several ways.  Attached for your
testing pleasure.
- Dan

-- 
Dan Kegel
http://www.kegel.com
http://counter.li.org/cgi-bin/runscript/display-person.cgi?user=78045
-------------- next part --------------
/*
 * Unit tests for named pipe functions in Wine
 *
 * Copyright (c) 2002 Dan Kegel
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#ifndef STANDALONE
#include "wine/test.h"
#else
#include <assert.h>
#define START_TEST(name) main(int argc, char **argv)
#define ok(condition, msg) assert(condition)
#define todo_wine
#endif

#include <wtypes.h>
#include <windef.h>
#include <winbase.h>
#include <winerror.h>

#define PIPENAME "\\\\.\\PiPe\\tests_" __FILE__

static void msg(const char *s)
{
	DWORD cbWritten;
	WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
		   s, strlen(s), &cbWritten, NULL);
}

void test_CreateNamedPipeA(void)
{
    HANDLE hnp;
    HANDLE hFile;
    const char obuf[] = "Bit Bucket";
    char ibuf[32];
    DWORD written;
    DWORD gelesen;

    /* Bad parameter checks */
    hnp = CreateNamedPipeA("not a named pipe", 
        PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_WAIT, 
        /* nMaxInstances */ 1,
        /* nOutBufSize */ 1024,
        /* nInBufSize */ 1024,
        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
        /* lpSecurityAttrib */ NULL);

    if (hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
        /* Is this the right way to notify user of skipped tests? */
        ok(hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, 
	    "CreateNamedPipe not supported on this platform, skipping tests.");
        return;
    }
    ok(hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_NAME,
        "CreateNamedPipe should fail if name doesn't start with \\\\.\\pipe");

    hnp = CreateNamedPipeA(NULL, 
        PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_WAIT, 
        1, 1024, 1024, NMPWAIT_USE_DEFAULT_WAIT, NULL);
    ok(hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PATH_NOT_FOUND,
        "CreateNamedPipe should fail if name is NULL");

    hFile = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, 
	NULL, OPEN_EXISTING, 0, 0);
    ok(hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND, "connecting to nonexistent named pipe should fail with ERROR_FILE_NOT_FOUND");

    /* Functional checks */

    hnp = CreateNamedPipeA(PIPENAME,
        PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_WAIT, 
        /* nMaxInstances */ 1,
        /* nOutBufSize */ 1024,
        /* nInBufSize */ 1024,
        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
        /* lpSecurityAttrib */ NULL);
    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed");

    hFile = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, 
            NULL, OPEN_EXISTING, 0, 0);
    todo_wine
    {
        ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed");
    }

    /* don't try to do i/o if one side couldn't be opened, as it hangs */
    if (hFile != INVALID_HANDLE_VALUE) {
	HANDLE hFile2;

	/* Make sure we can read and write a few bytes in both directions*/
	memset(ibuf, 0, sizeof(ibuf));
	ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile");
	ok(written == sizeof(obuf), "write file len");
	ok(ReadFile(hFile, ibuf, sizeof(obuf), &gelesen, NULL), "ReadFile");
	ok(gelesen == sizeof(obuf), "read file len");
	ok(memcmp(obuf, ibuf, written) == 0, "content check");

	memset(ibuf, 0, sizeof(ibuf));
	ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile");
	ok(written == sizeof(obuf), "write file len");
	ok(ReadFile(hnp, ibuf, sizeof(obuf), &gelesen, NULL), "ReadFile");
	ok(gelesen == sizeof(obuf), "read file len");
	ok(memcmp(obuf, ibuf, written) == 0, "content check");

	/* Picky conformance tests */

	/* Verify that you can't connect to pipe again
	 * until server calls DisconnectNamedPipe+ConnectNamedPipe
	 * or creates a new pipe
	 * case 1: other client not yet closed
	 */
	hFile2 = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, 
	    NULL, OPEN_EXISTING, 0, 0);
	ok(hFile2 == INVALID_HANDLE_VALUE, "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail");
	ok(GetLastError() == ERROR_PIPE_BUSY, "connecting to named pipe before other client closes should fail with ERROR_PIPE_BUSY");

	ok(CloseHandle(hFile), "CloseHandle");

	/* case 2: other client already closed */
	hFile = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, 
	    NULL, OPEN_EXISTING, 0, 0);
	ok(hFile == INVALID_HANDLE_VALUE, "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail");
	ok(GetLastError() == ERROR_PIPE_BUSY, "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail with ERROR_PIPE_BUSY");

	ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe");

	/* case 3: server has called DisconnectNamedPipe but not ConnectNamed Pipe */
	hFile = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, 
	    NULL, OPEN_EXISTING, 0, 0);
	ok(hFile == INVALID_HANDLE_VALUE, "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail");
	ok(GetLastError() == ERROR_PIPE_BUSY, "connecting to named pipe after other client closes but before ConnectNamedPipe should fail with ERROR_PIPE_BUSY");

	/* to be complete, we'd call ConnectNamedPipe here and loop,
	 * but by default that's blocking, so we'd either have
	 * to turn on the uncommon nonblocking mode, or
	 * use another thread.
	 */
    }

    ok(CloseHandle(hnp), "CloseHandle");

}

/** implementation of alarm() */
static DWORD CALLBACK alarmThreadMain(LPVOID arg)
{
	DWORD timeout = (DWORD) arg;
	msg("alarmThreadMain\n");
	Sleep(timeout);
	ok(FALSE, "alarm");
	ExitProcess(1);
	return 1;
}

/** Trivial byte echo server */
static DWORD CALLBACK serverThreadMain(LPVOID arg)
{
	HANDLE hnp = (HANDLE) arg;
	int i;
    for (i=0; ; i++) {
		char buf[512];
		DWORD written;
		DWORD gelesen;
		DWORD success;

		/* Wait for client to connect */
		msg("Server calling ConnectNamedPipe...\n");
		ok(ConnectNamedPipe(hnp, NULL) || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe");
		msg("ConnectNamedPipe returned.\n");

		/* Echo bytes once */
		memset(buf, 0, sizeof(buf));

		/* Hmm.  This read seems to completely hose wine.  Total lockup. */
		msg("Server reading...\n");
		success = ReadFile(hnp, buf, sizeof(buf), &gelesen, NULL);
		msg("Server done reading.\n");
	   	ok(success, "ReadFile");

		msg("Server writing...\n");
		ok(WriteFile(hnp, buf, gelesen, &written, NULL), "WriteFile");
		msg("Server done writing.\n");
		ok(written == gelesen, "write file len");

		/* finish this connection, wait for next one */
		ok(FlushFileBuffers(hnp), "FlushFileBuffers");
		ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe");
	}
}

void test_NamedPipe_2(void)
{
	HANDLE hnp;
	HANDLE serverThread;
	HANDLE alarmThread;
	DWORD serverThreadId;
	DWORD alarmThreadId;
	int i;

	/* Set up a ten second timeout */
    alarmThread = CreateThread(NULL,0,alarmThreadMain,(void *)10000,0,&alarmThreadId);

	/* Set up a simple echo server */
    hnp = CreateNamedPipeA(PIPENAME,
        PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_WAIT, 
        /* nMaxInstances */ 1,
        /* nOutBufSize */ 1024,
        /* nInBufSize */ 1024,
        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
        /* lpSecurityAttrib */ NULL);
    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed");
    serverThread = CreateThread(NULL,0,serverThreadMain,hnp,0,&serverThreadId);
	ok(serverThread != INVALID_HANDLE_VALUE, "CreateThread");

	for (i=0; i<3; i++) {
		HANDLE hFile;
		const char obuf[] = "Bit Bucket";
		char ibuf[32];
		DWORD written;
		DWORD gelesen;
		int loop;

		for (loop=0; loop<3; loop++) {
			msg("Client connecting...\n");
			/* Connect to the server */
			hFile = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, 
					NULL, OPEN_EXISTING, 0, 0);
			if (hFile != INVALID_HANDLE_VALUE) break;
			/* must be the second time around the loop, and server hasn't called ConnectNamedPipe yet */
			ok(GetLastError() == ERROR_PIPE_BUSY, "connecting to pipe");
			msg("connect failed, retrying\n");
			Sleep(10);
		}
		ok(hFile != INVALID_HANDLE_VALUE, "client opening named pipe");

		/* Make sure it can echo */
		memset(ibuf, 0, sizeof(ibuf));
		msg("Client writing...\n");
		ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile");
		ok(written == sizeof(obuf), "write file len");
		msg("Client reading...\n");
		ok(ReadFile(hFile, ibuf, sizeof(obuf), &gelesen, NULL), "ReadFile");
		ok(gelesen == sizeof(obuf), "read file len");
		ok(memcmp(obuf, ibuf, written) == 0, "content check");

		msg("Client closing...\n");
		ok(CloseHandle(hFile), "CloseHandle");
	}

	ok(TerminateThread(alarmThread, 0), "TerminateThread");
	ok(TerminateThread(serverThread, 0), "TerminateThread");
	ok(WaitForSingleObject(serverThread,5000)==WAIT_OBJECT_0,
     "TerminateThread didn't work");
	ok(CloseHandle(hnp), "CloseHandle");
	msg("test_NamedPipe_2 done\n");
}

START_TEST(pipe)
{
	test_NamedPipe_2();
    test_CreateNamedPipeA();
}


More information about the wine-devel mailing list