Skip to content
Merged
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 @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **`Context::get()` and `getLocal()` now throw `Async\ContextException` when the key is missing, instead of returning `null`.** They were exact duplicates of `find()`/`findLocal()`, which made them pointless. `get()` is the mandatory-value form; `find()` remains the one that answers `null`.

### Fixed
- **Segfault: a `PDOStatement` outliving a pooled `PDO` after the first async stream operation (#200).** The scheduler promotes the running main flow into the main coroutine on the fly, so a connection acquired before that (`ATTR_POOL_ENABLED`) was keyed to a context that no longer matched afterwards. The statement's destructor then saw it as orphaned and returned it to the pool, while the coroutine binding still owned it and returned it a second time — the pool destroyed the same connection twice. The main flow now keeps one binding across the launch, and every release path detaches the owning binding first.

- **Use-after-free when the cycle collector freed a pooled `PDO` before one of its statements (#200).** Releasing a borrowed connection read the pool through `stmt->dbh`, which GC may already have freed. A statement now reaches the pool through the connection itself, which holds its own reference, so the release is safe in any destruction order.

- **`delay()`/`timeout()` fired almost instantly after synchronous CPU work in the same coroutine (#185).** libuv computes a timer's deadline from `loop->time`, its clock cached once per loop iteration. A coroutine that burned CPU without yielding for longer than the timeout it then armed left that clock stale, so the deadline was already in the past and the timer fired at once — silently skipping the wait. Relative userland timeouts now refresh the reactor clock at arm time; hot per-I/O deadline timers are unaffected.

- **`foreach` over a `Channel` broke on the ordinary producer/consumer pattern.** Closing a channel while a consumer was parked in the loop left the wake-up `ChannelException` pending, so it escaped `foreach` uncaught — and surfaced against the producer's `close()`, which had done nothing wrong. An explicit `close()` now ends the iteration cleanly, while a deadlock or a disposal still propagates.
Expand Down
Loading