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
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); }