# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.7.1]
### Changed
- **The test suite dropped `test_executors`, breaking a publish cycle** - `test_executors` depends on this crate, so a dev-dependency pointing back at it formed a cycle that neither crate could publish out of; the `>=0.4.1, <0.6` range and the paragraph of manifest comment explaining it existed only to work around that. The two things the tests actually wanted from it are now supplied by wasm_lite, which this crate already depends on. `spin_on` becomes either `wasm_lite_std::block_on` (native-only tests, including the two that need `catch_unwind` around a driven future) or a plain `.await` in a test that is now an `async fn`; `#[test_executors::async_test]` becomes `#[wasm_lite::wasm_lite_test]`, which is what it expanded to on wasm32 anyway. Tests converted to `async fn` are driven by the browser event loop on wasm32 and by `block_on` off it, and the four that were `cfg_attr` pairs collapse to one attribute. `block_on` parks on a condvar with a real waker where `spin_on` busy-polled, so the native suite no longer burns a core per blocked test.
No library code changed, and `test_executors` remains a recommended reference executor in the crate docs - this is about what *this* crate's own tests link against.
- **Three `no_run` doctests now actually run** - The `TypedObserver` examples for cancellation-on-drop, `observe()` and `detach()` used `todo!()` as a stand-in for an observer, so they only ever type-checked. They now obtain a real observer by spawning on `current_executor()`, which is public, always available, and works in a browser. The setup is hidden behind `#`, so the rendered examples are unchanged - they are simply verified now, on both native and wasm32.
The remaining three `no_run` examples stay that way, and now record why in a form that answers the question rather than restating it: the two in `global_executor` cannot run because `set_global_executor` writes a process-wide `OnceLock` and edition 2024 merges a crate's doctests into a single process, so a running version would set the global executor for every other doctest and change what the neighbouring `is_some()` examples observe; the `SpawnedLocalTask` one cannot run because polling it needs a value of the concrete `Executor` type parameter and this crate exposes no public `SomeLocalExecutor`. The one `compile_fail` block is a deliberate negative test.
- **The wasm_lite dev-dependency comes from crates.io again** - The two features the tests above rely on - a public `wasm_lite_std::block_on`, and `#[wasm_lite_test]` accepting an `async fn` and registering natively - were committed upstream but unreleased when those tests landed, so the manifest carried a `[patch.crates-io]` block pointing at a sibling checkout. `cargo publish` ignores `[patch]` and resolves dev-dependencies from the registry, so that block blocked publishing. wasm_lite 0.1.2 has shipped: the dev-dependency now requires `0.1.2` and the patch block is gone. The library half still only needs `0.1.1`, and its requirements are unchanged.
## [0.7.0]
### Changed
- **`wasm_lite` and `wasm_lite_std` now come from crates.io** - Both were local path dependencies on a sibling checkout, which cannot be published and left the crate unresolvable for anyone without that checkout. They're pinned to the published `0.1.1` instead.
### Fixed
- **The wasm32 test suite runs again** - Three separate breakages: `scripts/wasm32/tests` invoked a `runner` binary that wasm_lite no longer builds (the runner is the `wasm_lite` CLI's `run` subcommand); the wasm32 link flags never exported `__stack_pointer`, which wasm_lite's worker bootstrap requires of any module that spawns threads; and the registry `wasm_lite` linked alongside the path copy reached through the dev-cycle patches, producing duplicate symbols at link time. The full suite now passes in a browser.
- **Erased notifiers receive the value, not the box around it** - Any task carrying a notifier panicked with "Downcast failed" the moment it completed, on every type-erased spawn path: `Box<dyn SomeExecutor>::spawn` (which is what `current_executor()` hands you), `spawn_objsafe`, `spawn_static`, `spawn_static_objsafe`, and the local objsafe path. The boxed notifier's "unboxing" impl forwarded `&Box<dyn Any>` unchanged, and Rust resolves that to `&dyn Any` by unsizing the *box* rather than dereferencing it - so the erased notifier tried to downcast a `Box<dyn Any>` to the task's output type and always failed. The panic fired inside `send`, before the result was published, which meant a task that ran perfectly well came back to its observer as `Cancelled`.
- **Successful tasks no longer report themselves cancelled** - `ObserverSender::drop` marked the in-flight cancellation token unconditionally, including when the task had already produced its value. Anything holding a clone of that token - `IS_CANCELLED.with(|c| c.cloned())` is public API - saw `is_cancelled()` come back `true` after a clean completion. Only a task dropped before it produced a value marks the token now.
- **Completed observers stay completed** - Consuming a successful observer with `observe()` or `.await` no longer sends a late cancellation request to the executor when the observer is dropped.
- **Observer wakeups no longer risk re-entrant deadlock** - Completion and cancellation now release the observer state mutex before invoking its waker, so eager executors can safely poll again inline.
- **Re-entrant static spawns keep task-locals private** - The native static fallback now waits for active task-local scopes to restore their values before polling a child task, preventing request context or credentials from leaking across task boundaries.
- **WASM tests use the runner that owns their test format** - The test script now invokes the wasm_lite runner instead of silently handing wasm_lite test sections to `wasm-bindgen-test-runner`; a missing runner fails fast with setup guidance.
- **Missing task executors stay missing** - Tasks polled without an executor now install an explicit empty task-local context instead of inheriting a re-entrant parent task's executor.
- **Task-locals now survive panics** - If a future panicked during a poll, both `task_local!` scopes and the built-in task-locals (`TASK_ID`, `TASK_LABEL`, `IS_CANCELLED`, ...) leaked the panicking task's values onto the OS thread, where unrelated tasks could observe them (and later `scope()` calls would panic). Restoration now happens in drop guards, so unwinding cleans up properly.
- **Nested `scope()` of the same task-local works** - `KEY.scope(a, async { KEY.scope(b, ...).await }).await` previously panicked with "Task-local already set"; it now shadows and restores the outer value, as the docs always promised.
- **Lost-wakeup fix in `LastResortExecutor`** - The waker could fire in the window between the executor deciding to sleep and actually parking on the condvar, hanging the task forever. The static path got this fix in d274a3b; the non-static path now matches, plus proper acquire/release ordering on the wake handshake for weakly-ordered targets.
- **`into_objsafe` preserves task identity** - Converting a task for type-erased spawning (including implicitly via `Box<dyn SomeExecutor>::spawn`) silently allocated a fresh `TaskID`, breaking correlation with the ID recorded at creation. The ID (and thus `TASK_ID` inside the future) is now preserved.
- **`detach()` no longer leaks** - The default `Observer::detach` was `mem::forget`, permanently leaking the observation channel (and the task's eventual result) for erased observers - every `spawn(...).detach()` through a boxed executor leaked an allocation. A new object-safe `Observer::detach_in_place(&mut self)` primitive detaches without leaking. **Breaking**: implementors of `Observer` must now provide `detach_in_place`.
- **`Task::spawn_local_current` is implemented** - It was a documented public method whose body was `todo!()`; it now spawns on the thread-local executor (and panics with a helpful message if none is set).
- **Last-resort executors respect `poll_after`** - Spawning a task configured with a future `poll_after` on the fallback executors used to trip an assertion; they now delay (sleeping on native, `setTimeout` on wasm) until the configured time.
- **`pin_static_to_thread` keeps the task's notifier** - The notifier was silently dropped, which reads as a spurious cancellation signal under the documented drop-without-notify contract; it's now notified on completion. `pin_static_to_thread` and `Task::pin_current` gained an `ObserverNotified` bound on the notifier parameter to support this.
- **wasm `close()` interception is reusable** - After a `close()` attempt in a context where close doesn't terminate execution (browser windows), the saved original handler was lost and later patches chained the wrapper to itself. State is now restored around the call.
- **Awaiting an observer after `observe()` took the value** panics with a clear message instead of `unreachable!`, and the behavior is documented.
- Misc: `thread_static_executor` no longer holds the thread-local borrow while running the last-resort fallback (which could panic re-entrantly); `Box<dyn SomeStaticExecutor>::spawn_static_async` now routes through the underlying executor's async spawn path; `common_poll` no longer calls user `clone_box` while holding a task-local borrow; `task_local!` expands with fully-qualified `::std` paths; docs corrected where they promised `TASK_LOCAL_EXECUTOR` population and synchronous wasm spawning that don't happen.
## [0.6.3]
### Added
- New project scripts for consolidated checks, docs, clippy, and tests across native and wasm targets.
### Changed
- Switched core synchronization paths to `wasm_safe_thread` for wasm-friendly behavior.
- Updated CI and wasm32 documentation/build flags for more reliable browser-target checks.
- Improved last-resort executor behavior on wasm to avoid blocking as aggressively.
- Documentation pipelines now skip dependency docs (`--no-deps`) to keep crate docs focused and avoid external rustdoc failures.
## [0.6.2]
### Added
- **Panic support for builtin executors** - You can now set `SOME_EXECUTOR_BUILTIN_SHOULD_PANIC=1` to make the builtin executor panic on task failures. Perfect for debugging when you want your executor to tell you loudly that something went wrong, rather than silently carrying on.
- **Improved WASM32 build configuration** - Fresh cargo config flags for wasm32 targets that make cross-compilation smoother. Your WASM builds just got a little friendlier.
### Changed
- **Documentation refresh** - Clearer docs to help you get up and running faster. We're not saying the old docs were confusing, but... let's just say these are better. Plus some fresh README updates so you know what you're getting into.
- **CI pipeline updates** - Housekeeping on the CI front to keep things running smoothly. Nothing you'll notice, but our build robots are happier.
### Fixed
- **WASM-bindgen compatibility** - Squashed a pesky issue ([wasm-bindgen#4820](https://github.com/wasm-bindgen/wasm-bindgen/issues/4820)) that was making WASM builds grumpy. Your WebAssembly projects should now compile without the drama.
### Housekeeping
- Added `.gitignore` for cleaner repos
- Updated Cargo metadata
- Bumped `test-executors` dev dependency to latest
- Clippy made us tidy up some code—you won't see the difference, but it's there, judging us silently
## [0.6.1] - Previous Release
*Initial tracked release for changelog purposes.*
[Unreleased]: https://github.com/drewcrawford/some_executor/compare/v0.7.1...HEAD
[0.7.1]: https://github.com/drewcrawford/some_executor/compare/v0.7.0...v0.7.1
[0.7.0]: https://github.com/drewcrawford/some_executor/compare/v0.6.3...v0.7.0
[0.6.3]: https://github.com/drewcrawford/some_executor/compare/v0.6.2...v0.6.3
[0.6.2]: https://github.com/drewcrawford/some_executor/compare/v0.6.1...v0.6.2
[0.6.1]: https://github.com/drewcrawford/some_executor/releases/tag/v0.6.1