On Sat Apr 29 14:54:15 2023 +0000, Jinoh Kang wrote:
To reduce complexity, I've tried refactoring the state management. Currently, the async has the following two state flags that deal with the state of the client that originally requested the async:
- `signaled`: indicates whether the async object is signaled; the client
may wait on the async handle before returning from the corresponding wine system call.
- Valid transition: 0 -> 1.
- This flag is set if only if any of the following conditions are true:
- Given that `!async->blocking`,
- `async_wake_obj(async)` has been called when
`async->unknown_status = 0`, or - `async_handoff(async, ...)` has been called with `get_error() != STATUS_ALERTED && (async->pending || !NT_ERROR(get_error()) && (get_error() != STATUS_PENDING || async->iosb->status != STATUS_PENDING)`. - (Regardless of `async->blocking`) `async_set_result(async, status, ...)` has been called with `async->terminated && (!async->alerted || status != STATUS_PENDING)`.
- Note that this flag exhibits slightly different behavior depending
on `async->blocking`. - If `async->blocking`, it tries to block the requestor until the async operation has completed. - If `!async->blocking`, it tries to signal as soon as the initial status is known, _unless_ the wait handle is being closed, in which case it doesn't bother to touch the flag at all.
- If signaled and a wait on the async object is satisfied, the
requestor function should have performed the synchronous completion sequence (either directly or via APC_ASYNC_IO) before the wait is satisfied, unless `async->pending`.
- `direct_result`: from Wine codebase, `a flag if we're passing result
directly from request instead of APC`.
- Valid transition: 1 -> 0 (after set to 1 from `create_request_async`).
- Whenever async state is settled, the flag is set if all of the
following conditions are true: - The async was created via create_request_async(), and - async_set_unknown_status() has not been called, and - The async handle has never been waited on (wait consumes the flag), and - Any of: - async_handoff() has not been called, or - From async_handoff(), the async is terminated or alerted synchronously.
- The flag has the following effect:
- For each async_terminate() call, it will send APC_ASYNC_IO if and
only if !direct_result. - For each async handle wait satisfaction, it will call async_set_result() if and only if direct_result. We can observe the following from above:
- The flags are defined in terms of what the flags *do* rather than what
the value of the flags *mean*. It doesn't help that the transitions for each flag are quite scattered and sparse.
- Their behaviors are dependent on the client-side async implementation,
so reasoning about them requires verifying how the requestor side behaves.
- Depending on other part of the async object state, one or more flags
may be left unobserved and thus ignored, effectivly becoming a "don't care" bit. This increases complexity and hinders maintenance in my opinion. In particular, it's difficult to determine precisely *when* the flag is or should be set or unset. I'd propose that the constituent conditions of each flag be refactored into separate state variables first. Paradoxically, it may even help combine two or more binary flags into an enum as the semantics become more simpler.
Independent of above, I'm thinking about introducing a new `completed` flag and replacing `terminated` with `completed || alerted`.