Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **A write submitted without an awaiter reports its failure on the handle.** `io_pipe_write_cb` and `io_pipe_writev_cb` know the libuv status, but on that path the request is freed here and its `free_cb` takes no status, so the error object was built and released without anyone seeing it — a consumer could only infer the failure from the read side, where a peer that shut its write half down looks exactly like one that is gone. Both callbacks now set `ZEND_ASYNC_IO_WRITE_FAILED` on the io handle, which the reactor never clears. Needs php-src carrying that flag (TrueAsync ABI 0.26.0); against an older one the report is compiled out.

### Fixed

- **Four MySQL tests shared one fixture table and dropped it under each other whenever the suite ran in parallel.** `createTestTable`, `cleanupTestTable` and `runAsyncTest` defaulted to `async_test` in both the pdo_mysql and the mysqli helper, and every caller took the default, so `002`, `004`, `011` and `012` raced on one name in one database: the `DROP TABLE IF EXISTS` of whichever started second removed the table the first had just created, and its `INSERT` came back as `Table 'test.async_test' doesn't exist`. The default is now `async_test_<pid>`, unique because run-tests gives each test a process. Measured over the four tests at `-j4`: 10 failing runs out of 10 before, 0 out of 10 after. The suite has been reaching MySQL on CI only intermittently, which is why the collision surfaced now. Test only.
Expand Down
17 changes: 17 additions & 0 deletions libuv_reactor.c
Original file line number Diff line number Diff line change
Expand Up @@ -4924,6 +4924,15 @@ static void io_pipe_write_cb(uv_write_t *write_request, int status)
req->base.transferred = -1;
req->base.exception =
async_new_exception(async_ce_input_output_exception, "Pipe write error: %s", uv_strerror(status));

/* The handle carries the verdict because the request may not: a
* fire-and-forget write is freed here, and its free_cb takes no status.
* Guarded because php-src carries the flag only from ABI 0.26.0. */
#ifdef ZEND_ASYNC_IO_WRITE_FAILED
if (io != NULL) {
io->base.state |= ZEND_ASYNC_IO_WRITE_FAILED;
}
#endif
}

req->base.completed = true;
Expand Down Expand Up @@ -4993,6 +5002,14 @@ static void io_pipe_writev_cb(uv_write_t *write_request, int status)
req->base.transferred = -1;
req->base.exception = async_new_exception(
async_ce_input_output_exception, "Pipe writev error: %s", uv_strerror(status));

/* See io_pipe_write_cb: the handle keeps the verdict for a caller whose
* completion carries none. */
#ifdef ZEND_ASYNC_IO_WRITE_FAILED
if (req->io != NULL) {
req->io->base.state |= ZEND_ASYNC_IO_WRITE_FAILED;
}
#endif
}
req->base.completed = true;

Expand Down
Loading