[RFC PATCH v4 1/5] server: Allow calling async_handoff() with status code STATUS_ALERTED.
Jinoh Kang
jinoh.kang.kr at gmail.com
Thu Feb 3 09:28:21 CST 2022
On 2/3/22 08:45, Zebediah Figura wrote:
> On 1/29/22 01:47, Jinoh Kang wrote:
...
>> diff --git a/server/async.c b/server/async.c
>> index 7aef28355f0..e169bb23225 100644
>> --- a/server/async.c
>> +++ b/server/async.c
>> @@ -338,6 +338,30 @@ obj_handle_t async_handoff( struct async *async, data_size_t *result, int force_
>> return async->wait_handle;
>> }
>> + if (!async->pending && get_error() == STATUS_ALERTED)
>
> Why "!async->pending"?
The rationale is twofold:
1. async_handoff() can be called with "async->pending = 1".
Notably the device manager and some sock IOCTLs call set_async_pending() before returning.
Also, there exists another conditional statement below which explicitly tests for "async->pending".
2. If "async->pending && get_error() == STATUS_ALERTED", the async will be left in an inconsistent state.
- There exists an established invariant that "async->unknown_status" implies "!async->pending".
It would be invalid that "async->unknown_status && async->pending", since an async
"even the initial state" of which "is not known yet" cannot possibly be queued as STATUS_PENDING.
- Unless "async->direct_result", an APC_ASYNC_IO is required for the async to progress;
Our code path however issues no APC calls, leaving the async in a stale, hard-to-debug state.
That said, an alternative would be to simply "assert( !async->pending )" inside the new code path.
However, I don't really see a reason to reject STATUS_ALERTED on a pending async; we can let it
simply follow the normal path, allowing the async to be terminated with STATUS_ALERTED as usual.
I think this best sticks to the principle of least surprise. It's entirely my own perspective, though...
>
>> + {
>> + /* Give the client opportunity to complete synchronously.
>> + * If it turns out that the I/O request is not actually immediately satiable,
>> + * the client may then choose to re-queue the async (with STATUS_PENDING).
>
> I'd explicitly mention *how* the async should be requeued here.
I worded it that way since each I/O operation can have their own way to manage their async queue
(e.g. socket operations would use sock->read_q/write_q, device managers would use its own IRP queue,
while others may just use fd->read_q/write_q). I chose to retain the abstractness so that the comment would't get easily stale or out of date.
That said, I suppose you specifically meant mentioning how to _transition_ the async to STATUS_PENDING.
I agree with you in that case. I'll add a mention of the new wineserver call here.
>
>> + */
>> + async->unknown_status = 1;
>> +
>> + /* Deactivate async so that it can be safely queued.
>> + * Otherwise, the async will be erroneously alerted the next time
>> + * someone calls async_wake_up() on the queue.
>
> Not to put too much weight on my own perspective, but I think it's clearer to explain *why* to do something, rather than why not.
By "why not" did you meant the "otherwise" part?
> In this case, what we're doing is mimicking a normal "alert" case, except that we're not using an APC.
If I understood correctly, I should just say instead:
> Emulate calling async_terminate() with STATUS_ALERTED (request client to complete I/O),
> but don't actually queue APC_ASYNC_IO under any circumstances.
Is this correct?
>
>> + *
>> + * Don't use async_terminate() here since we'd like to leave the IOSB
>> + * status as STATUS_PENDING.
>
> Wait, why is this important?
Oops, looks like a leftover from v3. My apologies...
(In v3 it was required for making async_handoff() behave on async "restart" case and properly return STATUS_PENDING; otherwise, it would notice the STATUS_ALERTED in async->iosb->status and return just that, ignoring our request to set the status to STATUS_PENDING and restart the async.)
>
>> Also we certainly don't want APC_ASYNC_IO
>> + * to fire in any circumstances.
>> + */
>> + async->terminated = 1;
>> + async->alerted = 1;
>> +
>> + async_reselect( async );
>> +
>> + return async->wait_handle;
>> + }
>> +
>> async->initial_status = get_error();
>> if (!async->pending && NT_ERROR( get_error() ))
--
Sincerely,
Jinoh Kang
More information about the wine-devel
mailing list