|
1 | 1 | # Changelog |
2 | 2 |
|
3 | | -## 3.0.0 (Unreleased) |
| 3 | +## 3.0.0 (2026-05-03) |
4 | 4 |
|
5 | 5 | ### Breaking Changes |
6 | 6 |
|
|
46 | 46 | `{ok, [Result1, ...]}` on success or `{error, {gather_failed, [{Idx, Reason}, ...]}}` |
47 | 47 | if any call fails. The previous implementation returned `gather_not_implemented`. |
48 | 48 |
|
| 49 | +- **Thread-callback flakes (issue #63)** - Six layered defects in the |
| 50 | + `erlang.call`/`erlang.async_call` plumbing could deliver wrong values to |
| 51 | + the wrong caller under load. Reads now loop on partial/EINTR with a |
| 52 | + monotonic deadline; sync writes use a single length-prefixed frame on a |
| 53 | + dirty I/O scheduler with deadlined non-blocking writes; the sync wire |
| 54 | + carries the originating callback id and the receiver discards mismatched |
| 55 | + frames; the async pipe has one writer process per fd with an |
| 56 | + atomics-bounded mailbox (`?ASYNC_WRITER_MAX_QUEUE = 10000`) and a |
| 57 | + resumable nonblocking parser on the read end; workers that fail to |
| 58 | + resync are unlinked from the pool, freed, and bounded by |
| 59 | + `MAX_POISONED_WORKERS = 64`. |
| 60 | + |
| 61 | +### Documentation |
| 62 | + |
| 63 | +- Audited every fenced code block in `README.md` and `docs/*.md` for |
| 64 | + current-API references. Fixed `Py_GIL_OWN` to `PyInterpreterConfig_OWN_GIL` |
| 65 | + in `docs/scalability.md`, corrected the `multi_executor` fallback claim |
| 66 | + in `docs/migration.md`, and repaired a broken `SharedDict` example in |
| 67 | + `docs/shared-dict.md`. |
| 68 | +- New `test/coverage_audit.md` maps every public `py:*` and `erlang.*` API |
| 69 | + to its test suite. Added cases for `py:cast/4`, `py:async_gather/2`, and |
| 70 | + `py:dup_fd/1` so each documented API has a regression test. |
| 71 | +- New `scripts/lint_doc_snippets.escript` (driven by `make lint-docs` and |
| 72 | + CI) statically validates every Erlang `py:Fn(/N)` call and parses every |
| 73 | + Python block in the docs. Snippets that intentionally show removed APIs |
| 74 | + or REPL output opt out via `<!-- skip-lint -->`. |
| 75 | + |
49 | 76 | ### Changed |
50 | 77 |
|
51 | 78 | - **Per-context worker threads** - Each context now gets its own dedicated pthread |
|
73 | 100 | - `context_dispatch_call/eval/exec` functions (dead code) |
74 | 101 | - References to `PY_MODE_MULTI_EXECUTOR` in context operations |
75 | 102 | - `py_async_pool` legacy gen_server (unused after async API rewire) |
| 103 | +- `priv/_erlang_impl/_ssl.py` (`SSLTransport`, `create_ssl_transport`) had no |
| 104 | + importer and was never wired into the asyncio event loop. Removed. |
| 105 | +- Internal `py_util` exports `send_response/3`, `normalize_timeout/1`, and |
| 106 | + `normalize_timeout/2` had no callers anywhere. Removed. The module is |
| 107 | + marked `@private`; no external API changes. |
76 | 108 | - **Explicit `py:subinterp_*` handle API removed.** `py:subinterp_create/0`, |
77 | 109 | `subinterp_destroy/1`, `subinterp_call/4,5`, `subinterp_eval/2,3`, |
78 | 110 | `subinterp_exec/2`, `subinterp_cast/4`, `subinterp_async_call/4`, |
|
0 commit comments