diff --git a/server/async.c b/server/async.c index 440cb2e27ed713f57c2bb671c3f1a4f2518d9f2f..8307e20c0bcab879d2e09f99ddc212835942bfcc 100644 --- a/server/async.c +++ b/server/async.c @@ -62,6 +62,35 @@ static const struct object_ops async_ops = async_destroy /* destroy */ }; + +struct async_queue +{ + struct object obj; /* object header */ + struct fd *fd; /* file descriptor owning this queue */ + struct list queue; /* queue of async objects */ +}; + +static void async_queue_dump( struct object *obj, int verbose ); +static void async_queue_destroy( struct object *obj ); + +static const struct object_ops async_queue_ops = +{ + sizeof(struct async_queue), /* size */ + async_queue_dump, /* dump */ + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ + no_map_access, /* map_access */ + no_lookup_name, /* lookup_name */ + no_open_file, /* open_file */ + no_close_handle, /* close_handle */ + async_queue_destroy /* destroy */ +}; + + static void async_dump( struct object *obj, int verbose ) { struct async *async = (struct async *)obj; @@ -79,6 +108,21 @@ static void async_destroy( struct object *obj ) release_object( async->thread ); } +static void async_queue_dump( struct object *obj, int verbose ) +{ + struct async_queue *async_queue = (struct async_queue *)obj; + assert( obj->ops == &async_queue_ops ); + fprintf( stderr, "Async queue fd=%p\n", async_queue->fd ); +} + +static void async_queue_destroy( struct object *obj ) +{ + struct async_queue *async_queue = (struct async_queue *)obj; + assert( obj->ops == &async_queue_ops ); + + async_wake_up( async_queue, STATUS_HANDLES_CLOSED ); +} + /* notifies client thread of new status of its async request */ /* destroys the server side of it */ static void async_terminate( struct async *async, unsigned int status ) @@ -108,9 +152,22 @@ static void async_timeout( void *private ) async_terminate( async, STATUS_TIMEOUT ); } +/* create a new async queue for a given fd */ +struct async_queue *create_async_queue( struct fd *fd ) +{ + struct async_queue *queue = alloc_object( &async_queue_ops ); + + if (queue) + { + queue->fd = fd; + list_init( &queue->queue ); + } + return queue; +} + /* create an async on a given queue of a fd */ struct async *create_async( struct thread *thread, const struct timeval *timeout, - struct list *queue, const async_data_t *data ) + struct async_queue *queue, const async_data_t *data ) { struct event *event = NULL; struct async *async; @@ -128,7 +185,7 @@ struct async *create_async( struct thread *thread, const struct timeval *timeout async->event = event; async->data = *data; - list_add_tail( queue, &async->queue_entry ); + list_add_tail( &queue->queue, &async->queue_entry ); if (timeout) async->timeout = add_timeout_user( timeout, async_timeout, async ); else async->timeout = NULL; @@ -164,21 +221,23 @@ void async_set_result( struct object *obj, unsigned int status ) } } -/* terminate the async operation at the head of the queue */ -void async_terminate_head( struct list *queue, unsigned int status ) +/* check if an async operation is waiting to be alerted */ +int async_waiting( struct async_queue *queue ) { - struct list *ptr = list_head( queue ); - if (ptr) async_terminate( LIST_ENTRY( ptr, struct async, queue_entry ), status ); + return queue && !list_empty( &queue->queue ); } -/* terminate all async operations on the queue */ -void async_terminate_queue( struct list *queue, unsigned int status ) +/* wake up async operations on the queue */ +void async_wake_up( struct async_queue *queue, unsigned int status ) { struct list *ptr, *next; - LIST_FOR_EACH_SAFE( ptr, next, queue ) + if (!queue) return; + + LIST_FOR_EACH_SAFE( ptr, next, &queue->queue ) { struct async *async = LIST_ENTRY( ptr, struct async, queue_entry ); async_terminate( async, status ); + if (status == STATUS_ALERTED) break; /* only wake up the first one */ } } diff --git a/server/change.c b/server/change.c index 70eb54e47ed5fe7e159d7252db19a7903e78ac27..c4696a0a80f5e46e88d5f36fbe729b7241545fe0 100644 --- a/server/change.c +++ b/server/change.c @@ -572,7 +572,7 @@ static void inotify_do_change_notify( struct dir *dir, unsigned int action, list_add_tail( &dir->change_records, &record->entry ); } - fd_async_terminate_head( dir->fd, ASYNC_TYPE_WAIT, STATUS_ALERTED ); + fd_async_wake_up( dir->fd, ASYNC_TYPE_WAIT, STATUS_ALERTED ); } static unsigned int filter_from_event( struct inotify_event *ie ) @@ -1097,7 +1097,7 @@ DECL_HANDLER(read_directory_changes) /* if there's already a change in the queue, send it */ if (!list_empty( &dir->change_records )) - fd_async_terminate_head( dir->fd, ASYNC_TYPE_WAIT, STATUS_ALERTED ); + fd_async_wake_up( dir->fd, ASYNC_TYPE_WAIT, STATUS_ALERTED ); /* setup the real notification */ if (!inotify_adjust_changes( dir )) diff --git a/server/fd.c b/server/fd.c index baab135afd0294afbc095efbb412e93a291c6cf1..ffaf404edbee708574e9aa7184e44a30d003c60e 100644 --- a/server/fd.c +++ b/server/fd.c @@ -171,9 +171,9 @@ struct fd int fs_locks :1; /* can we use filesystem locks for this fd? */ int unmounted :1;/* has the device been unmounted? */ int poll_index; /* index of fd in poll array */ - struct list read_q; /* async readers of this fd */ - struct list write_q; /* async writers of this fd */ - struct list wait_q; /* other async waiters of this fd */ + struct async_queue *read_q; /* async readers of this fd */ + struct async_queue *write_q; /* async writers of this fd */ + struct async_queue *wait_q; /* other async waiters of this fd */ }; static void fd_dump( struct object *obj, int verbose ); @@ -1286,9 +1286,9 @@ static void fd_destroy( struct object *obj ) { struct fd *fd = (struct fd *)obj; - async_terminate_queue( &fd->read_q, STATUS_CANCELLED ); - async_terminate_queue( &fd->write_q, STATUS_CANCELLED ); - async_terminate_queue( &fd->wait_q, STATUS_CANCELLED ); + if (fd->read_q) release_object( fd->read_q ); + if (fd->write_q) release_object( fd->write_q ); + if (fd->wait_q) release_object( fd->wait_q ); remove_fd_locks( fd ); list_remove( &fd->inode_entry ); @@ -1330,8 +1330,8 @@ static inline void unmount_fd( struct fd *fd ) { assert( fd->inode ); - async_terminate_queue( &fd->read_q, STATUS_VOLUME_DISMOUNTED ); - async_terminate_queue( &fd->write_q, STATUS_VOLUME_DISMOUNTED ); + async_wake_up( fd->read_q, STATUS_VOLUME_DISMOUNTED ); + async_wake_up( fd->write_q, STATUS_VOLUME_DISMOUNTED ); if (fd->poll_index != -1) set_fd_events( fd, -1 ); @@ -1363,11 +1363,11 @@ static struct fd *alloc_fd_object(void) fd->fs_locks = 1; fd->unmounted = 0; fd->poll_index = -1; + fd->read_q = NULL; + fd->write_q = NULL; + fd->wait_q = NULL; list_init( &fd->inode_entry ); list_init( &fd->locks ); - list_init( &fd->read_q ); - list_init( &fd->write_q ); - list_init( &fd->wait_q ); if ((fd->poll_index = add_poll_user( fd )) == -1) { @@ -1394,11 +1394,11 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use fd->fs_locks = 0; fd->unmounted = 0; fd->poll_index = -1; + fd->read_q = NULL; + fd->write_q = NULL; + fd->wait_q = NULL; list_init( &fd->inode_entry ); list_init( &fd->locks ); - list_init( &fd->read_q ); - list_init( &fd->write_q ); - list_init( &fd->wait_q ); return fd; } @@ -1692,27 +1692,16 @@ int default_fd_get_poll_events( struct fd *fd ) { int events = 0; - if (!list_empty( &fd->read_q )) - events |= POLLIN; - if (!list_empty( &fd->write_q )) - events |= POLLOUT; - + if (async_waiting( fd->read_q )) events |= POLLIN; + if (async_waiting( fd->write_q )) events |= POLLOUT; return events; } /* default handler for poll() events */ void default_poll_event( struct fd *fd, int event ) { - if (!list_empty( &fd->read_q ) && (POLLIN & event) ) - { - async_terminate_head( &fd->read_q, STATUS_ALERTED ); - return; - } - if (!list_empty( &fd->write_q ) && (POLLOUT & event) ) - { - async_terminate_head( &fd->write_q, STATUS_ALERTED ); - return; - } + if (event & POLLIN) async_wake_up( fd->read_q, STATUS_ALERTED ); + if (event & POLLOUT) async_wake_up( fd->write_q, STATUS_ALERTED ); /* if an error occurred, stop polling this fd to avoid busy-looping */ if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 ); @@ -1722,18 +1711,21 @@ void default_poll_event( struct fd *fd, int event ) int fd_queue_async_timeout( struct fd *fd, const async_data_t *data, int type, int count, const struct timeval *timeout ) { - struct list *queue; + struct async_queue *queue; switch (type) { case ASYNC_TYPE_READ: - queue = &fd->read_q; + if (!fd->read_q && !(fd->read_q = create_async_queue( fd ))) return 0; + queue = fd->read_q; break; case ASYNC_TYPE_WRITE: - queue = &fd->write_q; + if (!fd->write_q && !(fd->write_q = create_async_queue( fd ))) return 0; + queue = fd->write_q; break; case ASYNC_TYPE_WAIT: - queue = &fd->wait_q; + if (!fd->wait_q && !(fd->wait_q = create_async_queue( fd ))) return 0; + queue = fd->wait_q; break; default: set_error( STATUS_INVALID_PARAMETER ); @@ -1746,41 +1738,23 @@ int fd_queue_async_timeout( struct fd *fd, const async_data_t *data, int type, i if (!fd->inode) set_fd_events( fd, fd->fd_ops->get_poll_events( fd ) ); else /* regular files are always ready for read and write */ - if (type != ASYNC_TYPE_WAIT) async_terminate_head( queue, STATUS_ALERTED ); + if (type != ASYNC_TYPE_WAIT) async_wake_up( queue, STATUS_ALERTED ); return 1; } -void fd_async_terminate_head( struct fd *fd, int type, unsigned int status ) -{ - switch (type) - { - case ASYNC_TYPE_READ: - async_terminate_head( &fd->read_q, status ); - break; - case ASYNC_TYPE_WRITE: - async_terminate_head( &fd->write_q, status ); - break; - case ASYNC_TYPE_WAIT: - async_terminate_head( &fd->wait_q, status ); - break; - default: - assert(0); - } -} - -void fd_async_terminate_queue( struct fd *fd, int type, unsigned int status ) +void fd_async_wake_up( struct fd *fd, int type, unsigned int status ) { switch (type) { case ASYNC_TYPE_READ: - async_terminate_queue( &fd->read_q, status ); + async_wake_up( fd->read_q, status ); break; case ASYNC_TYPE_WRITE: - async_terminate_queue( &fd->write_q, status ); + async_wake_up( fd->write_q, status ); break; case ASYNC_TYPE_WAIT: - async_terminate_queue( &fd->wait_q, status ); + async_wake_up( fd->wait_q, status ); break; default: assert(0); @@ -1802,9 +1776,9 @@ void default_fd_queue_async( struct fd *fd, const async_data_t *data, int type, void default_fd_cancel_async( struct fd *fd ) { - async_terminate_queue( &fd->read_q, STATUS_CANCELLED ); - async_terminate_queue( &fd->write_q, STATUS_CANCELLED ); - async_terminate_queue( &fd->wait_q, STATUS_CANCELLED ); + async_wake_up( fd->read_q, STATUS_CANCELLED ); + async_wake_up( fd->write_q, STATUS_CANCELLED ); + async_wake_up( fd->wait_q, STATUS_CANCELLED ); } /* default flush() routine */ diff --git a/server/file.h b/server/file.h index f0f3a6befdabe2e6d6d49c3f636a1d236c595d60..9bae11276a17780802f0b83e9bdc49316e85c229 100644 --- a/server/file.h +++ b/server/file.h @@ -24,6 +24,7 @@ #include "object.h" struct fd; +struct async_queue; typedef unsigned __int64 file_pos_t; @@ -70,8 +71,7 @@ extern int default_fd_get_poll_events( struct fd *fd ); extern void default_poll_event( struct fd *fd, int event ); extern int fd_queue_async_timeout( struct fd *fd, const async_data_t *data, int type, int count, const struct timeval *timeout ); -extern void fd_async_terminate_head( struct fd *fd, int type, unsigned int status ); -extern void fd_async_terminate_queue( struct fd *fd, int type, unsigned int status ); +extern void fd_async_wake_up( struct fd *fd, int type, unsigned int status ); extern void default_fd_queue_async( struct fd *fd, const async_data_t *data, int type, int count ); extern void default_fd_cancel_async( struct fd *fd ); extern void no_flush( struct fd *fd, struct event **event ); @@ -124,11 +124,12 @@ extern int is_serial_fd( struct fd *fd ); extern struct object *create_serial( struct fd *fd, unsigned int options ); /* async I/O functions */ +extern struct async_queue *create_async_queue( struct fd *fd ); extern struct async *create_async( struct thread *thread, const struct timeval *timeout, - struct list *queue, const async_data_t *data ); + struct async_queue *queue, const async_data_t *data ); extern void async_set_result( struct object *obj, unsigned int status ); -extern void async_terminate_head( struct list *queue, unsigned int status ); -extern void async_terminate_queue( struct list *queue, unsigned int status ); +extern int async_waiting( struct async_queue *queue ); +extern void async_wake_up( struct async_queue *queue, unsigned int status ); /* access rights that require Unix read permission */ #define FILE_UNIX_READ_ACCESS (FILE_READ_DATA|FILE_READ_ATTRIBUTES|FILE_READ_EA) diff --git a/server/named_pipe.c b/server/named_pipe.c index a76398294ae62e441be8a34dadd0e85c684f857e..d76c2420d164452d3b9eea40c53de2eb499d770e 100644 --- a/server/named_pipe.c +++ b/server/named_pipe.c @@ -74,7 +74,7 @@ struct pipe_server struct named_pipe *pipe; struct timeout_user *flush_poll; struct event *event; - struct list wait_q; /* only a single one can be queued */ + struct async_queue *wait_q; /* only a single one can be queued */ unsigned int options; /* pipe options */ }; @@ -96,7 +96,7 @@ struct named_pipe unsigned int timeout; unsigned int instances; struct list servers; /* list of servers using this pipe */ - struct list waiters; /* list of clients waiting to connect */ + struct async_queue *waiters; /* list of clients waiting to connect */ }; struct named_pipe_device @@ -274,7 +274,7 @@ static void named_pipe_destroy( struct object *obj) assert( list_empty( &pipe->servers ) ); assert( !pipe->instances ); - async_terminate_queue( &pipe->waiters, STATUS_HANDLES_CLOSED ); + if (pipe->waiters) release_object( pipe->waiters ); } static struct fd *pipe_client_get_fd( struct object *obj ) @@ -366,7 +366,7 @@ static void pipe_server_destroy( struct object *obj) server->client = NULL; } - async_terminate_head( &server->wait_q, STATUS_HANDLES_CLOSED ); + release_object( server->wait_q ); assert( server->pipe->instances ); server->pipe->instances--; @@ -634,7 +634,7 @@ static struct pipe_server *create_pipe_server( struct named_pipe *pipe, unsigned server->client = NULL; server->flush_poll = NULL; server->options = options; - list_init( &server->wait_q ); + server->wait_q = create_async_queue( NULL ); list_add_head( &pipe->servers, &server->entry ); grab_object( pipe ); @@ -718,8 +718,7 @@ static struct object *named_pipe_open_file( struct object *obj, unsigned int acc if (client->fd && server->fd && res != 1) { if (server->state == ps_wait_open) - async_terminate_head( &server->wait_q, STATUS_SUCCESS ); - assert( list_empty( &server->wait_q ) ); + async_wake_up( server->wait_q, STATUS_SUCCESS ); server->state = ps_connected_server; server->client = client; client->server = server; @@ -753,8 +752,8 @@ DECL_HANDLER(create_named_pipe) { /* initialize it if it didn't already exist */ pipe->instances = 0; + pipe->waiters = NULL; list_init( &pipe->servers ); - list_init( &pipe->waiters ); pipe->insize = req->insize; pipe->outsize = req->outsize; pipe->maxinstances = req->maxinstances; @@ -805,8 +804,8 @@ DECL_HANDLER(connect_named_pipe) case ps_wait_connect: assert( !server->fd ); server->state = ps_wait_open; - create_async( current, NULL, &server->wait_q, &req->async ); - async_terminate_queue( &server->pipe->waiters, STATUS_SUCCESS ); + create_async( current, NULL, server->wait_q, &req->async ); + if (server->pipe->waiters) async_wake_up( server->pipe->waiters, STATUS_SUCCESS ); set_error( STATUS_PENDING ); break; case ps_connected_server: @@ -849,9 +848,14 @@ DECL_HANDLER(wait_named_pipe) server = find_available_server( pipe ); if (!server) { + if (!pipe->waiters && !(pipe->waiters = create_async_queue( NULL ))) + { + release_object( pipe ); + return; + } if (req->timeout == NMPWAIT_WAIT_FOREVER) { - if (create_async( current, NULL, &pipe->waiters, &req->async )) + if (create_async( current, NULL, pipe->waiters, &req->async )) set_error( STATUS_PENDING ); } else @@ -859,7 +863,7 @@ DECL_HANDLER(wait_named_pipe) struct timeval when = current_time; if (req->timeout == NMPWAIT_USE_DEFAULT_WAIT) add_timeout( &when, pipe->timeout ); else add_timeout( &when, req->timeout ); - if (create_async( current, &when, &pipe->waiters, &req->async )) + if (create_async( current, &when, pipe->waiters, &req->async )) set_error( STATUS_PENDING ); } } diff --git a/server/serial.c b/server/serial.c index 8cd59e26fbe6c0948e2af32c6758c51cb688292a..12401ac5177138671d612098bbf0e3cedfb3c88a 100644 --- a/server/serial.c +++ b/server/serial.c @@ -267,7 +267,7 @@ DECL_HANDLER(set_serial_info) serial->eventmask = req->eventmask; if (!serial->eventmask) { - fd_async_terminate_queue( serial->fd, ASYNC_TYPE_WAIT, STATUS_SUCCESS ); + fd_async_wake_up( serial->fd, ASYNC_TYPE_WAIT, STATUS_SUCCESS ); } } diff --git a/server/sock.c b/server/sock.c index 009d37eade77e7d4aad04760578f51878ea7aec8..b9938f7a016012ba78545171d62963e283406e02 100644 --- a/server/sock.c +++ b/server/sock.c @@ -83,8 +83,8 @@ struct sock obj_handle_t wparam; /* message wparam (socket handle) */ int errors[FD_MAX_EVENTS]; /* event errors */ struct sock *deferred; /* socket that waits for a deferred accept */ - struct list read_q; /* queue for asynchronous reads */ - struct list write_q; /* queue for asynchronous writes */ + struct async_queue *read_q; /* queue for asynchronous reads */ + struct async_queue *write_q; /* queue for asynchronous writes */ }; static void sock_dump( struct object *obj, int verbose ); @@ -245,16 +245,16 @@ static void sock_wake_up( struct sock *sock, int pollev ) if ( sock->flags & WSA_FLAG_OVERLAPPED ) { - if ( pollev & (POLLIN|POLLPRI) && !list_empty( &sock->read_q )) + if ( pollev & (POLLIN|POLLPRI) && async_waiting( sock->read_q )) { if (debug_level) fprintf( stderr, "activating read queue for socket %p\n", sock ); - async_terminate_head( &sock->read_q, STATUS_ALERTED ); + async_wake_up( sock->read_q, STATUS_ALERTED ); async_active = 1; } - if ( pollev & POLLOUT && !list_empty( &sock->write_q )) + if ( pollev & POLLOUT && async_waiting( sock->write_q )) { if (debug_level) fprintf( stderr, "activating write queue for socket %p\n", sock ); - async_terminate_head( &sock->write_q, STATUS_ALERTED ); + async_wake_up( sock->write_q, STATUS_ALERTED ); async_active = 1; } } @@ -483,9 +483,9 @@ static int sock_get_poll_events( struct fd *fd ) /* listening, wait for readable */ return (sock->hmask & FD_ACCEPT) ? 0 : POLLIN; - if (mask & (FD_READ) || (sock->flags & WSA_FLAG_OVERLAPPED && !list_empty( &sock->read_q ))) + if (mask & (FD_READ) || (sock->flags & WSA_FLAG_OVERLAPPED && async_waiting( sock->read_q ))) ev |= POLLIN | POLLPRI; - if (mask & FD_WRITE || (sock->flags & WSA_FLAG_OVERLAPPED && !list_empty( &sock->write_q ))) + if (mask & FD_WRITE || (sock->flags & WSA_FLAG_OVERLAPPED && async_waiting( sock->write_q ))) ev |= POLLOUT; /* We use POLLIN with 0 bytes recv() as FD_CLOSE indication for stream sockets. */ if ( sock->type == SOCK_STREAM && ( sock->mask & ~sock->hmask & FD_CLOSE) ) @@ -512,7 +512,7 @@ static enum server_fd_type sock_get_info( struct fd *fd, int *flags ) static void sock_queue_async( struct fd *fd, const async_data_t *data, int type, int count ) { struct sock *sock = get_fd_user( fd ); - struct list *queue; + struct async_queue *queue; int pollev; assert( sock->obj.ops == &sock_ops ); @@ -526,11 +526,13 @@ static void sock_queue_async( struct fd *fd, const async_data_t *data, int type, switch (type) { case ASYNC_TYPE_READ: - queue = &sock->read_q; + if (!sock->read_q && !(sock->read_q = create_async_queue( sock->fd ))) return; + queue = sock->read_q; sock->hmask &= ~FD_CLOSE; break; case ASYNC_TYPE_WRITE: - queue = &sock->write_q; + if (!sock->write_q && !(sock->write_q = create_async_queue( sock->fd ))) return; + queue = sock->write_q; break; default: set_error( STATUS_INVALID_PARAMETER ); @@ -557,8 +559,8 @@ static void sock_cancel_async( struct fd *fd ) struct sock *sock = get_fd_user( fd ); assert( sock->obj.ops == &sock_ops ); - async_terminate_queue( &sock->read_q, STATUS_CANCELLED ); - async_terminate_queue( &sock->write_q, STATUS_CANCELLED ); + async_wake_up( sock->read_q, STATUS_CANCELLED ); + async_wake_up( sock->write_q, STATUS_CANCELLED ); } static struct fd *sock_get_fd( struct object *obj ) @@ -577,11 +579,8 @@ static void sock_destroy( struct object *obj ) if ( sock->deferred ) release_object( sock->deferred ); - if ( sock->flags & WSA_FLAG_OVERLAPPED ) - { - async_terminate_queue( &sock->read_q, STATUS_CANCELLED ); - async_terminate_queue( &sock->write_q, STATUS_CANCELLED ); - } + if (sock->read_q) release_object( sock->read_q ); + if (sock->write_q) release_object( sock->write_q ); if (sock->event) release_object( sock->event ); if (sock->fd) { @@ -624,13 +623,13 @@ static struct object *create_socket( int family, int type, int protocol, unsigne sock->message = 0; sock->wparam = 0; sock->deferred = NULL; + sock->read_q = NULL; + sock->write_q = NULL; if (!(sock->fd = create_anonymous_fd( &sock_fd_ops, sockfd, &sock->obj ))) { release_object( sock ); return NULL; } - list_init( &sock->read_q ); - list_init( &sock->write_q ); sock_reselect( sock ); clear_error(); return &sock->obj; @@ -693,14 +692,14 @@ static struct sock *accept_socket( obj_handle_t handle ) if (sock->event) acceptsock->event = (struct event *)grab_object( sock->event ); acceptsock->flags = sock->flags; acceptsock->deferred = NULL; + acceptsock->read_q = NULL; + acceptsock->write_q = NULL; if (!(acceptsock->fd = create_anonymous_fd( &sock_fd_ops, acceptfd, &acceptsock->obj ))) { release_object( acceptsock ); release_object( sock ); return NULL; } - list_init( &acceptsock->read_q ); - list_init( &acceptsock->write_q ); } clear_error(); sock->pmask &= ~FD_ACCEPT;