# Quick Analysis: - The test waits for the `WaitCommEvent` (overlapped) to signal in less than 1500 ms on EV_TXEMPTY after an asynchronous `WriteFile`. - The Wine implementation: `WaitCommEvent` = `DeviceIoControl(IOCTL_SERIAL_WAIT_ON_MASK)` → path serial.c → `wait_on()` launches an async poll via `async_wait_proc`. - EV_TXEMPTY is only generated if (in `check_events`): `( (!old->temt || pending_write) && new->temt )`, i.e. an edge (transition to void) OR if a write is still marked pending (`pending_write`). - `pending_write` is fetched via `get_wait_mask(..., &pending_write)` only if the mask includes EV_TXEMPTY. If there is a time lag where the write completes before the first polling iteration and `old->temt` is already true, no edge is seen → no event → timeout test.
## Why my NUMA change may break: - The poll loop depends on asynchronous wakeups (`STATUS_ALERTED` then recheck). Inter-CPU latency or reordering can cause: 1. The buffer to become empty (TEMT) before the initial `old->temt` state has been captured (it is captured in `get_irq_info` just before arming the async). If already empty, no transition. 2. The `pending_write` flag may be cleared early if the write completion arrives before the first evaluation in the asynchronous thread. - With more CPUs / modified affinities (NUMA), the race window increases → more timeouts.
## Technical points: - The code only triggers EV_TXEMPTY on transition; an already empty state initially produces nothing (while Windows seems to allow an immediate return if the TX is empty at the time of the call). - The test comment already mentions a weakness: "calling SetCommMask after WriteFile leads to WaitCommEvent failures." Here, we do the opposite (SetCommMask before WriteFile) to avoid this case, but the race remains possible.
## Provisional conclusion: Probable divergence between Wine and Windows: Windows documents that if the event has already occurred, `WaitCommEvent` (overlapped) completes immediately (no edge necessarily required). Wine requires an edge; on fast machines / different latencies (NUMA), the transmission (small buffer) empties before the async is activated → no event.
## Recommendation: 1. Confirm on Windows: Calling `WaitCommEvent` with EV_TXEMPTY right after configuring the port when the buffer is already empty should immediately return with EV_TXEMPTY (verify). 2. Adapt Wine: if the initial `check_events` (in the `STATUS_ALERTED` branch) finds nothing, should you redo a `get_irq_info` before arming to capture the transition? Or, simpler: if EV_TXEMPTY is in the mask and `old->temt` is already true at the time of initial arming, report immediately. 3. Alternative test: Relax the assertion on line 859 to accept a timeout and then the recovery path (the code already provides for a fallback) — but this masks a real divergence.
## Diagnostics to try before patching: - Enable traces: export `WINEDEBUG=+serial,+comm` to see `old->temt` / `new->temt`. - Insert a `Sleep(1)` just before `WaitCommEvent` to see if it makes the race more or less frequent (timing diagnostics). - Force a longer write (increase tbuf size or further decrease baud) to create a visible transition.
## Decision: You should fix the implementation (front-only) to match Windows semantics (level). The test is legitimate: it waits for the overlapped event to be reported in <1.5s (and almost immediately). Adapting the test would weaken the coverage.
## Patch suggestion (idea): In `wait_on()` after `get_irq_info(fd, &commio->irq_info)` and before calling the server: - If `(commio->evtmask & EV_TXEMPTY)` and `commio->irq_info.temt` is true → immediately complete with EV_TXEMPTY.
Or in `async_wait_proc`, first iteration: if `(commio->evtmask & EV_TXEMPTY)` and `!pending_write` and `commio->irq_info.temt` is already true, return the event.
## Edge cases: - Don't generate in a loop: remember that EV_TXEMPTY has already been delivered until a new write is pending (can use `pending_write` or store a flag). - Windows: EV_TXEMPTY is retriggered after each fill/empty cycle. Therefore, reset the flag if `pending_write` becomes true again.
## NUMA Risk: - Very low; additional checks are local.
## Next steps: - Add a `delivered_txempty` field to `async_commio`, handle it in `wait_on`/`async_wait_proc`. - Or, more minimally: treat the initial state as an event by making a pseudo-transition: in `check`