From: Matteo Bruni mbruni@codeweavers.com
--- server/async.c | 162 +++++++++++++++++++++++++++++++++++++++++------ server/process.c | 2 + server/process.h | 3 +- 3 files changed, 147 insertions(+), 20 deletions(-)
diff --git a/server/async.c b/server/async.c index 20f906c5d78..43b62343037 100644 --- a/server/async.c +++ b/server/async.c @@ -34,12 +34,88 @@ #include "process.h" #include "handle.h"
+struct async_cancel +{ + struct object obj; /* object header */ + struct event_sync *sync; /* sync object for wait/signal */ + struct list asyncs; /* list of asyncs in the cancel group */ + struct list entry; /* entry in process client_cancels list */ +}; + +static void async_cancel_dump( struct object *obj, int verbose ); +static struct object *async_cancel_get_sync( struct object *obj ); +static void async_cancel_destroy( struct object *obj ); + +static const struct object_ops async_cancel_ops = +{ + sizeof(struct async_cancel), /* size */ + &no_type, /* type */ + async_cancel_dump, /* dump */ + NULL, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ + async_cancel_get_sync, /* get_sync */ + default_map_access, /* map_access */ + default_get_sd, /* get_sd */ + default_set_sd, /* set_sd */ + no_get_full_name, /* get_full_name */ + no_lookup_name, /* lookup_name */ + no_link_name, /* link_name */ + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ + no_close_handle, /* close_handle */ + async_cancel_destroy /* destroy */ +}; + +static void async_cancel_dump( struct object *obj, int verbose ) +{ + struct async_cancel *cancel = (struct async_cancel *)obj; + assert( obj->ops == &async_cancel_ops ); + fprintf( stderr, "async_cancel %p\n", cancel ); +} + +static struct object *async_cancel_get_sync( struct object *obj ) +{ + struct async_cancel *cancel = (struct async_cancel *)obj; + + assert( obj->ops == &async_cancel_ops ); + return grab_object( cancel->sync ); +} + +static void async_cancel_destroy( struct object *obj ) +{ + struct async_cancel *cancel = (struct async_cancel *)obj; + + assert( obj->ops == &async_cancel_ops ); + if (cancel->sync) release_object( cancel->sync ); +} + +static struct async_cancel *create_async_cancel(void) +{ + struct async_cancel *cancel; + + if (!(cancel = alloc_object( &async_cancel_ops ))) return NULL; + cancel->sync = NULL; + list_init( &cancel->asyncs ); + + if (!(cancel->sync = create_event_sync( 1, 0 ))) + { + release_object( cancel ); + return NULL; + } + return cancel; +} + struct async { struct object obj; /* object header */ struct thread *thread; /* owning thread */ struct list queue_entry; /* entry in async queue list */ - struct list process_entry; /* entry in process list */ + struct list process_entry; /* entry in process / cancel list */ struct async_queue *queue; /* queue containing this async */ struct fd *fd; /* fd associated with an unqueued async */ struct timeout_user *timeout; @@ -62,6 +138,7 @@ struct async unsigned int comp_flags; /* completion flags */ async_completion_callback completion_callback; /* callback to be called on completion */ void *completion_callback_private; /* argument to completion_callback */ + struct async_cancel *async_cancel; /* cancel object if async is being cancelled */ };
static void async_dump( struct object *obj, int verbose ); @@ -145,6 +222,7 @@ static void async_destroy( struct object *obj ) struct async *async = (struct async *)obj; assert( obj->ops == &async_ops );
+ assert( !async->async_cancel ); list_remove( &async->process_entry );
if (async->queue) @@ -282,6 +360,7 @@ struct async *create_async( struct fd *fd, struct thread *thread, const struct a async->comp_flags = 0; async->completion_callback = NULL; async->completion_callback_private = NULL; + async->async_cancel = NULL;
if (iosb) async->iosb = (struct iosb *)grab_object( iosb ); else async->iosb = NULL; @@ -483,6 +562,23 @@ static void add_async_completion( struct async *async, apc_param_t cvalue, unsig if (async->completion) add_completion( async->completion, async->comp_key, cvalue, status, information ); }
+static void async_complete_cancel( struct async *async ) +{ + struct async_cancel *cancel; + + if (!(cancel = async->async_cancel)) return; + async->async_cancel = NULL; + list_remove( &async->process_entry ); + list_init( &async->process_entry ); + + if (list_empty( &cancel->asyncs )) + { + signal_sync( cancel->sync ); + list_remove( &cancel->entry ); + release_object( cancel ); + } +} + /* store the result of the client-side async callback */ void async_set_result( struct object *obj, unsigned int status, apc_param_t total ) { @@ -540,6 +636,7 @@ void async_set_result( struct object *obj, unsigned int status, apc_param_t tota }
async_call_completion_callback( async ); + async_complete_cancel( async );
if (async->queue) { @@ -573,17 +670,21 @@ int async_waiting( struct async_queue *queue ) return !async->terminated; }
-static void cancel_async( struct process *process, struct async *async ) +static void cancel_async( struct list *cancelled_asyncs, struct async *async ) { list_remove( &async->process_entry ); - list_add_tail( &process->cancelled_asyncs, &async->process_entry ); + list_add_tail( cancelled_asyncs, &async->process_entry ); fd_cancel_async( async->fd, async ); }
static struct async *find_async_from_user( struct process *process, client_ptr_t user ) { + struct async_cancel *cancel; struct async *async;
+ LIST_FOR_EACH_ENTRY( cancel, &process->client_cancels, struct async_cancel, entry ) + LIST_FOR_EACH_ENTRY( async, &cancel->asyncs, struct async, process_entry ) + if (async->data.user == user) return async; LIST_FOR_EACH_ENTRY( async, &process->cancelled_asyncs, struct async, process_entry ) if (async->data.user == user) return async; LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) @@ -592,15 +693,16 @@ static struct async *find_async_from_user( struct process *process, client_ptr_t return NULL; }
-static int cancel_process_async( struct process *process, struct object *obj, struct thread *thread, client_ptr_t iosb ) +static obj_handle_t cancel_process_async( struct process *process, struct object *obj, struct thread *thread, client_ptr_t iosb ) { + struct async_cancel *cancel; + obj_handle_t handle = 0; struct async *async; - int woken = 0;
- /* FIXME: it would probably be nice to replace the "canceled" flag with a - * single LIST_FOR_EACH_ENTRY_SAFE, but currently cancelling an async can - * cause other asyncs to be removed via async_reselect() */ + if (!(cancel = create_async_cancel())) return 0;
+ /* Cancelling an async can cause other asyncs to be removed via + * async_reselect() */ restart: LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) { @@ -609,12 +711,19 @@ restart: (!thread || async->thread == thread) && (!iosb || async->data.iosb == iosb)) { - cancel_async( process, async ); - woken++; + cancel_async( &cancel->asyncs, async ); + async->async_cancel = cancel; goto restart; } } - return woken; + + if (list_empty( &cancel->asyncs )) release_object( cancel ); + else + { + handle = alloc_handle( process, cancel, SYNCHRONIZE, 0 ); + list_add_tail( &process->client_cancels, &cancel->entry ); + } + return handle; }
static int cancel_blocking( struct process *process, struct thread *thread, client_ptr_t iosb ) @@ -629,7 +738,7 @@ restart: if (async->blocking && async->thread == thread && (!iosb || async->data.iosb == iosb)) { - cancel_async( process, async ); + cancel_async( &process->cancelled_asyncs, async ); woken++; goto restart; } @@ -639,15 +748,29 @@ restart:
void cancel_terminating_process_asyncs( struct process *process ) { - struct async *async; + struct async_cancel *cancel, *next_cancel; + struct async *async, *next_async;
restart: LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) { if (async->terminated) continue; - cancel_async( process, async ); + cancel_async( &process->cancelled_asyncs, async ); goto restart; } + + LIST_FOR_EACH_ENTRY_SAFE( cancel, next_cancel, &process->client_cancels, struct async_cancel, entry ) + { + LIST_FOR_EACH_ENTRY_SAFE( async, next_async, &cancel->asyncs, struct async, process_entry ) + { + async->async_cancel = NULL; + list_remove( &async->process_entry ); + list_init( &async->process_entry ); + } + + list_remove( &cancel->entry ); + release_object( cancel ); + } }
int async_close_obj_handle( struct object *obj, struct process *process, obj_handle_t handle ) @@ -664,7 +787,7 @@ restart: { if (async->terminated || get_fd_user( async->fd ) != obj) continue; if (!async->completion || !async->data.apc_context || async->event) continue; - cancel_async( process, async ); + cancel_async( &process->cancelled_asyncs, async ); goto restart; } return 1; @@ -672,15 +795,16 @@ restart:
void cancel_terminating_thread_asyncs( struct thread *thread ) { + struct process *process = thread->process; struct async *async;
restart: - LIST_FOR_EACH_ENTRY( async, &thread->process->asyncs, struct async, process_entry ) + LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) { if (async->thread != thread || async->terminated) continue; if (async->completion && async->data.apc_context && !async->event) continue; if (async->is_system) continue; - cancel_async( thread->process, async ); + cancel_async( &process->cancelled_asyncs, async ); goto restart; } } @@ -830,8 +954,8 @@ DECL_HANDLER(cancel_async)
if (obj) { - int count = cancel_process_async( current->process, obj, thread, req->iosb ); - if (!count && !thread) set_error( STATUS_NOT_FOUND ); + obj_handle_t wait_handle = cancel_process_async( current->process, obj, thread, req->iosb ); + if (!wait_handle && !thread) set_error( STATUS_NOT_FOUND ); release_object( obj ); } } diff --git a/server/process.c b/server/process.c index 268744ef2cf..45e74cac9c0 100644 --- a/server/process.c +++ b/server/process.c @@ -706,6 +706,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla list_init( &process->locks ); list_init( &process->asyncs ); list_init( &process->cancelled_asyncs ); + list_init( &process->client_cancels ); list_init( &process->classes ); list_init( &process->views );
@@ -782,6 +783,7 @@ static void process_destroy( struct object *obj ) assert( list_empty( &process->thread_list )); assert( list_empty( &process->asyncs )); assert( list_empty( &process->cancelled_asyncs )); + assert( list_empty( &process->client_cancels ));
assert( !process->sigkill_timeout ); /* timeout should hold a reference to the process */
diff --git a/server/process.h b/server/process.h index 0e01fa9eafb..7d5d9538677 100644 --- a/server/process.h +++ b/server/process.h @@ -67,7 +67,8 @@ struct process struct job *job; /* job object associated with this process */ struct list job_entry; /* list entry for job object */ struct list asyncs; /* list of async object owned by the process */ - struct list cancelled_asyncs;/* list of async object owned by the process */ + struct list cancelled_asyncs;/* list of server-cancelled async objects */ + struct list client_cancels; /* list of client async_cancel objects */ struct list locks; /* list of file locks owned by the process */ struct list classes; /* window classes owned by the process */ struct console *console; /* console input */