server: Add support for restarting an async I/O when the client side couldn't finish it right away.
diff --git a/server/async.c b/server/async.c
index e0877b2..8b3049c 100644
--- a/server/async.c
+++ b/server/async.c
@@ -38,6 +38,7 @@
struct thread *thread; /* owning thread */
struct list queue_entry; /* entry in async queue list */
struct async_queue *queue; /* queue containing this async */
+ unsigned int status; /* current status */
struct timeout_user *timeout;
unsigned int timeout_status; /* status to report upon timeout */
struct event *event;
@@ -92,6 +93,11 @@
};
+static inline void async_reselect( struct async *async )
+{
+ if (async->queue->fd) fd_reselect_async( async->queue->fd, async->queue );
+}
+
static void async_dump( struct object *obj, int verbose )
{
struct async *async = (struct async *)obj;
@@ -104,10 +110,12 @@
struct async *async = (struct async *)obj;
assert( obj->ops == &async_ops );
+ list_remove( &async->queue_entry );
+ async_reselect( async );
+
if (async->timeout) remove_timeout_user( async->timeout );
if (async->event) release_object( async->event );
release_object( async->queue );
- async->queue = NULL;
release_object( async->thread );
}
@@ -119,11 +127,19 @@
}
/* 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 )
{
apc_call_t data;
+ assert( status != STATUS_PENDING );
+
+ if (async->status != STATUS_PENDING)
+ {
+ /* already terminated, just update status */
+ async->status = status;
+ return;
+ }
+
memset( &data, 0, sizeof(data) );
data.type = APC_ASYNC_IO;
data.async_io.func = async->data.callback;
@@ -131,11 +147,9 @@
data.async_io.sb = async->data.iosb;
data.async_io.status = status;
thread_queue_apc( async->thread, &async->obj, &data );
-
- if (async->timeout) remove_timeout_user( async->timeout );
- async->timeout = NULL;
- list_remove( &async->queue_entry );
- release_object( async );
+ async->status = status;
+ async_reselect( async );
+ release_object( async ); /* so that it gets destroyed when the async is done */
}
/* callback for timeout on an async request */
@@ -184,11 +198,12 @@
return NULL;
}
- async->thread = (struct thread *)grab_object( thread );
- async->event = event;
- async->data = *data;
+ async->thread = (struct thread *)grab_object( thread );
+ async->event = event;
+ async->status = STATUS_PENDING;
+ async->data = *data;
async->timeout = NULL;
- async->queue = (struct async_queue *)grab_object( queue );
+ async->queue = (struct async_queue *)grab_object( queue );
list_add_tail( &queue->queue, &async->queue_entry );
grab_object( async );
@@ -214,12 +229,24 @@
if (obj->ops != &async_ops) return; /* in case the client messed up the APC results */
- if (status == STATUS_PENDING)
+ assert( async->status != STATUS_PENDING ); /* it must have been woken up if we get a result */
+
+ if (status == STATUS_PENDING) /* restart it */
{
- /* FIXME: restart the async operation */
+ status = async->status;
+ async->status = STATUS_PENDING;
+ grab_object( async );
+
+ if (status != STATUS_ALERTED) /* it was terminated in the meantime */
+ async_terminate( async, status );
+ else
+ async_reselect( async );
}
else
{
+ if (async->timeout) remove_timeout_user( async->timeout );
+ async->timeout = NULL;
+ async->status = status;
if (async->data.apc)
{
apc_call_t data;
@@ -238,7 +265,13 @@
/* check if an async operation is waiting to be alerted */
int async_waiting( struct async_queue *queue )
{
- return queue && !list_empty( &queue->queue );
+ struct list *ptr;
+ struct async *async;
+
+ if (!queue) return 0;
+ if (!(ptr = list_head( &queue->queue ))) return 0;
+ async = LIST_ENTRY( ptr, struct async, queue_entry );
+ return async->status == STATUS_PENDING;
}
/* wake up async operations on the queue */
diff --git a/server/change.c b/server/change.c
index 33034da..a12c68b 100644
--- a/server/change.c
+++ b/server/change.c
@@ -183,12 +183,13 @@
static const struct fd_ops dir_fd_ops =
{
- dir_get_poll_events, /* get_poll_events */
- default_poll_event, /* poll_event */
- no_flush, /* flush */
- dir_get_info, /* get_file_info */
- default_fd_queue_async, /* queue_async */
- default_fd_cancel_async /* cancel_async */
+ dir_get_poll_events, /* get_poll_events */
+ default_poll_event, /* poll_event */
+ no_flush, /* flush */
+ dir_get_info, /* get_file_info */
+ default_fd_queue_async, /* queue_async */
+ default_fd_reselect_async, /* reselect_async */
+ default_fd_cancel_async /* cancel_async */
};
static struct list change_list = LIST_INIT(change_list);
@@ -516,12 +517,13 @@
static const struct fd_ops inotify_fd_ops =
{
- inotify_get_poll_events, /* get_poll_events */
- inotify_poll_event, /* poll_event */
- no_flush, /* flush */
- no_get_file_info, /* get_file_info */
- default_fd_queue_async, /* queue_async */
- default_fd_cancel_async, /* cancel_async */
+ inotify_get_poll_events, /* get_poll_events */
+ inotify_poll_event, /* poll_event */
+ no_flush, /* flush */
+ no_get_file_info, /* get_file_info */
+ default_fd_queue_async, /* queue_async */
+ default_fd_reselect_async, /* reselect_async */
+ default_fd_cancel_async, /* cancel_async */
};
static int inotify_get_poll_events( struct fd *fd )
diff --git a/server/fd.c b/server/fd.c
index 3a044b2..504512e 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -1638,6 +1638,7 @@
struct pollfd pfd;
if (fd->unix_fd == -1) return POLLERR;
+ if (fd->inode) return events; /* regular files are always signaled */
pfd.fd = fd->unix_fd;
pfd.events = events;
@@ -1666,12 +1667,12 @@
/* default handler for poll() events */
void default_poll_event( struct fd *fd, int event )
{
- if (event & POLLIN) async_wake_up( fd->read_q, STATUS_ALERTED );
- if (event & POLLOUT) async_wake_up( fd->write_q, STATUS_ALERTED );
+ if (event & (POLLIN | POLLERR | POLLHUP)) async_wake_up( fd->read_q, STATUS_ALERTED );
+ if (event & (POLLOUT | POLLERR | POLLHUP)) 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 );
- else set_fd_events( fd, fd->fd_ops->get_poll_events( fd ) );
+ else if (!fd->inode) set_fd_events( fd, fd->fd_ops->get_poll_events( fd ) );
}
struct async *fd_queue_async( struct fd *fd, const async_data_t *data, int type, int count )
@@ -1725,6 +1726,11 @@
}
}
+void fd_reselect_async( struct fd *fd, struct async_queue *queue )
+{
+ fd->fd_ops->reselect_async( fd, queue );
+}
+
void default_fd_queue_async( struct fd *fd, const async_data_t *data, int type, int count )
{
int flags;
@@ -1743,6 +1749,19 @@
}
}
+/* default reselect_async() fd routine */
+void default_fd_reselect_async( struct fd *fd, struct async_queue *queue )
+{
+ if (queue != fd->wait_q)
+ {
+ int poll_events = fd->fd_ops->get_poll_events( fd );
+ int events = check_fd_events( fd, poll_events );
+ if (events) fd->fd_ops->poll_event( fd, events );
+ else set_fd_events( fd, poll_events );
+ }
+}
+
+/* default cancel_async() fd routine */
void default_fd_cancel_async( struct fd *fd )
{
async_wake_up( fd->read_q, STATUS_CANCELLED );
diff --git a/server/file.c b/server/file.c
index edc92f7..e038af3 100644
--- a/server/file.c
+++ b/server/file.c
@@ -96,6 +96,7 @@
file_flush, /* flush */
file_get_info, /* get_file_info */
default_fd_queue_async, /* queue_async */
+ default_fd_reselect_async, /* reselect_async */
default_fd_cancel_async /* cancel_async */
};
diff --git a/server/file.h b/server/file.h
index 096b9df..54d2d73 100644
--- a/server/file.h
+++ b/server/file.h
@@ -41,6 +41,8 @@
enum server_fd_type (*get_file_info)(struct fd *fd, int *flags);
/* queue an async operation */
void (*queue_async)(struct fd *, const async_data_t *data, int type, int count);
+ /* selected events for async i/o need an update */
+ void (*reselect_async)( struct fd *, struct async_queue *queue );
/* cancel an async operation */
void (*cancel_async)(struct fd *);
};
@@ -70,7 +72,9 @@
extern void default_poll_event( struct fd *fd, int event );
extern struct async *fd_queue_async( struct fd *fd, const async_data_t *data, int type, int count );
extern void fd_async_wake_up( struct fd *fd, int type, unsigned int status );
+extern void fd_reselect_async( struct fd *fd, struct async_queue *queue );
extern void default_fd_queue_async( struct fd *fd, const async_data_t *data, int type, int count );
+extern void default_fd_reselect_async( struct fd *fd, struct async_queue *queue );
extern void default_fd_cancel_async( struct fd *fd );
extern void no_flush( struct fd *fd, struct event **event );
extern enum server_fd_type no_get_file_info( struct fd *fd, int *flags );
diff --git a/server/mailslot.c b/server/mailslot.c
index 2898d1a..d130f6e 100644
--- a/server/mailslot.c
+++ b/server/mailslot.c
@@ -97,6 +97,7 @@
no_flush, /* flush */
mailslot_get_info, /* get_file_info */
mailslot_queue_async, /* queue_async */
+ default_fd_reselect_async, /* reselect_async */
default_fd_cancel_async /* cancel_async */
};
@@ -185,6 +186,7 @@
no_flush, /* flush */
mailslot_device_get_file_info, /* get_file_info */
default_fd_queue_async, /* queue_async */
+ default_fd_reselect_async, /* reselect_async */
default_fd_cancel_async /* cancel_async */
};
diff --git a/server/named_pipe.c b/server/named_pipe.c
index a736d83..4b23816 100644
--- a/server/named_pipe.c
+++ b/server/named_pipe.c
@@ -158,11 +158,12 @@
static const struct fd_ops pipe_server_fd_ops =
{
- default_fd_get_poll_events, /* get_poll_events */
+ default_fd_get_poll_events, /* get_poll_events */
default_poll_event, /* poll_event */
pipe_server_flush, /* flush */
pipe_server_get_info, /* get_file_info */
default_fd_queue_async, /* queue_async */
+ default_fd_reselect_async, /* reselect_async */
default_fd_cancel_async, /* cancel_async */
};
@@ -197,6 +198,7 @@
pipe_client_flush, /* flush */
pipe_client_get_info, /* get_file_info */
default_fd_queue_async, /* queue_async */
+ default_fd_reselect_async, /* reselect_async */
default_fd_cancel_async /* cancel_async */
};
@@ -233,6 +235,7 @@
no_flush, /* flush */
named_pipe_device_get_file_info, /* get_file_info */
default_fd_queue_async, /* queue_async */
+ default_fd_reselect_async, /* reselect_async */
default_fd_cancel_async /* cancel_async */
};
diff --git a/server/process.c b/server/process.c
index f947d6d..f06f5f4 100644
--- a/server/process.c
+++ b/server/process.c
@@ -87,6 +87,7 @@
no_flush, /* flush */
no_get_file_info, /* get_file_info */
no_queue_async, /* queue_async */
+ NULL, /* reselect_async */
no_cancel_async /* cancel async */
};
diff --git a/server/protocol.def b/server/protocol.def
index 48d1096..e1e504f 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -279,7 +279,7 @@
struct
{
enum apc_type type; /* APC_ASYNC_IO */
- void (__stdcall *func)(void*, void*, unsigned int);
+ unsigned int (*func)(void*, void*, unsigned int);
void *user; /* user pointer */
void *sb; /* status block */
unsigned int status; /* I/O status */
diff --git a/server/queue.c b/server/queue.c
index 4e66f99..22f4dcd 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -169,6 +169,7 @@
no_flush, /* flush */
no_get_file_info, /* get_file_info */
no_queue_async, /* queue_async */
+ NULL, /* reselect_async */
no_cancel_async /* cancel async */
};
diff --git a/server/request.c b/server/request.c
index 20fe435..7dac5d1 100644
--- a/server/request.c
+++ b/server/request.c
@@ -109,6 +109,7 @@
no_flush, /* flush */
no_get_file_info, /* get_file_info */
no_queue_async, /* queue_async */
+ NULL, /* reselect_async */
no_cancel_async /* cancel_async */
};
diff --git a/server/serial.c b/server/serial.c
index 8dc1d18..b9a3bbd 100644
--- a/server/serial.c
+++ b/server/serial.c
@@ -109,6 +109,7 @@
serial_flush, /* flush */
serial_get_info, /* get_file_info */
serial_queue_async, /* queue_async */
+ default_fd_reselect_async, /* reselect_async */
default_fd_cancel_async /* cancel_async */
};
diff --git a/server/signal.c b/server/signal.c
index 7b388e1..238edef 100644
--- a/server/signal.c
+++ b/server/signal.c
@@ -85,6 +85,7 @@
no_flush, /* flush */
no_get_file_info, /* get_file_info */
no_queue_async, /* queue_async */
+ NULL, /* reselect_async */
no_cancel_async /* cancel_async */
};
diff --git a/server/sock.c b/server/sock.c
index 90103f9..51109c3 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -97,6 +97,7 @@
static void sock_poll_event( struct fd *fd, int event );
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 );
+static void sock_reselect_async( struct fd *fd, struct async_queue *queue );
static void sock_cancel_async( struct fd *fd );
static int sock_get_error( int err );
@@ -126,6 +127,7 @@
no_flush, /* flush */
sock_get_info, /* get_file_info */
sock_queue_async, /* queue_async */
+ sock_reselect_async, /* reselect_async */
sock_cancel_async /* cancel_async */
};
@@ -556,6 +558,13 @@
if ( pollev ) sock_try_event( sock, pollev );
}
+static void sock_reselect_async( struct fd *fd, struct async_queue *queue )
+{
+ struct sock *sock = get_fd_user( fd );
+ int events = sock_reselect( sock );
+ if (events) sock_try_event( sock, events );
+}
+
static void sock_cancel_async( struct fd *fd )
{
struct sock *sock = get_fd_user( fd );
diff --git a/server/thread.c b/server/thread.c
index ec1b161..09e0367 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -132,6 +132,7 @@
no_flush, /* flush */
no_get_file_info, /* get_file_info */
no_queue_async, /* queue_async */
+ NULL, /* reselect_async */
no_cancel_async /* cancel_async */
};
diff --git a/server/trace.c b/server/trace.c
index 0d11e7c..f1e5aca 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -4161,6 +4161,7 @@
{ "MEDIA_WRITE_PROTECTED", STATUS_MEDIA_WRITE_PROTECTED },
{ "MUTANT_NOT_OWNED", STATUS_MUTANT_NOT_OWNED },
{ "NAME_TOO_LONG", STATUS_NAME_TOO_LONG },
+ { "NOTIFY_ENUM_DIR", STATUS_NOTIFY_ENUM_DIR },
{ "NOT_ALL_ASSIGNED", STATUS_NOT_ALL_ASSIGNED },
{ "NOT_A_DIRECTORY", STATUS_NOT_A_DIRECTORY },
{ "NOT_IMPLEMENTED", STATUS_NOT_IMPLEMENTED },