diff --git a/.agents/conventions.md b/.agents/conventions.md index 0438116..5bb21e7 100644 --- a/.agents/conventions.md +++ b/.agents/conventions.md @@ -7,4 +7,4 @@ - Keep native I/O backends under `scheduler/selector/` and runtime adapters alongside their runtime implementation. - Pass task owners explicitly when libraries spawn child tasks. Preserve barrier ownership, cancellation, and shutdown behavior. - Keep public implementation guidance in `context/implementation.md` and architecture decisions in `context/design.md`. -- Keep repository-only conventions under `.agents/`; use the Bake Cargo agent context for the shared publishing process. +- Keep repository-only conventions under `.agents/`; use the Socketry Project releasing skill for the shared publishing process. diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b02d9ac..23d8904 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -124,3 +124,16 @@ jobs: env: RUSTFLAGS: -Zsanitizer=${{ matrix.sanitizer }} TSAN_OPTIONS: suppressions=${{ github.workspace }}/.github/tsan-suppressions.txt + + test-result: + # Sanitizers are diagnostic; regular tests and coverage gate merging. + if: always() + needs: [coverage, test, io-uring, freebsd] + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Require successful tests and coverage + env: + JOB_RESULTS: ${{ toJSON(needs) }} + run: | + echo "$JOB_RESULTS" | jq -e 'all(.[]; .result == "success")' diff --git a/Cargo.lock b/Cargo.lock index 0f4513f..623bfb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "bake" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbed34a1e40a3a5893a12c47de45006de3ae53112db64c0919a6e49c1a38d7e0" +checksum = "2f96c84418680954a2d9c8fe9afffb35aafb0b7086cb3cc141d613a2f0a74b42" dependencies = [ "bake-macros", "linkme", @@ -87,7 +87,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml_ng", - "socketry-markdown", + "socketry-markdown 0.1.1", ] [[package]] @@ -101,7 +101,7 @@ dependencies = [ "bake-releases", "serde", "serde_json", - "socketry-markdown", + "socketry-markdown 0.1.1", "tempfile", "toml_edit", "ureq", @@ -114,21 +114,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4877b435fe237311d88e546e8fb2b4a9021aff3550b625b8286e27da0ec693fd" dependencies = [ "bake", - "socketry-markdown", + "socketry-markdown 0.1.1", "tempfile", ] [[package]] name = "bake-macros" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b801275922e3bbc727ecdbb809f1a82591744cad907c9e915bdfa9b28bf001f" +checksum = "696dc147efb72a305f77a058388c94e651cd3586222a0ccfe3fcf145c7143242" dependencies = [ "proc-macro2", "quote", "syn 2.0.119", ] +[[package]] +name = "bake-markdown" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be72df839f4db26a93f2fe0659e57ce7998900b9445b04ba1367d51b4ba38587" +dependencies = [ + "bake", + "socketry-markdown 0.3.0", +] + [[package]] name = "bake-readme" version = "0.2.0" @@ -137,7 +147,7 @@ checksum = "9cbf2eb3710006c27705edd4186c53bc85bcca4fa80ea927a3718660e59833f2" dependencies = [ "bake", "serde_json", - "socketry-markdown", + "socketry-markdown 0.1.1", "tempfile", ] @@ -148,7 +158,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8439a25fe3a3192a666570d3d907cfb5bff8fdb5df17c92e495bcd4132ce0283" dependencies = [ "bake", - "socketry-markdown", + "socketry-markdown 0.1.1", "tempfile", ] @@ -943,14 +953,14 @@ dependencies = [ [[package]] name = "socketry" -version = "0.1.3" +version = "0.1.4" dependencies = [ "socketry-executor", ] [[package]] name = "socketry-executor" -version = "0.1.3" +version = "0.1.4" dependencies = [ "async-io", "async-task", @@ -976,16 +986,27 @@ dependencies = [ "unicode-id", ] +[[package]] +name = "socketry-markdown" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c02f802fe678b660dd21a6b6130bc2d0eb3423c24b9f26ebddb46514769e788" +dependencies = [ + "regex", + "unicode-id", +] + [[package]] name = "socketry-project" -version = "0.3.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f3a4391b29d2fcfe7522e3fdf651aaff9fd3de6c530a90412060b6bd1ad4e4" +checksum = "c2d4af345d5695d21119455e14c348b42267bc0c18eea584b44599d24f42d33e" dependencies = [ "bake", "bake-agent-context", "bake-cargo", "bake-license", + "bake-markdown", "bake-readme", "bake-releases", "bake-test-rust", diff --git a/Cargo.toml b/Cargo.toml index a61632d..097440e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ manifest = "bake/Cargo.toml" reviewers = ["socketry/managers"] [workspace.package] -version = "0.1.3" +version = "0.1.4" edition = "2024" license = "MIT" repository = "https://github.com/socketry/socketry-rust" @@ -25,7 +25,7 @@ readme = "readme.md" include = ["Cargo.toml", "readme.md", "license.md", "releases.md", "context/**", "src/**"] [dependencies] -socketry-executor = { version = "0.1.3", path = "crates/executor", default-features = false } +socketry-executor = { version = "0.1.4", path = "crates/executor", default-features = false } [features] default = ["native"] diff --git a/bake/Cargo.toml b/bake/Cargo.toml index 6360540..994838d 100644 --- a/bake/Cargo.toml +++ b/bake/Cargo.toml @@ -5,5 +5,5 @@ edition.workspace = true publish = false [dependencies] -bake = "0.18.0" -socketry-project = ">=0.3.4" +bake = "0.19" +socketry-project = ">=0.3.7" diff --git a/bake/src/bake_generated_tasks/mod.rs b/bake/src/bake_generated_tasks/mod.rs index 06bcfcb..1792099 100644 --- a/bake/src/bake_generated_tasks/mod.rs +++ b/bake/src/bake_generated_tasks/mod.rs @@ -1,3 +1,5 @@ -// Generated by `cargo bake --regenerate`; do not edit. +// Released under the MIT License. +// Copyright, 2026, by Samuel Williams. +// Generated by `cargo bake --regenerate`; do not edit. use socketry_project as _; diff --git a/context/design.md b/context/design.md index 6d6b8c2..e620289 100644 --- a/context/design.md +++ b/context/design.md @@ -1,52 +1,29 @@ # Future foundation -This document records the direction for Socketry's Rust foundation. The -executor now polls futures directly using async-task and Crossbeam queues, -with worker threads, work stealing and explicit task owners. The coroutine -prototype is preserved in commit `b520f3d` on branch `coroutine`. Portable TCP, -file and clock capabilities, native selectors and a Tokio adapter are now -implemented. The io-event timer port and further optimizations remain planned. +This document records the direction for Socketry's Rust foundation. The executor now polls futures directly using async-task and Crossbeam queues, with worker threads, work stealing and explicit task owners. The coroutine prototype is preserved in commit `b520f3d` on branch `coroutine`. Portable TCP, file and clock capabilities, native selectors and a Tokio adapter are now implemented. The io-event timer port and further optimizations remain planned. ## Goals -- Port the useful designs and modular organization of Socketry's Ruby - libraries into Rust. +- Port the useful designs and modular organization of Socketry's Ruby libraries into Rust. - Use futures for task execution, with no private coroutine stack per task. -- Let applications choose a Socketry runtime, Tokio, or another implementation - of the capabilities a library needs. -- Support Linux io_uring without compromising buffer lifetime or cancellation - safety, with an explicit strategy for other platforms. -- Port io-event's timer algorithm and preserve its batching and cancellation - behavior. +- Let applications choose a Socketry runtime, Tokio, or another implementation of the capabilities a library needs. +- Support Linux io\_uring without compromising buffer lifetime or cancellation safety, with an explicit strategy for other platforms. +- Port io-event's timer algorithm and preserve its batching and cancellation behavior. - Establish conventions and agent context that other Socketry crates can use. ## Shared interfaces and concrete implementations -Rust already standardizes the interface between a future and an executor: -`Future::poll`, `Context`, and `Waker`. Awaiting a future does not inherently -select a particular scheduler. Spawning tasks, creating timers, and opening -sockets require additional services, which are not all standardized by -`std::future`. +Rust already standardizes the interface between a future and an executor: `Future::poll`, `Context`, and `Waker`. Awaiting a future does not inherently select a particular scheduler. Spawning tasks, creating timers, and opening sockets require additional services, which are not all standardized by `std::future`. -Make those services explicit. A library should receive the capabilities it -uses: a task owner, a clock, a connector, or an existing stream. Avoid requiring -a complete runtime just to parse a protocol or operate on an existing stream. +Make those services explicit. A library should receive the capabilities it uses: a task owner, a clock, a connector, or an existing stream. Avoid requiring a complete runtime just to parse a protocol or operate on an existing stream. -Zig's `std.Io` is a useful reference for passing an implementation into code. -The Rust translation uses future-returning operations and ordinary `.await`. -It does not reproduce Zig's synchronous-looking suspension mechanism. +Zig's `std.Io` is a useful reference for passing an implementation into code. The Rust translation uses future-returning operations and ordinary `.await`. It does not reproduce Zig's synchronous-looking suspension mechanism. -Prefer generic traits and concrete future types initially. Runtime selection -can happen when constructing the application. Supporting different runtimes -does not require a trait object or heap allocation for every operation. Add -dynamic dispatch where a real caller needs runtime selection at that boundary. +Prefer generic traits and concrete future types initially. Runtime selection can happen when constructing the application. Supporting different runtimes does not require a trait object or heap allocation for every operation. Add dynamic dispatch where a real caller needs runtime selection at that boundary. ### Candidate package boundaries -The workspace currently contains `socketry` and `socketry-executor`. The other -names below describe possible extractions from the executor, not packages to -publish before they have code. Split modules into packages when consumers or -optional dependencies justify it. +The workspace currently contains `socketry` and `socketry-executor`. The other names below describe possible extractions from the executor, not packages to publish before they have code. Split modules into packages when consumers or optional dependencies justify it. | Package | Responsibility | | --- | --- | @@ -56,129 +33,64 @@ optional dependencies justify it. | `socketry-timers` | Timer ordering, cancellation, and batching, independent of an executor or OS selector. | | `socketry-tokio` | Tokio implementations of the portable contracts and focused compatibility adapters. | -Keep protocol parsing and serialization independent of the runtime wherever -possible. Higher-level protocol packages need not live in this repository. +Keep protocol parsing and serialization independent of the runtime wherever possible. Higher-level protocol packages need not live in this repository. ## Execution and ownership -The executor polls pinned future state on worker threads' ordinary stacks. -Use Send futures for tasks eligible to run on different workers. A local task -facility can accept non-Send futures with explicit thread restrictions. -Pinned future storage stays at a stable address while the worker allowed to -poll it may change. Only one worker may poll a task at a time. +The executor polls pinned future state on worker threads' ordinary stacks. Use Send futures for tasks eligible to run on different workers. A local task facility can accept non-Send futures with explicit thread restrictions. Pinned future storage stays at a stable address while the worker allowed to poll it may change. Only one worker may poll a task at a time. -The scheduler owns top-level tasks. A parent task explicitly owns child groups -through barriers. Both a scheduler and a barrier expose the spawning contract, -so accepting connections can take either as its task owner. The owner controls -the task lifetime; execution still goes through the selected executor. +The scheduler owns top-level tasks. A parent task explicitly owns child groups through barriers. Both a scheduler and a barrier expose the spawning contract, so accepting connections can take either as its task owner. The owner controls the task lifetime; execution still goes through the selected executor. - Spawning registers ownership before a task can run. - Joining reports completion and failures to the owner. - Waiting for a barrier waits for its owned children. - Stopping requests cancellation and waits for termination and cleanup. - Closing an owner prevents new children from escaping into a stopping group. -- Dropping a public handle must have a documented policy; it must not - accidentally detach a child that still belongs to a barrier. +- Dropping a public handle must have a documented policy; it must not accidentally detach a child that still belongs to a barrier. -Use owned, `'static` futures for independently spawned tasks initially. A -parent-child relationship alone does not make borrowing a parent's local -variables safe. Scoped borrowing requires a separate lifetime design that -remains sound when futures or handles are dropped or forgotten. +Use owned, `'static` futures for independently spawned tasks initially. A parent-child relationship alone does not make borrowing a parent's local variables safe. Scoped borrowing requires a separate lifetime design that remains sound when futures or handles are dropped or forgotten. ### Cleanup in a future executor -Stable Rust's Drop implementation cannot await. A barrier destructor can -request cancellation, but cannot promise that children on other workers have -finished cleanup when it returns. An explicit `stop().await` can wait. +Stable Rust's Drop implementation cannot await. A barrier destructor can request cancellation, but cannot promise that children on other workers have finished cleanup when it returns. An explicit `stop().await` can wait. -For automatic ownership cleanup, the executor must retain the task/group -record while children drain. It can delay reporting the parent as fully -terminated until descendants finish. This is a task lifecycle guarantee; it -does not keep every local variable in a dropped parent future alive. +For automatic ownership cleanup, the executor must retain the task/group record while children drain. It can delay reporting the parent as fully terminated until descendants finish. This is a task lifecycle guarantee; it does not keep every local variable in a dropped parent future alive. -Distinguish cooperative cancellation, dropping a future, and waiting for -termination. Dropping a future runs synchronous destructors but does not run -the remainder of its async body. Graceful asynchronous shutdown requires a -live future that the runtime continues to poll. Runtime shutdown must define -how cancellation and pending I/O completions are drained. +Distinguish cooperative cancellation, dropping a future, and waiting for termination. Dropping a future runs synchronous destructors but does not run the remainder of its async body. Graceful asynchronous shutdown requires a live future that the runtime continues to poll. Runtime shutdown must define how cancellation and pending I/O completions are drained. -Contextual APIs such as `Scheduler::current()` can remain conveniences. Set -and restore their context around every poll, including when polling panics; -do not assume a future always runs on the thread where it was created. +Contextual APIs such as `Scheduler::current()` can remain conveniences. Set and restore their context around every poll, including when polling panics; do not assume a future always runs on the thread where it was created. ### Current executor -The implementation currently lives in socketry-executor. async-task owns -pinned future storage and its runnable/waker state. Socketry supplies thread -management, ownership, cancellation flags, task identity and queue selection. -Each worker has a FIFO Crossbeam queue plus an incoming injector for remote -wakeups. External submissions enter a global injector; worker submissions go -to that worker's queue. Wakeups target the last worker. Only workers without -their own work steal from other local queues or incoming queues. - -Periodic external checks prevent a busy local queue from starving submissions. -Idle flags and a post-registration queue search coordinate parking and wakeups. -Task registration and completion use an ownership mutex; ordinary polling and -wakeups use the task state and queues without that mutex. - -Scheduler, SchedulerHandle and Barrier implement Spawn. Its associated handle -type permits runtime-specific join implementations. Public TaskHandle drop -abandons a result while leaving execution owned. Awaiting it reports output, -cancellation or a panic payload. Barrier wait/stop join direct children; parent -termination does not yet automatically wait for descendants. Explicitly await -barriers when that guarantee is required. - -Native I/O and sleep capabilities now accompany the executor. Regular-file -fallbacks use blocking pools; there is no public blocking-task API or local -non-Send task executor. Coroutine sources and historical tests are preserved -on their branch and are no longer built by this workspace. +The implementation currently lives in socketry-executor. async-task owns pinned future storage and its runnable/waker state. Socketry supplies thread management, ownership, cancellation flags, task identity and queue selection. Each worker has a FIFO Crossbeam queue plus an incoming injector for remote wakeups. External submissions enter a global injector; worker submissions go to that worker's queue. Wakeups target the last worker. Only workers without their own work steal from other local queues or incoming queues. + +Periodic external checks prevent a busy local queue from starving submissions. Idle flags and a post-registration queue search coordinate parking and wakeups. Task registration and completion use an ownership mutex; ordinary polling and wakeups use the task state and queues without that mutex. + +Scheduler, SchedulerHandle and Barrier implement Spawn. Its associated handle type permits runtime-specific join implementations. Public TaskHandle drop abandons a result while leaving execution owned. Awaiting it reports output, cancellation or a panic payload. Barrier wait/stop join direct children; parent termination does not yet automatically wait for descendants. Explicitly await barriers when that guarantee is required. + +Native I/O and sleep capabilities now accompany the executor. Regular-file fallbacks use blocking pools; there is no public blocking-task API or local non-Send task executor. Coroutine sources and historical tests are preserved on their branch and are no longer built by this workspace. ## Compatibility with Tokio and other runtimes Separate three levels of compatibility: -1. A future that needs only standard polling and wakeups can run on a suitable - executor, subject to its Send and lifetime requirements. -2. A library written against portable spawn, clock, and I/O contracts can use - different implementations of those contracts. -3. A library that directly calls Tokio APIs needs the relevant Tokio runtime - services. Implementing an I/O trait does not replace those services. - -Tokio and futures-io expose different AsyncRead and AsyncWrite traits. -`tokio-util::compat` already adapts these traits in both directions. Reuse -those adapters where they fit. An adapted Tokio socket still belongs to its -Tokio I/O driver, which must remain alive and be driven. - -Entering a Tokio runtime context provides access to its services; entering -alone does not drive the runtime. Some mixed execution is possible when the -required services are running, but must be established for the concrete APIs -being used. Do not advertise universal Tokio compatibility from a Waker or -stream adapter alone. - -The optional `scheduler::tokio` adapter implements Network, FileIo, Clock and -Spawn against an existing runtime. It preserves direct-child barrier ownership -and joins owned task destruction on asynchronous shutdown. The same generic -TCP program runs on Socketry and Tokio. The adapter scopes runtime context to -individual polls when registering resources; its futures can also be polled by -Socketry workers while Tokio drives the underlying services. Socketry contextual -lookups still identify Socketry execution; portable code passes handles explicitly. - -For existing libraries tied to Tokio, either keep their work on Tokio -and bridge owned messages/results, or provide the particular trait adapter -they consume. Avoid a broad imitation of Tokio's API. - -## I/O and io_uring - -Account for completion-based I/O before fixing the portable API. An operation -submitted to io_uring may still access its buffer after the Rust future -waiting for it is dropped. A borrowed byte slice and a synchronous destructor -are not sufficient to establish that the kernel has stopped using memory. - -Use an operation model that transfers ownership of a stable buffer to the -selector and returns it with the result on completion. The selector retains the -buffer and required descriptor ownership until it knows all kernel accesses -are finished, even when the caller abandons its future. Requesting cancellation -does not by itself prove that the original operation has finished. +1. A future that needs only standard polling and wakeups can run on a suitable executor, subject to its Send and lifetime requirements. +2. A library written against portable spawn, clock, and I/O contracts can use different implementations of those contracts. +3. A library that directly calls Tokio APIs needs the relevant Tokio runtime services. Implementing an I/O trait does not replace those services. + +Tokio and futures-io expose different AsyncRead and AsyncWrite traits. `tokio-util::compat` already adapts these traits in both directions. Reuse those adapters where they fit. An adapted Tokio socket still belongs to its Tokio I/O driver, which must remain alive and be driven. + +Entering a Tokio runtime context provides access to its services; entering alone does not drive the runtime. Some mixed execution is possible when the required services are running, but must be established for the concrete APIs being used. Do not advertise universal Tokio compatibility from a Waker or stream adapter alone. + +The optional `scheduler::tokio` adapter implements Network, FileIo, Clock and Spawn against an existing runtime. It preserves direct-child barrier ownership and joins owned task destruction on asynchronous shutdown. The same generic TCP program runs on Socketry and Tokio. The adapter scopes runtime context to individual polls when registering resources; its futures can also be polled by Socketry workers while Tokio drives the underlying services. Socketry contextual lookups still identify Socketry execution; portable code passes handles explicitly. + +For existing libraries tied to Tokio, either keep their work on Tokio and bridge owned messages/results, or provide the particular trait adapter they consume. Avoid a broad imitation of Tokio's API. + +## I/O and io\_uring + +Account for completion-based I/O before fixing the portable API. An operation submitted to io\_uring may still access its buffer after the Rust future waiting for it is dropped. A borrowed byte slice and a synchronous destructor are not sufficient to establish that the kernel has stopped using memory. + +Use an operation model that transfers ownership of a stable buffer to the selector and returns it with the result on completion. The selector retains the buffer and required descriptor ownership until it knows all kernel accesses are finished, even when the caller abandons its future. Requesting cancellation does not by itself prove that the original operation has finished. Plan for: @@ -190,116 +102,63 @@ Plan for: - Runtime capability probing and explicit fallback or unsupported errors. - Non-Linux implementations using suitable readiness or OS completion APIs. -A borrowed-buffer AsyncRead/AsyncWrite facade can sit above an owned-buffer -selector using internal buffers when necessary. Document the copying and -buffering costs of adapters. Do not require the lowest-level io_uring API to -pretend every operation has borrowed-buffer semantics. +A borrowed-buffer AsyncRead/AsyncWrite facade can sit above an owned-buffer selector using internal buffers when necessary. Document the copying and buffering costs of adapters. Do not require the lowest-level io\_uring API to pretend every operation has borrowed-buffer semantics. -Keep ring ownership explicit. The initial implementation uses a dedicated -selector thread. Work stealing routes operations to that selector without -moving their registrations. A pinned address does not establish Send. +Keep ring ownership explicit. The initial implementation uses a dedicated selector thread. Work stealing routes operations to that selector without moving their registrations. A pinned address does not establish Send. -Tokio-uring is useful reference code for owned-buffer operations. It has its -own driver/runtime requirements; using it does not make ordinary Tokio I/O -and io_uring interchangeable. +Tokio-uring is useful reference code for owned-buffer operations. It has its own driver/runtime requirements; using it does not make ordinary Tokio I/O and io\_uring interchangeable. ### Current selectors -The implementations live under `scheduler/selector/`, selected with Cargo -features and target configuration. epoll, kqueue and iocp expose shared async-io -readiness operations; the process-wide reactor owns persistent registrations. -Windows uses IOCP/AFD socket readiness. Regular-file fallback operations use a -blocking pool, because readiness does not make ordinary file I/O nonblocking. - -The Linux `io-uring` feature selects a dedicated ring for socket/file reads and -writes. Connect, accept, explicit readiness waits and sleep still use async-io. -Initialization probes required opcodes and reliable completion overflow; it -returns an error if unavailable rather than silently changing implementation. -Shutdown cancels and drains original completions. Operation identifiers are -never reused, and cancellation completions cannot release original buffers. -Unexpected selector failure retains resources whose kernel lifetime cannot be -established and panics their waiting operations. Normal cancellation does not -retain completed resources. +The implementations live under `scheduler/selector/`, selected with Cargo features and target configuration. epoll, kqueue and iocp expose shared async-io readiness operations; the process-wide reactor owns persistent registrations. Windows uses IOCP/AFD socket readiness. Regular-file fallback operations use a blocking pool, because readiness does not make ordinary file I/O nonblocking. + +The Linux `io-uring` feature selects a dedicated ring for socket/file reads and writes. Connect, accept, explicit readiness waits and sleep still use async-io. Initialization probes required opcodes and reliable completion overflow; it returns an error if unavailable rather than silently changing implementation. Shutdown cancels and drains original completions. Operation identifiers are never reused, and cancellation completions cannot release original buffers. Unexpected selector failure retains resources whose kernel lifetime cannot be established and panics their waiting operations. Normal cancellation does not retain completed resources. The current owned-buffer API uses Vec values and returns `(io::Result, -Vec)`. Read buffers must already have an initialized length. Operations may -be partial; dropping a waiting future can abandon I/O that consumes or transmits -bytes. Socket and listener types are associated with the concrete implementation. -Tokio registrations reject use through another runtime's adapter. +Vec)`. Read buffers must already have an initialized length. Operations may be partial; dropping a waiting future can abandon I/O that consumes or transmits bytes. Socket and listener types are associated with the concrete implementation. Tokio registrations reject use through another runtime's adapter. -This is an initial implementation: each ring operation uses a command and a -completion channel, and operation records and buffers are not pooled. General -descriptors, UDP and native overlapped Windows files remain future work. -Blocking file operations can outlive task cancellation; neither Socketry nor -the Tokio adapter shuts down the process-wide or external blocking pool. +This is an initial implementation: each ring operation uses a command and a completion channel, and operation records and buffers are not pooled. General descriptors, UDP and native overlapped Windows files remain future work. Blocking file operations can outlive task cancellation; neither Socketry nor the Tokio adapter shuts down the process-wide or external blocking pool. ## Timer port -The source reviewed is socketry/io-event commit -`66caf64fbe27d752e4c64a60a7d0fba8bceaddbc`: +The source reviewed is socketry/io-event commit `66caf64fbe27d752e4c64a60a7d0fba8bceaddbc`: - `lib/io/event/timers.rb` - `lib/io/event/priority_heap.rb` - `test/io/event/timers.rb` - `test/io/event/priority_heap.rb` -This is a binary min-heap with deferred insertion and lazy cancellation. -Preserve these choices in the Rust port: +This is a binary min-heap with deferred insertion and lazy cancellation. Preserve these choices in the Rust port: - Scheduling appends to a pending batch; heap insertion is deferred. - Cancellation clears the payload and updates bookkeeping in constant time. - Cancelled pending timers are filtered before insertion. - Cancelled timers at the heap root are removed when querying or firing. -- Compact the heap when at least 128 retained entries are cancelled and - cancelled entries are more than half the heap. -- Clear an entirely cancelled heap even below that threshold when there is - no live pending batch. +- Compact the heap when at least 128 retained entries are cancelled and cancelled entries are more than half the heap. +- Clear an entirely cancelled heap even below that threshold when there is no live pending batch. - Combine compaction and pending insertion into one heap rebuild. -- Build from empty or rebuild when the incoming batch exceeds twice the - existing heap size; otherwise insert incrementally. +- Build from empty or rebuild when the incoming batch exceeds twice the existing heap size; otherwise insert incrementally. -Keep the timer queue independent of a scheduler, OS sleep, or Tokio. Let the -selector provide monotonic time and consume expired entries. An executor-facing -sleep future registers a waker with the selector, which uses the queue to choose -its next wake deadline. Wake callbacks outside queue locks. +Keep the timer queue independent of a scheduler, OS sleep, or Tokio. Let the selector provide monotonic time and consume expired entries. An executor-facing sleep future registers a waker with the selector, which uses the queue to choose its next wake deadline. Wake callbacks outside queue locks. -Use Instant/Duration or an explicit monotonic tick type rather than floating -point deadlines. Accept the current time explicitly in queue operations so -behavior can be checked without wall-clock sleeps. Record where Rust behavior -differs: expired deadlines can use a zero wait duration, and public live counts -should be distinguished from retained cancelled entries. +Use Instant/Duration or an explicit monotonic tick type rather than floating point deadlines. Accept the current time explicitly in queue operations so behavior can be checked without wall-clock sleeps. Record where Rust behavior differs: expired deadlines can use a zero wait duration, and public live counts should be distinguished from retained cancelled entries. -Reusable registration handles must not allow an old cancellation to affect a -new timer occupying the same storage. Document reset behavior and whether it -reuses an allocation. Preserve MIT attribution for Samuel Williams and Wander -Hillen, the source paths, and the revision used for the translated code. +Reusable registration handles must not allow an old cancellation to affect a new timer occupying the same storage. Document reset behavior and whether it reuses an allocation. Preserve MIT attribution for Samuel Williams and Wander Hillen, the source paths, and the revision used for the translated code. -The algorithm's existing Ruby performance motivates the port. Rust performance -claims require measurements of scheduling, cancellation, expiration, batching, -and retained memory under representative workloads. +The algorithm's existing Ruby performance motivates the port. Rust performance claims require measurements of scheduling, cancellation, expiration, batching, and retained memory under representative workloads. ## Implementation sequence 1. Record boundaries and reusable Rust conventions (implemented). -2. Replace stackful execution with async-task and Crossbeam worker queues - (implemented). Keep the coroutine prototype in its saved branch. -3. Implement explicit owners, barriers, cancellation and shutdown (implemented - for direct children). Automatic descendant draining remains future work. -4. Establish minimal clock and I/O contracts with concrete consumers - (implemented with Network, FileIo, Clock and a portable TCP example). -5. Implement native readiness and the Tokio adapter, running the same consumers - with both (implemented). Native sleep uses async-io until the timer port. -6. Implement io_uring's owned-buffer lifecycle, socket/file operations, - cancellation and runtime probing (implemented). Improve operation reuse, - buffer registration and submission backpressure in subsequent work. +2. Replace stackful execution with async-task and Crossbeam worker queues (implemented). Keep the coroutine prototype in its saved branch. +3. Implement explicit owners, barriers, cancellation and shutdown (implemented for direct children). Automatic descendant draining remains future work. +4. Establish minimal clock and I/O contracts with concrete consumers (implemented with Network, FileIo, Clock and a portable TCP example). +5. Implement native readiness and the Tokio adapter, running the same consumers with both (implemented). Native sleep uses async-io until the timer port. +6. Implement io\_uring's owned-buffer lifecycle, socket/file operations, cancellation and runtime probing (implemented). Improve operation reuse, buffer registration and submission backpressure in subsequent work. 7. Port the timer queue with upstream attribution and deterministic verification. -8. Measure the executor and complete system. Multiple workers and work stealing - are implemented, but performance claims require benchmarks. +8. Measure the executor and complete system. Multiple workers and work stealing are implemented, but performance claims require benchmarks. -When implementation verification is requested, cover timer threshold -boundaries, duplicate and late wakeups, cancellation races, parent cleanup, -I/O dropped in flight, and backend compatibility. Linux execution is required -to establish io_uring behavior; a build on another platform does not do so. +When implementation verification is requested, cover timer threshold boundaries, duplicate and late wakeups, cancellation races, parent cleanup, I/O dropped in flight, and backend compatibility. Linux execution is required to establish io\_uring behavior; a build on another platform does not do so. ## References diff --git a/context/implementation.md b/context/implementation.md index 9d06058..21d6d6e 100644 --- a/context/implementation.md +++ b/context/implementation.md @@ -1,115 +1,55 @@ # Implementation Context -This guide describes the current implementation and its boundaries. Read -[the design guide](design.md) before changing public APIs, runtime boundaries, -package names or workspace layout. Shared Rust development guidance is provided -by the `bake-agent-context` dependency. +This guide describes the current implementation and its boundaries. Read [the design guide](design.md) before changing public APIs, runtime boundaries, package names or workspace layout. Shared Rust development guidance is provided by the `bake-agent-context` dependency. ## Implemented foundation -- `socketry` is the facade for `socketry-executor`. Shared Rust development - guidance is provided by the `bake-agent-context` crate. +- `socketry` is the facade for `socketry-executor`. Shared Rust development guidance is provided by the `bake-agent-context` crate. - `socketry-executor` executes futures using async-task and Crossbeam queues. -- Scheduler construction starts worker threads. Tasks require Send + 'static; - the pinned future remains stationary while workers may change between polls. -- Workers own FIFO ready queues and concurrent incoming queues. Wakeups target - the last worker; idle workers steal from ready queues and incoming queues. +- Scheduler construction starts worker threads. Tasks require Send + 'static; the pinned future remains stationary while workers may change between polls. +- Workers own FIFO ready queues and concurrent incoming queues. Wakeups target the last worker; idle workers steal from ready queues and incoming queues. - A shared injector accepts submissions originating outside a worker. -- Worker parking publishes an idle flag/count before rechecking all work. - Preserve this protocol and treat Steal::Retry differently from empty. -- Queue operations use Crossbeam synchronization. Task admission, completion, - cancellation and closure use a separate ownership registry mutex. +- Worker parking publishes an idle flag/count before rechecking all work. Preserve this protocol and treat Steal::Retry differently from empty. +- Queue operations use Crossbeam synchronization. Task admission, completion, cancellation and closure use a separate ownership registry mutex. - Never run user code or invoke wakers while holding the registry mutex. -- Task context is installed around execution and destruction and restored on - unwinding. Scheduler context is installed on workers and block_on roots. -- No coroutine stacks, nested synchronous wait or explicit transfer remain in - the current executor. The full prototype is on branch coroutine at b520f3d. +- Task context is installed around execution and destruction and restored on unwinding. Scheduler context is installed on workers and block\_on roots. +- No coroutine stacks, nested synchronous wait or explicit transfer remain in the current executor. The full prototype is on branch coroutine at b520f3d. ## Ownership -- Scheduler and Barrier implement Spawn, with a runtime-specific associated - handle type. Adapters need to preserve its ownership and result contract. +- Scheduler and Barrier implement Spawn, with a runtime-specific associated handle type. Adapters need to preserve its ownership and result contract. - Register ownership before publishing a runnable task. - Dropping TaskHandle abandons the result without cancelling the owner's task. -- Task cancellation sets a flag and wakes it. The future is destroyed after - an in-progress poll returns. TaskHandle::cancel awaits that destruction. -- Barrier::close prevents admission; wait awaits direct children; stop closes, - cancels and waits. Drop requests cancellation without waiting. -- Awaited task results report cancellation or the original panic payload. - Barrier waits and Scheduler::run do not aggregate child errors. -- Parents must explicitly await barriers for joined cleanup. Automatic waiting - for descendants after parent destruction is not implemented. -- Scheduler Drop cancels all tasks, joining threads outside a worker. On a - worker it requests shutdown without joining. Surviving handles reject spawn. +- Task cancellation sets a flag and wakes it. The future is destroyed after an in-progress poll returns. TaskHandle::cancel awaits that destruction. +- Barrier::close prevents admission; wait awaits direct children; stop closes, cancels and waits. Drop requests cancellation without waiting. +- Awaited task results report cancellation or the original panic payload. Barrier waits and Scheduler::run do not aggregate child errors. +- Parents must explicitly await barriers for joined cleanup. Automatic waiting for descendants after parent destruction is not implemented. +- Scheduler Drop cancels all tasks, joining threads outside a worker. On a worker it requests shutdown without joining. Surviving handles reject spawn. ## I/O and runtime boundaries -- `scheduler/mod.rs` defines Network, FileIo and Clock. Operations return - concrete Send futures; portable consumers receive the required capabilities. -- `scheduler/socketry.rs` owns the executor; `socketry/operations.rs` forwards - capabilities to its lazily initialized, compile-time selected selector. -- `scheduler/selector/` contains readiness, epoll, kqueue, iocp and io_uring. - Platform readiness modules share async-io's persistent registrations and - process-wide reactor. Registered sockets remain usable as tasks migrate. -- Default feature `native` provides TCP, positioned files and sleep. Feature - `io-uring` selects Linux completion reads/writes; other supported platforms - retain readiness. Feature `tokio` enables the separate runtime adapter. - No default features builds the executor and contracts without native I/O. -- Read/write buffers are owned Vec values, returned with ordinary errors as - well as success. Reads use the initialized length and leave it unchanged. - Operations can be partial; cancellation can consume or transmit bytes. -- io_uring's dedicated selector thread owns buffers and descriptors until the - original terminal completion, never merely a cancellation completion. Numeric - identifiers are not reused. Pending, unsubmitted requests can return buffers - immediately. Unexpected failure retains kernel-accessible resources and - panics waiting operations; do not free buffers with unknown completion. -- Ring shutdown closes admission, cancels and drains completions. Scheduler - Drop outside a worker waits for this drain; Drop on a worker only requests it. - Shutdown racing lazy initialization must still close the new selector. -- Regular files use blocking pools except with io_uring. An already-started - blocking operation can outlive its waiting task. Use non-append regular files - and explicit offsets; the Windows fallback also changes the shared cursor. -- `scheduler/tokio.rs` adapts an existing runtime with I/O and time enabled. - It preserves Spawn/barrier ownership, but does not own or drive that runtime. - Its shutdown joins owned task destruction, not the runtime's blocking pool. -- Register Tokio task ownership before spawn, but release the registry mutex - before calling into Tokio: a stopped runtime may synchronously drop a future. - Future destruction precedes ownership completion. Never hold an EnterGuard - across await; enter per poll for operations that register resources. -- Tokio resources carry runtime identity. Explicit handles select that runtime; - Socketry's Task::current and Scheduler::current remain Socketry-specific. +- `scheduler/mod.rs` defines Network, FileIo and Clock. Operations return concrete Send futures; portable consumers receive the required capabilities. +- `scheduler/socketry.rs` owns the executor; `socketry/operations.rs` forwards capabilities to its lazily initialized, compile-time selected selector. +- `scheduler/selector/` contains readiness, epoll, kqueue, iocp and io\_uring. Platform readiness modules share async-io's persistent registrations and process-wide reactor. Registered sockets remain usable as tasks migrate. +- Default feature `native` provides TCP, positioned files and sleep. Feature `io-uring` selects Linux completion reads/writes; other supported platforms retain readiness. Feature `tokio` enables the separate runtime adapter. No default features builds the executor and contracts without native I/O. +- Read/write buffers are owned Vec values, returned with ordinary errors as well as success. Reads use the initialized length and leave it unchanged. Operations can be partial; cancellation can consume or transmit bytes. +- io\_uring's dedicated selector thread owns buffers and descriptors until the original terminal completion, never merely a cancellation completion. Numeric identifiers are not reused. Pending, unsubmitted requests can return buffers immediately. Unexpected failure retains kernel-accessible resources and panics waiting operations; do not free buffers with unknown completion. +- Ring shutdown closes admission, cancels and drains completions. Scheduler Drop outside a worker waits for this drain; Drop on a worker only requests it. Shutdown racing lazy initialization must still close the new selector. +- Regular files use blocking pools except with io\_uring. An already-started blocking operation can outlive its waiting task. Use non-append regular files and explicit offsets; the Windows fallback also changes the shared cursor. +- `scheduler/tokio.rs` adapts an existing runtime with I/O and time enabled. It preserves Spawn/barrier ownership, but does not own or drive that runtime. Its shutdown joins owned task destruction, not the runtime's blocking pool. +- Register Tokio task ownership before spawn, but release the registry mutex before calling into Tokio: a stopped runtime may synchronously drop a future. Future destruction precedes ownership completion. Never hold an EnterGuard across await; enter per poll for operations that register resources. +- Tokio resources carry runtime identity. Explicit handles select that runtime; Socketry's Task::current and Scheduler::current remain Socketry-specific. ## Next boundaries -- The io-event timer port, operation pools, registered buffers, general - descriptor/UDP interfaces and overlapped Windows file operations remain - planned. Native sleep currently uses async-io; the adapter uses Tokio timers. -- Keep runtime requirements explicit; ordinary Future support does not provide - another runtime's I/O or timers. +- The io-event timer port, operation pools, registered buffers, general descriptor/UDP interfaces and overlapped Windows file operations remain planned. Native sleep currently uses async-io; the adapter uses Tokio timers. +- Keep runtime requirements explicit; ordinary Future support does not provide another runtime's I/O or timers. - There is no non-Send task executor, public blocking-task API or scoped borrowing. - Do not introduce unsafe Send, stack migration or nested blocking waits. -- Native coroutine sources, sanitizer hooks and historical tests belong to - the preserved coroutine branch, not the future executor's build. +- Native coroutine sources, sanitizer hooks and historical tests belong to the preserved coroutine branch, not the future executor's build. ## Verification -Public behavior is covered in crates/executor/tests. Deterministic channels -force stealing, migration, concurrent wakeups and cancellation during polling. -The parking test uses Loom to model the queue-publication/idle-registration -handshake. Keep its atomics and fence order aligned with the implementation; -this models the handshake, not Crossbeam or async-task internals. -When verification is requested, run workspace tests and doctests with `tokio` -enabled; on Linux also run all features to exercise io_uring. Check executor-only -and Tokio-only feature combinations. The same TCP/file consumers exercise both -implementations; Linux tests cover cancellation batches and shutdown races. -CI covers Linux, macOS, Windows and FreeBSD, with separate Linux io_uring and -sanitizer jobs; distinguish configured CI from executed results. +Public behavior is covered in crates/executor/tests. Deterministic channels force stealing, migration, concurrent wakeups and cancellation during polling. The parking test uses Loom to model the queue-publication/idle-registration handshake. Keep its atomics and fence order aligned with the implementation; this models the handshake, not Crossbeam or async-task internals. When verification is requested, run workspace tests and doctests with `tokio` enabled; on Linux also run all features to exercise io\_uring. Check executor-only and Tokio-only feature combinations. The same TCP/file consumers exercise both implementations; Linux tests cover cancellation batches and shutdown races. CI covers Linux, macOS, Windows and FreeBSD, with separate Linux io\_uring and sanitizer jobs; distinguish configured CI from executed results. -ThreadSanitizer loads `.github/tsan-suppressions.txt` to suppress Crossbeam's -internal queue `Buffer::read` and `Buffer::write` race reports. Crossbeam reads -slots speculatively and discards values when atomic validation fails. Its -non-atomic volatile accesses remain a known Rust memory-model limitation; -the suppression accepts that limitation rather than fixing it. See the -[upstream discussion](https://github.com/crossbeam-rs/crossbeam/issues/589#issuecomment-720972996). -Keep suppression patterns scoped to those buffer accesses and reassess them -when updating Crossbeam. Other race reports continue to fail the sanitizer job. +ThreadSanitizer loads `.github/tsan-suppressions.txt` to suppress Crossbeam's internal queue `Buffer::read` and `Buffer::write` race reports. Crossbeam reads slots speculatively and discards values when atomic validation fails. Its non-atomic volatile accesses remain a known Rust memory-model limitation; the suppression accepts that limitation rather than fixing it. See the [upstream discussion](https://github.com/crossbeam-rs/crossbeam/issues/589#issuecomment-720972996). Keep suppression patterns scoped to those buffer accesses and reassess them when updating Crossbeam. Other race reports continue to fail the sanitizer job. diff --git a/license.md b/license.md index e589fcc..17fbb94 100644 --- a/license.md +++ b/license.md @@ -1,21 +1,9 @@ # MIT License -Copyright, 2026, by Samuel Williams. +Copyright, 2026, by Samuel Williams. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/readme.md b/readme.md index 97adef4..702d86d 100644 --- a/readme.md +++ b/readme.md @@ -2,10 +2,7 @@ Foundational concurrency APIs for Rust projects in the Socketry ecosystem. -The `socketry` package re-exports `socketry-executor`: owned asynchronous -tasks, explicit child barriers, and a futures executor with multiple workers -and work stealing. Tasks use ordinary future polling and have no private -coroutine stacks. +The `socketry` package re-exports `socketry-executor`: owned asynchronous tasks, explicit child barriers, and a futures executor with multiple workers and work stealing. Tasks use ordinary future polling and have no private coroutine stacks. ## Usage @@ -30,22 +27,13 @@ fn main() -> Result<(), Box> { } ``` -Workers start when the scheduler is constructed. `spawn` accepts -`Send + 'static` futures and returns an awaitable result handle. Dropping that -handle leaves the task owned by its scheduler or barrier. Dropping the -scheduler requests cancellation and joins workers when called outside a worker. +Workers start when the scheduler is constructed. `spawn` accepts `Send + 'static` futures and returns an awaitable result handle. Dropping that handle leaves the task owned by its scheduler or barrier. Dropping the scheduler requests cancellation and joins workers when called outside a worker. -Use `scheduler.barrier()` to own children explicitly. Both schedulers and -barriers implement `Spawn`. `barrier.wait().await` waits for completion; -`barrier.stop().await` closes the barrier, cancels children, and waits for their -synchronous destructors. Dropping a barrier requests cancellation without -waiting. Observe individual results and panics through task handles. +Use `scheduler.barrier()` to own children explicitly. Both schedulers and barriers implement `Spawn`. `barrier.wait().await` waits for completion; `barrier.stop().await` closes the barrier, cancels children, and waits for their synchronous destructors. Dropping a barrier requests cancellation without waiting. Observe individual results and panics through task handles. -`Scheduler::current()` and `Task::current()` provide contextual lookup while -tasks run. Use `.await` to suspend; synchronous blocking calls occupy a worker. +`Scheduler::current()` and `Task::current()` provide contextual lookup while tasks run. Use `.await` to suspend; synchronous blocking calls occupy a worker. -See the [executor package](crates/executor/readme.md) for scheduling, -ownership, cancellation and allocation details. An executable example is: +See the [executor package](crates/executor/readme.md) for scheduling, ownership, cancellation and allocation details. An executable example is: ```sh cargo run --package socketry-executor --example work_stealing @@ -53,9 +41,7 @@ cargo run --package socketry-executor --example work_stealing ## Portable I/O and runtime selection -Generic code can accept `Network`, `FileIo`, `Clock`, and `Spawn` capabilities. -Socketry and the optional Tokio adapter implement these contracts with concrete -future and resource types. Import the traits to call their methods. +Generic code can accept `Network`, `FileIo`, `Clock`, and `Spawn` capabilities. Socketry and the optional Tokio adapter implement these contracts with concrete future and resource types. Import the traits to call their methods. | Cargo configuration | Implementation | | --- | --- | @@ -65,11 +51,7 @@ future and resource types. Import the traits to call their methods. | `default-features = false, features = ["tokio"]` | Tokio adapter without Socketry's native I/O dependencies. | | `default-features = false` | Task executor and portable contracts, without I/O implementations. | -Socket registrations persist across operations and worker migration. Reads and -writes take a reusable owned `Vec` and return `(io::Result, Vec)`. -Reads fill the buffer's existing length; allocate it with `vec![0; capacity]`. -Operations may transfer fewer bytes than requested. Dropping a future can -abandon an operation that has already consumed or transmitted bytes. +Socket registrations persist across operations and worker migration. Reads and writes take a reusable owned `Vec` and return `(io::Result, Vec)`. Reads fill the buffer's existing length; allocate it with `vec![0; capacity]`. Operations may transfer fewer bytes than requested. Dropping a future can abandon an operation that has already consumed or transmitted bytes. Run the same TCP exchange with either runtime: @@ -80,60 +62,39 @@ cargo run -p socketry-executor --example portable_io --no-default-features --fea cargo run -p socketry-executor --example portable_io --features io-uring ``` -The Tokio adapter needs a live runtime with I/O and time enabled. It preserves -explicit task/barrier ownership, and its asynchronous `shutdown().await` joins -task destruction. Passing its handle explicitly selects Tokio; Socketry's -`Scheduler::current()` continues to identify Socketry execution. +The Tokio adapter needs a live runtime with I/O and time enabled. It preserves explicit task/barrier ownership, and its asynchronous `shutdown().await` joins task destruction. Passing its handle explicitly selects Tokio; Socketry's `Scheduler::current()` continues to identify Socketry execution. ## Current scope -TCP connect/accept/read/write/readiness, positioned file reads/writes, and sleep -are implemented. Regular files use blocking pools except for Linux io_uring. -Windows socket readiness uses IOCP/AFD; native overlapped file operations are -not implemented. Socketry currently uses async-io's shared readiness reactor -and timers; the io-event timer port remains planned in the -[design guide](context/design.md). +TCP connect/accept/read/write/readiness, positioned file reads/writes, and sleep are implemented. Regular files use blocking pools except for Linux io\_uring. Windows socket readiness uses IOCP/AFD; native overlapped file operations are not implemented. Socketry currently uses async-io's shared readiness reactor and timers; the io-event timer port remains planned in the [design guide](context/design.md). -The io_uring selector owns a dedicated thread, retains buffers until terminal -completions, and drains cancellation during shutdown. Connection setup and -readiness waits still use async-io. Kernel support is probed when the selector -is first needed; failures are returned without silently falling back. Operation -pooling, registered buffers, UDP, arbitrary descriptor APIs, and a local -`!Send` task executor remain future work. +The io\_uring selector owns a dedicated thread, retains buffers until terminal completions, and drains cancellation during shutdown. Connection setup and readiness waits still use async-io. Kernel support is probed when the selector is first needed; failures are returned without silently falling back. Operation pooling, registered buffers, UDP, arbitrary descriptor APIs, and a local `!Send` task executor remain future work. -The former coroutine implementation is preserved on branch `coroutine`, at -commit `b520f3d`. Its native sources, stack allocation, nested synchronous -`wait` and task transfer are absent from the future executor. +The former coroutine implementation is preserved on branch `coroutine`, at commit `b520f3d`. Its native sources, stack allocation, nested synchronous `wait` and task transfer are absent from the future executor. ## Releasing -Prepare a release with `cargo bake cargo:version:patch` (or `minor`, `major`, -or `bump --version X.Y.Z`), then run `cargo bake cargo:release` and open a -pull request. After review and merge, GitHub Actions publishes the release -when the configured `crates-io` environment approves it, then creates or updates -the matching GitHub Release from `releases.md`. See the shared -[Releasing skill](https://github.com/socketry/socketry-project-rust/blob/main/context/releasing.md) -for the standard release process. +Prepare a release with `cargo bake cargo:version:patch` (or `minor`, `major`, or `bump --version X.Y.Z`), then run `cargo bake cargo:release` and open a pull request. After review and merge, GitHub Actions publishes the release when the configured `crates-io` environment approves it, then creates or updates the matching GitHub Release from `releases.md`. See the shared [Releasing skill](https://github.com/socketry/socketry-project-rust/blob/main/context/releasing.md) for the standard release process. ## Releases + See [releases.md](releases.md) for the full release history. +### v0.1.4 + +- Adopt `socketry-project` 0.3.7 for shared project tasks and Markdown normalization. +- Require the aggregate test and coverage result for pull request merges. + ### v0.1.3 - Use the shared Socketry Project tasks and update agent context setup guidance. ### v0.1.2 -- Use the shared `socketry-project` Releasing skill for the standard release - process and remove references to the duplicate Bake Cargo publishing context. - -### v0.1.1 +- Use the shared `socketry-project` Releasing skill for the standard release process and remove references to the duplicate Bake Cargo publishing context. -- Create or update GitHub Releases after successful crates.io publication. -- Move implementation and design guidance into the package's public context. -- Add Bake Agent Context tasks to the repository's development workspace. ## Contributing @@ -142,12 +103,8 @@ Please open an issue or pull request on [GitHub](https://github.com/socketry/soc ### Agent Context -Run `cargo bake agent:context:install` to install shared context and skills. -Read `.agents/context/index.md` to find relevant guides, follow `agents.md` if -present, and apply skills under `.agents/skills/`. See the [Agent Context guide] -for guidance on organizing package context and repository-only instructions. +Run `cargo bake agent:context:install` to install shared context and skills. Read `.agents/context/index.md` to find relevant guides, follow `agents.md` if present, and apply skills under `.agents/skills/`. The installer preserves repository-owned `agents.md`; it does not create or regenerate that file. [Agent Context guide]: https://github.com/socketry/bake-agent-context-rust/blob/main/context/agent-context.md -The crate publishes [implementation](context/implementation.md) and -[design](context/design.md) guides for its architecture and development. +The crate publishes [implementation](context/implementation.md) and [design](context/design.md) guides for its architecture and development. diff --git a/releases.md b/releases.md index f3559c2..3b8105e 100644 --- a/releases.md +++ b/releases.md @@ -1,13 +1,17 @@ # Releases +## v0.1.4 + +- Adopt `socketry-project` 0.3.7 for shared project tasks and Markdown normalization. +- Require the aggregate test and coverage result for pull request merges. + ## v0.1.3 - Use the shared Socketry Project tasks and update agent context setup guidance. ## v0.1.2 -- Use the shared `socketry-project` Releasing skill for the standard release - process and remove references to the duplicate Bake Cargo publishing context. +- Use the shared `socketry-project` Releasing skill for the standard release process and remove references to the duplicate Bake Cargo publishing context. ## v0.1.1