Named pipe patch 4 (supercedes 3)
Mike McCormack
mike at codeweavers.com
Thu May 8 03:57:34 CDT 2003
Hi,
This patch allows us to pass the WineHQ named pipe regression test, and
to get quite a way through Dan Kegel's named pipe test suite at:
http://www.kegel.com/pipe.c
Mike
ChangeLog:
* allow pipe to be opened without the server calling ConnectNamedPipe
* distinguish between a newly opened pipe and a disconnected pipe
* limit number of pipe servers to MaxInstances
* don't create incompatable pipe servers
* do the right thing for FileFlushBuffers
-------------- next part --------------
Index: server/named_pipe.c
===================================================================
RCS file: /home/wine/wine/server/named_pipe.c,v
retrieving revision 1.23
diff -u -r1.23 named_pipe.c
--- server/named_pipe.c 17 Apr 2003 02:14:04 -0000 1.23
+++ server/named_pipe.c 8 May 2003 08:49:39 -0000
@@ -53,7 +53,9 @@
ps_wait_connect,
ps_connected_server,
ps_connected_client,
- ps_disconnected
+ ps_wait_disconnect,
+ ps_disconnected_server,
+ ps_disconnected_client
};
struct named_pipe;
@@ -68,6 +70,8 @@
struct pipe_user *next;
struct pipe_user *prev;
struct thread *thread;
+ struct timeout_user *flush_poll;
+ struct event *event;
void *func;
void *overlapped;
};
@@ -80,6 +84,7 @@
unsigned int outsize;
unsigned int insize;
unsigned int timeout;
+ unsigned int instances;
struct pipe_user *users;
};
@@ -104,6 +109,7 @@
static int pipe_user_get_poll_events( struct fd *fd );
static int pipe_user_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags );
+static int pipe_user_flush( struct fd *fd, struct event *event );
static const struct object_ops pipe_user_ops =
{
@@ -121,7 +127,7 @@
{
pipe_user_get_poll_events, /* get_poll_events */
default_poll_event, /* poll_event */
- no_flush, /* flush */
+ pipe_user_flush, /* flush */
pipe_user_get_info, /* get_file_info */
no_queue_async /* queue_async */
};
@@ -144,6 +150,7 @@
{
struct named_pipe *pipe = (struct named_pipe *)obj;
assert( !pipe->users );
+ assert( !pipe->instances );
}
static void notify_waiter( struct pipe_user *user, unsigned int status)
@@ -163,20 +170,68 @@
static struct fd *pipe_user_get_fd( struct object *obj )
{
struct pipe_user *user = (struct pipe_user *)obj;
- if (user->fd) return (struct fd *)grab_object( user->fd );
- set_error( STATUS_PIPE_DISCONNECTED );
+ if (user->fd)
+ {
+ assert( (user->state == ps_connected_server ) ||
+ (user->state == ps_connected_client ) );
+ return (struct fd *)grab_object( user->fd );
+ }
+ switch(user->state)
+ {
+ case ps_wait_open:
+ case ps_idle_server:
+ set_error( STATUS_PIPE_LISTENING );
+ break;
+ case ps_disconnected_server:
+ case ps_disconnected_client:
+ default:
+ set_error( STATUS_PIPE_DISCONNECTED );
+ }
return NULL;
}
+
+static void notify_empty( struct pipe_user *user )
+{
+ if( !user->flush_poll )
+ return;
+ assert( (user->state == ps_connected_server ) ||
+ (user->state == ps_connected_client ) );
+ assert( user->event );
+ remove_timeout_user( user->flush_poll );
+ user->flush_poll = NULL;
+ set_event( user->event );
+ release_object( user->event );
+ user->event = NULL;
+}
+
static void pipe_user_destroy( struct object *obj)
{
struct pipe_user *user = (struct pipe_user *)obj;
assert( obj->ops == &pipe_user_ops );
+ notify_empty( user );
+ if( user->other )
+ notify_empty( user->other );
+
if(user->overlapped)
notify_waiter(user,STATUS_HANDLES_CLOSED);
+ assert( user->pipe->instances );
+
+ switch( user->state )
+ {
+ case ps_connected_server:
+ case ps_idle_server:
+ case ps_wait_open:
+ case ps_wait_disconnect:
+ case ps_disconnected_server:
+ user->pipe->instances--;
+ default:
+ break;
+ }
+
if(user->other)
{
release_object( user->other->fd );
@@ -184,10 +239,10 @@
switch(user->other->state)
{
case ps_connected_server:
- user->other->state = ps_idle_server;
+ user->other->state = ps_wait_disconnect;
break;
case ps_connected_client:
- user->other->state = ps_disconnected;
+ user->other->state = ps_disconnected_client;
break;
default:
fprintf(stderr,"connected pipe has strange state %d!\n",
@@ -211,6 +266,74 @@
return POLLIN | POLLOUT; /* FIXME */
}
+static int pipe_data_remaining( struct pipe_user *user )
+{
+ struct pollfd pfd;
+ int fd;
+
+ assert( user->other );
+
+ fd = get_unix_fd( user->other->fd );
+ if( fd < 0 )
+ return 0;
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+
+ if( 0 > poll( &pfd, 1, 0 ) )
+ return 0;
+
+ return pfd.revents&POLLIN;
+}
+
+static void pipe_check_flushed( void *arg )
+{
+ struct pipe_user *user = (struct pipe_user*) arg;
+
+ assert( user->event );
+ if( pipe_data_remaining( user ) )
+ {
+ struct timeval tv;
+
+ gettimeofday( &tv, 0 );
+ add_timeout( &tv, 100 );
+ user->flush_poll = add_timeout_user( &tv, pipe_check_flushed, user );
+ }
+}
+
+static int pipe_user_flush( struct fd *fd, struct event *event )
+{
+ struct pipe_user *user = get_fd_user( fd );
+
+ if( !user )
+ return 0;
+
+ if( (user->state != ps_connected_server ) &&
+ (user->state != ps_connected_client ) )
+ return 0;
+
+ /* FIXME: if multiple threads flush the same pipe,
+ maybe should create a list of processes to notify */
+ if( user->flush_poll )
+ return 0;
+
+ if( pipe_data_remaining( user ) )
+ {
+ struct timeval tv;
+
+ /* this kind of sux -
+ there's no unix way to be alerted when a pipe becomes empty */
+ user->event = event;
+ grab_object( event );
+ gettimeofday( &tv, 0 );
+ add_timeout( &tv, 100 );
+ user->flush_poll = add_timeout_user( &tv, pipe_check_flushed, user );
+ set_error( STATUS_PENDING );
+ }
+
+ return 0;
+}
+
static int pipe_user_get_info( struct fd *fd, struct get_file_info_reply *reply, int *flags )
{
if (reply)
@@ -240,6 +363,7 @@
{
/* initialize it if it didn't already exist */
pipe->users = 0;
+ pipe->instances = 0;
}
}
return pipe;
@@ -281,6 +405,7 @@
user->thread = NULL;
user->func = NULL;
user->overlapped = NULL;
+ user->flush_poll = NULL;
/* add to list of pipe users */
if ((user->next = pipe->users)) user->next->prev = user;
@@ -326,13 +451,31 @@
pipe->timeout = req->timeout;
pipe->pipemode = req->pipemode;
}
+ else
+ {
+ set_error( 0 ); /* clear the name collision */
+ if( pipe->maxinstances <= pipe->instances )
+ {
+ set_error( STATUS_PIPE_BUSY );
+ release_object( pipe );
+ return;
+ }
+ if( ( pipe->maxinstances != req->maxinstances ) ||
+ ( pipe->timeout != req->timeout ) ||
+ ( pipe->pipemode != req->pipemode ) )
+ {
+ set_error( STATUS_ACCESS_DENIED );
+ release_object( pipe );
+ return;
+ }
+ }
user = create_pipe_user( pipe );
-
if(user)
{
user->state = ps_idle_server;
reply->handle = alloc_handle( current->process, user, GENERIC_READ|GENERIC_WRITE, 0 );
+ user->pipe->instances++;
release_object( user );
}
@@ -343,28 +486,39 @@
{
struct pipe_user *user, *partner;
struct named_pipe *pipe;
+ int fds[2];
reply->handle = 0;
- if (!(pipe = open_named_pipe( get_req_data(), get_req_data_size() )))
+ pipe = open_named_pipe( get_req_data(), get_req_data_size() );
+ if ( !pipe )
{
set_error( STATUS_NO_SUCH_FILE );
return;
}
- if (!(partner = find_partner(pipe, ps_wait_open)))
+
+ for( partner = pipe->users; partner; partner=partner->next)
+ if( (partner->state==ps_idle_server) ||
+ //(partner->state==ps_disconnected_server) ||
+ (partner->state==ps_wait_open) )
+ break;
+
+ if ( !partner )
{
release_object(pipe);
set_error( STATUS_PIPE_NOT_AVAILABLE );
return;
}
- if ((user = create_pipe_user( pipe )))
- {
- int fds[2];
+ user = create_pipe_user( pipe );
+ if( user )
+ {
if(!socketpair(PF_UNIX, SOCK_STREAM, 0, fds))
{
- user->fd = create_anonymous_fd( &pipe_user_fd_ops, fds[1], &user->obj );
- partner->fd = create_anonymous_fd( &pipe_user_fd_ops, fds[0], &partner->obj );
+ user->fd = create_anonymous_fd( &pipe_user_fd_ops,
+ fds[1], &user->obj );
+ partner->fd = create_anonymous_fd( &pipe_user_fd_ops,
+ fds[0], &partner->obj );
if (user->fd && partner->fd)
{
notify_waiter(partner,STATUS_SUCCESS);
@@ -375,11 +529,12 @@
reply->handle = alloc_handle( current->process, user, req->access, 0 );
}
}
- else file_set_error();
+ else
+ file_set_error();
release_object( user );
}
- release_object( partner );
+
release_object( pipe );
}
@@ -391,12 +546,9 @@
if(!user)
return;
- if( user->state != ps_idle_server )
- {
- set_error(STATUS_PORT_ALREADY_SET);
- }
- else
+ switch( user->state )
{
+ case ps_idle_server:
user->state = ps_wait_open;
user->thread = (struct thread *)grab_object(current);
user->func = req->func;
@@ -408,6 +560,17 @@
notify_waiter(partner,STATUS_SUCCESS);
release_object(partner);
}
+ break;
+ case ps_connected_server:
+ set_error( STATUS_PIPE_CONNECTED );
+ break;
+ case ps_wait_disconnect:
+ case ps_disconnected_server:
+ set_error( STATUS_NO_DATA_DETECTED );
+ break;
+ default:
+ set_error( STATUS_INVALID_HANDLE );
+ break;
}
release_object(user);
@@ -454,18 +617,33 @@
user = get_pipe_user_obj(current->process, req->handle, 0);
if(!user)
return;
- if( (user->state == ps_connected_server) &&
- (user->other->state == ps_connected_client) )
+ switch( user->state )
{
+ case ps_connected_server:
+ assert (user->other->state == ps_connected_client);
+ assert (user->fd);
+ assert (user->other->fd);
+
+ notify_empty( user );
+
release_object( user->other->fd );
user->other->fd = NULL;
- user->other->state = ps_disconnected;
+ user->other->state = ps_disconnected_client;
user->other->other = NULL;
release_object( user->fd );
user->fd = NULL;
- user->state = ps_idle_server;
+ user->state = ps_disconnected_server;
user->other = NULL;
+ break;
+ case ps_wait_disconnect:
+ assert (!user->fd);
+ assert (!user->other);
+ notify_empty( user );
+ user->state = ps_disconnected_server;
+ break;
+ default:
+ set_error( STATUS_PIPE_DISCONNECTED );
}
release_object(user);
}
More information about the wine-patches
mailing list