Merge branch 'async-proposal-00' into 'master'

Draft: Refactor the async state flags

See merge request wine/wine!6369
This commit is contained in:
Jinoh Kang 2024-11-18 20:36:54 +00:00
commit 90bedd8305

View file

@ -34,6 +34,48 @@
#include "process.h" #include "process.h"
#include "handle.h" #include "handle.h"
enum async_state
{
/* The I/O operation has just initiated, or initial status is known. */
ASYNC_INITIAL,
/* The I/O operation has just initiated via create_request_async(). */
ASYNC_INITIAL_DIRECT_RESULT,
/* The initial status is not known yet. It will be determined by the client or winedevice.exe. */
ASYNC_UNKNOWN_STATUS,
/* The I/O operation is being processed by the wineserver or winedevice.eze. */
ASYNC_IN_PROGRESS,
/* The I/O operation is being processed by the client. */
ASYNC_ALERTED,
/* The I/O operation has finished synchronously (APC result) but not been
* acknowledged by the client.
*
* The completion is being delivered to the client indirectly via
* APC_ASYNC_IO. The client had no chance to fill the IOSB synchronously,
* due to unknwon initial status (e.g., processed by winedevice.exe).
*/
ASYNC_FINALIZING_SYNC_APC_RESULT,
/* The I/O operation has finished synchronously (direct result) but not
* been acknowledged by the client.
*
* The completion is being delivered to the client directly via server
* request return. The client may proceed to fill the IOSB synchronously,
* and notify the server that it has done so.
*/
ASYNC_FINALIZING_SYNC_DIRECT_RESULT,
/* The I/O operation has finished asynchronously but not been acknowledged by the client. */
ASYNC_FINALIZING_ASYNC,
/* The I/O operation has finished and the result has been acknowledged by the client. */
ASYNC_COMPLETED
};
struct async struct async
{ {
struct object obj; /* object header */ struct object obj; /* object header */
@ -49,13 +91,9 @@ struct async
struct iosb *iosb; /* I/O status block */ struct iosb *iosb; /* I/O status block */
obj_handle_t wait_handle; /* pre-allocated wait handle */ obj_handle_t wait_handle; /* pre-allocated wait handle */
unsigned int initial_status; /* status returned from initial request */ unsigned int initial_status; /* status returned from initial request */
unsigned int signaled :1; enum async_state state;
unsigned int pending :1; /* request successfully queued, but pending */ unsigned int pending :1; /* request successfully queued, but pending */
unsigned int direct_result :1;/* a flag if we're passing result directly from request instead of APC */
unsigned int alerted :1; /* fd is signaled, but we are waiting for client-side I/O */
unsigned int terminated :1; /* async has been terminated */
unsigned int canceled :1; /* have we already queued cancellation for this async? */ unsigned int canceled :1; /* have we already queued cancellation for this async? */
unsigned int unknown_status :1; /* initial status is not known yet */
unsigned int blocking :1; /* async is blocking */ unsigned int blocking :1; /* async is blocking */
unsigned int is_system :1; /* background system operation not affecting userspace visible state. */ unsigned int is_system :1; /* background system operation not affecting userspace visible state. */
struct completion *completion; /* completion associated with fd */ struct completion *completion; /* completion associated with fd */
@ -110,7 +148,28 @@ static int async_signaled( struct object *obj, struct wait_queue_entry *entry )
{ {
struct async *async = (struct async *)obj; struct async *async = (struct async *)obj;
assert( obj->ops == &async_ops ); assert( obj->ops == &async_ops );
return async->signaled; switch (async->state)
{
case ASYNC_INITIAL:
case ASYNC_INITIAL_DIRECT_RESULT:
case ASYNC_UNKNOWN_STATUS:
case ASYNC_FINALIZING_SYNC_APC_RESULT:
return 0;
case ASYNC_FINALIZING_SYNC_DIRECT_RESULT:
/* The client will "wait" on the async to signal completion. */
return 1;
case ASYNC_IN_PROGRESS:
case ASYNC_ALERTED:
case ASYNC_FINALIZING_ASYNC:
/* If nonblocking, the client will "wait" on the async to close it. */
return !async->blocking;
case ASYNC_COMPLETED:
/* If there is an open async handle, notify the waiter of completion. */
return 1;
default:
assert( 0 );
return 0;
}
} }
static void async_satisfied( struct object *obj, struct wait_queue_entry *entry ) static void async_satisfied( struct object *obj, struct wait_queue_entry *entry )
@ -121,10 +180,9 @@ static void async_satisfied( struct object *obj, struct wait_queue_entry *entry
/* we only return an async handle for asyncs created via create_request_async() */ /* we only return an async handle for asyncs created via create_request_async() */
assert( async->iosb ); assert( async->iosb );
if (async->direct_result) if (async->state == ASYNC_FINALIZING_SYNC_DIRECT_RESULT)
{ {
async_set_result( &async->obj, async->iosb->status, async->iosb->result ); async_set_result( &async->obj, async->iosb->status, async->iosb->result );
async->direct_result = 0;
} }
if (async->initial_status == STATUS_PENDING && async->blocking) if (async->initial_status == STATUS_PENDING && async->blocking)
@ -161,24 +219,70 @@ static void async_destroy( struct object *obj )
release_object( async->thread ); release_object( async->thread );
} }
static int async_terminated( const struct async *async )
{
switch (async->state)
{
case ASYNC_INITIAL:
case ASYNC_INITIAL_DIRECT_RESULT:
case ASYNC_IN_PROGRESS:
return 0;
case ASYNC_UNKNOWN_STATUS:
case ASYNC_ALERTED:
case ASYNC_FINALIZING_SYNC_APC_RESULT:
case ASYNC_FINALIZING_SYNC_DIRECT_RESULT:
case ASYNC_FINALIZING_ASYNC:
case ASYNC_COMPLETED:
return 1;
default:
assert( 0 );
return 0;
}
}
/* notifies client thread of new status of its async request */ /* notifies client thread of new status of its async request */
void async_terminate( struct async *async, unsigned int status ) void async_terminate( struct async *async, unsigned int status )
{ {
struct iosb *iosb = async->iosb; struct iosb *iosb = async->iosb;
if (async->terminated) return; if (async_terminated( async )) return;
assert( async->state == ASYNC_INITIAL || async->state == ASYNC_INITIAL_DIRECT_RESULT || async->state == ASYNC_IN_PROGRESS );
async->terminated = 1;
if (async->iosb && async->iosb->status == STATUS_PENDING) async->iosb->status = status;
if (status == STATUS_ALERTED) if (status == STATUS_ALERTED)
async->alerted = 1; {
if (async->state == ASYNC_INITIAL_DIRECT_RESULT)
{
async->state = ASYNC_UNKNOWN_STATUS;
}
else
{
async->state = ASYNC_ALERTED;
}
}
else
{
if (async->state == ASYNC_INITIAL)
async->state = ASYNC_FINALIZING_SYNC_APC_RESULT;
else if (async->state == ASYNC_INITIAL_DIRECT_RESULT)
async->state = ASYNC_FINALIZING_SYNC_DIRECT_RESULT;
else
async->state = ASYNC_FINALIZING_ASYNC;
}
if (async->iosb && async->iosb->status == STATUS_PENDING) async->iosb->status = status;
if (async->state == ASYNC_FINALIZING_SYNC_DIRECT_RESULT ||
(async->state == ASYNC_ALERTED || async->state == ASYNC_FINALIZING_ASYNC) && !async->blocking)
{
wake_up( &async->obj, 0 );
}
/* if no APC could be queued (e.g. the process is terminated), /* if no APC could be queued (e.g. the process is terminated),
* thread_queue_apc() may trigger async_set_result(), which may drop the * thread_queue_apc() may trigger async_set_result(), which may drop the
* last reference to the async, so grab a temporary reference here */ * last reference to the async, so grab a temporary reference here */
grab_object( async ); grab_object( async );
if (!async->direct_result) if (async->state != ASYNC_FINALIZING_SYNC_DIRECT_RESULT && async->state != ASYNC_UNKNOWN_STATUS)
{ {
union apc_call data; union apc_call data;
@ -269,14 +373,10 @@ struct async *create_async( struct fd *fd, struct thread *thread, const struct a
async->queue = NULL; async->queue = NULL;
async->fd = (struct fd *)grab_object( fd ); async->fd = (struct fd *)grab_object( fd );
async->initial_status = STATUS_PENDING; async->initial_status = STATUS_PENDING;
async->signaled = 0; async->state = ASYNC_INITIAL;
async->pending = 1; async->pending = 1;
async->wait_handle = 0; async->wait_handle = 0;
async->direct_result = 0;
async->alerted = 0;
async->terminated = 0;
async->canceled = 0; async->canceled = 0;
async->unknown_status = 0;
async->blocking = !is_fd_overlapped( fd ); async->blocking = !is_fd_overlapped( fd );
async->is_system = 0; async->is_system = 0;
async->completion = fd_get_completion( fd, &async->comp_key ); async->completion = fd_get_completion( fd, &async->comp_key );
@ -305,21 +405,22 @@ struct async *create_async( struct fd *fd, struct thread *thread, const struct a
void async_set_initial_status( struct async *async, unsigned int status ) void async_set_initial_status( struct async *async, unsigned int status )
{ {
async->initial_status = status; async->initial_status = status;
async->unknown_status = 0; if (async->state == ASYNC_UNKNOWN_STATUS)
async->state = ASYNC_INITIAL;
} }
void set_async_pending( struct async *async ) void set_async_pending( struct async *async )
{ {
if (!async->terminated) if (async->state == ASYNC_INITIAL_DIRECT_RESULT || async->state == ASYNC_UNKNOWN_STATUS)
async->pending = 1; async->pending = 1;
} }
void async_wake_obj( struct async *async ) void async_wake_obj( struct async *async )
{ {
assert( !async->unknown_status ); assert( async->state == ASYNC_INITIAL );
async->state = ASYNC_IN_PROGRESS;
if (!async->blocking) if (!async->blocking)
{ {
async->signaled = 1;
wake_up( &async->obj, 0 ); wake_up( &async->obj, 0 );
} }
} }
@ -336,7 +437,7 @@ obj_handle_t async_handoff( struct async *async, data_size_t *result, int force_
{ {
async->blocking = force_blocking || async->blocking; async->blocking = force_blocking || async->blocking;
if (async->unknown_status) if (async->state == ASYNC_UNKNOWN_STATUS)
{ {
/* even the initial status is not known yet */ /* even the initial status is not known yet */
set_error( STATUS_PENDING ); set_error( STATUS_PENDING );
@ -353,17 +454,16 @@ obj_handle_t async_handoff( struct async *async, data_size_t *result, int force_
* instead. * instead.
* *
* since we're deferring the initial I/O (to the client), we mark the * since we're deferring the initial I/O (to the client), we mark the
* async as having unknown initial status (unknown_status = 1). note * async as having unknown initial status. note that we don't reuse
* that we don't reuse async_set_unknown_status() here. this is because * async_set_unknown_status() here. this is because the one
* the one responsible for performing the I/O is not the device driver, * responsible for performing the I/O is not the device driver, but
* but instead the client that requested the I/O in the first place. * instead the client that requested the I/O in the first place.
* *
* also, async_set_unknown_status() would set direct_result to zero * also, async_set_unknown_status() would eventually force APC_ASYNC_IO
* forcing APC_ASYNC_IO to fire in async_terminate(), which is not * to fire in async_terminate(), which is not useful due to subtle
* useful due to subtle semantic differences between synchronous and * semantic differences between synchronous and asynchronous
* asynchronous completion. * completion.
*/ */
async->unknown_status = 1;
async_terminate( async, STATUS_ALERTED ); async_terminate( async, STATUS_ALERTED );
return async->wait_handle; return async->wait_handle;
} }
@ -398,11 +498,11 @@ obj_handle_t async_handoff( struct async *async, data_size_t *result, int force_
if (async->iosb->status != STATUS_PENDING) if (async->iosb->status != STATUS_PENDING)
{ {
if (result) *result = async->iosb->result; if (result) *result = async->iosb->result;
async->signaled = 1;
} }
else else
{ {
async->direct_result = 0; assert( async->state == ASYNC_INITIAL_DIRECT_RESULT );
async->state = ASYNC_IN_PROGRESS;
async->pending = 1; async->pending = 1;
if (!async->blocking) if (!async->blocking)
{ {
@ -457,8 +557,8 @@ void async_request_complete_alloc( struct async *async, unsigned int status, dat
/* mark an async as having unknown initial status */ /* mark an async as having unknown initial status */
void async_set_unknown_status( struct async *async ) void async_set_unknown_status( struct async *async )
{ {
async->unknown_status = 1; assert( async->state == ASYNC_INITIAL_DIRECT_RESULT );
async->direct_result = 0; async->state = ASYNC_UNKNOWN_STATUS;
} }
/* set the timeout of an async operation */ /* set the timeout of an async operation */
@ -491,21 +591,21 @@ void async_set_result( struct object *obj, unsigned int status, apc_param_t tota
if (obj->ops != &async_ops) return; /* in case the client messed up the APC results */ if (obj->ops != &async_ops) return; /* in case the client messed up the APC results */
assert( async->terminated ); /* it must have been woken up if we get a result */ assert( async_terminated( async ) ); /* it must have been woken up if we get a result */
if (async->unknown_status) async_set_initial_status( async, status ); if (async->state == ASYNC_UNKNOWN_STATUS) async_set_initial_status( async, status );
assert( async->state != ASYNC_UNKNOWN_STATUS );
if (async->alerted && status == STATUS_PENDING) /* restart it */ if ((async->state == ASYNC_INITIAL || async->state == ASYNC_ALERTED) &&
status == STATUS_PENDING) /* restart it */
{ {
async->terminated = 0; async->state = ASYNC_IN_PROGRESS;
async->alerted = 0;
async_reselect( async ); async_reselect( async );
} }
else else
{ {
if (async->timeout) remove_timeout_user( async->timeout ); if (async->timeout) remove_timeout_user( async->timeout );
async->timeout = NULL; async->timeout = NULL;
async->terminated = 1;
if (async->iosb) async->iosb->status = status; if (async->iosb) async->iosb->status = status;
/* don't signal completion if the async failed synchronously /* don't signal completion if the async failed synchronously
@ -534,11 +634,8 @@ void async_set_result( struct object *obj, unsigned int status, apc_param_t tota
else if (async->fd && !async->is_system) set_fd_signaled( async->fd, 1 ); else if (async->fd && !async->is_system) set_fd_signaled( async->fd, 1 );
} }
if (!async->signaled) async->state = ASYNC_COMPLETED;
{ wake_up( &async->obj, 0 );
async->signaled = 1;
wake_up( &async->obj, 0 );
}
async_call_completion_callback( async ); async_call_completion_callback( async );
@ -558,7 +655,7 @@ int async_queue_has_waiting_asyncs( struct async_queue *queue )
struct async *async; struct async *async;
LIST_FOR_EACH_ENTRY( async, &queue->queue, struct async, queue_entry ) LIST_FOR_EACH_ENTRY( async, &queue->queue, struct async, queue_entry )
if (!async->unknown_status) return 1; if (async->state != ASYNC_UNKNOWN_STATUS) return 1;
return 0; return 0;
} }
@ -571,7 +668,7 @@ int async_waiting( struct async_queue *queue )
if (!(ptr = list_head( &queue->queue ))) return 0; if (!(ptr = list_head( &queue->queue ))) return 0;
async = LIST_ENTRY( ptr, struct async, queue_entry ); async = LIST_ENTRY( ptr, struct async, queue_entry );
return !async->terminated; return !async_terminated( async );
} }
static int cancel_async( struct process *process, struct object *obj, struct thread *thread, client_ptr_t iosb ) static int cancel_async( struct process *process, struct object *obj, struct thread *thread, client_ptr_t iosb )
@ -586,7 +683,7 @@ static int cancel_async( struct process *process, struct object *obj, struct thr
restart: restart:
LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry )
{ {
if (async->terminated || async->canceled || async->is_system) continue; if (async_terminated( async ) || async->canceled || async->is_system) continue;
if ((!obj || (get_fd_user( async->fd ) == obj)) && if ((!obj || (get_fd_user( async->fd ) == obj)) &&
(!thread || async->thread == thread) && (!thread || async->thread == thread) &&
(!iosb || async->data.iosb == iosb)) (!iosb || async->data.iosb == iosb))
@ -608,7 +705,7 @@ static int cancel_blocking( struct process *process, struct thread *thread, clie
restart: restart:
LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry )
{ {
if (async->terminated || async->canceled) continue; if (async_terminated( async ) || async->canceled) continue;
if (async->blocking && async->thread == thread && if (async->blocking && async->thread == thread &&
(!iosb || async->data.iosb == iosb)) (!iosb || async->data.iosb == iosb))
{ {
@ -628,7 +725,7 @@ void cancel_process_asyncs( struct process *process )
restart: restart:
LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry )
{ {
if (async->terminated || async->canceled) continue; if (async_terminated( async ) || async->canceled) continue;
async->canceled = 1; async->canceled = 1;
fd_cancel_async( async->fd, async ); fd_cancel_async( async->fd, async );
goto restart; goto restart;
@ -647,7 +744,7 @@ int async_close_obj_handle( struct object *obj, struct process *process, obj_han
restart: restart:
LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry )
{ {
if (async->terminated || async->canceled || get_fd_user( async->fd ) != obj) continue; if (async_terminated( async ) || async->canceled || get_fd_user( async->fd ) != obj) continue;
if (!async->completion || !async->data.apc_context || async->event) continue; if (!async->completion || !async->data.apc_context || async->event) continue;
async->canceled = 1; async->canceled = 1;
@ -664,7 +761,7 @@ void cancel_terminating_thread_asyncs( struct thread *thread )
restart: restart:
LIST_FOR_EACH_ENTRY( async, &thread->process->asyncs, struct async, process_entry ) LIST_FOR_EACH_ENTRY( async, &thread->process->asyncs, struct async, process_entry )
{ {
if (async->thread != thread || async->terminated || async->canceled) continue; if (async->thread != thread || async_terminated( async ) || async->canceled) continue;
if (async->completion && async->data.apc_context && !async->event) continue; if (async->completion && async->data.apc_context && !async->event) continue;
if (async->is_system) continue; if (async->is_system) continue;
@ -771,7 +868,7 @@ struct async *create_request_async( struct fd *fd, unsigned int comp_flags, cons
return NULL; return NULL;
} }
async->pending = 0; async->pending = 0;
async->direct_result = 1; async->state = ASYNC_INITIAL_DIRECT_RESULT;
async->is_system = !!is_system; async->is_system = !!is_system;
async->comp_flags = comp_flags; async->comp_flags = comp_flags;
} }
@ -793,7 +890,7 @@ struct async *find_pending_async( struct async_queue *queue )
{ {
struct async *async; struct async *async;
LIST_FOR_EACH_ENTRY( async, &queue->queue, struct async, queue_entry ) LIST_FOR_EACH_ENTRY( async, &queue->queue, struct async, queue_entry )
if (!async->terminated) return (struct async *)grab_object( async ); if (!async_terminated( async )) return (struct async *)grab_object( async );
return NULL; return NULL;
} }
@ -863,7 +960,7 @@ DECL_HANDLER(set_async_direct_result)
if (!async) return; if (!async) return;
if (!async->unknown_status || !async->terminated || !async->alerted) if (async->state != ASYNC_UNKNOWN_STATUS)
{ {
set_error( STATUS_INVALID_PARAMETER ); set_error( STATUS_INVALID_PARAMETER );
release_object( &async->obj ); release_object( &async->obj );
@ -872,7 +969,6 @@ DECL_HANDLER(set_async_direct_result)
if (status == STATUS_PENDING) if (status == STATUS_PENDING)
{ {
async->direct_result = 0;
async->pending = 1; async->pending = 1;
} }
else if (req->mark_pending) else if (req->mark_pending)