diff --git a/Cargo.lock b/Cargo.lock index f6c30dc67bf..062dab10a9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2250,7 +2250,6 @@ dependencies = [ "toyos-net-wire", "toyos-pci-claim", "toyos-tco", - "toyos-transport", "toyos-virtio", ] @@ -4332,9 +4331,9 @@ dependencies = [ name = "toyos" version = "0.18.0" dependencies = [ + "loom", "rustc-std-workspace-core", "toyos-abi", - "toyos-transport", ] [[package]] @@ -4378,8 +4377,8 @@ version = "0.1.0" name = "toyos-blockring" version = "0.1.0" dependencies = [ + "toyos", "toyos-blockhold", - "toyos-transport", ] [[package]] @@ -4684,7 +4683,6 @@ dependencies = [ "toyos-net-tcp", "toyos-net-udp", "toyos-net-wire", - "toyos-transport", ] [[package]] @@ -4877,14 +4875,6 @@ dependencies = [ "toyos-abi", ] -[[package]] -name = "toyos-transport" -version = "0.1.0" -dependencies = [ - "loom", - "rustc-std-workspace-core", -] - [[package]] name = "toyos-tsc" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index ff9c1da3fc7..4d1094a0b94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,7 +90,6 @@ members = [ "sdk/std/copies", "sdk/osrelease", "sdk/toyos", - "sdk/transport", "sdk/window", "system/acpiserver", "system/acpiserver/aml", diff --git a/build/build.rs b/build/build.rs index d5b4bdeb482..1d2202bc9ce 100644 --- a/build/build.rs +++ b/build/build.rs @@ -2724,16 +2724,19 @@ mod tests { "test-actuators", ]; - /// Features of a host workspace member that choose a build's world rather + /// Features of a host-tested member that choose a build's world rather /// than revert a decision: loom's atomics, a crate's `std` and its default /// set, the toolchain's `core` and `rustc-dep-of-std`, libc's `std-runtime`, /// the signer's `sign`, `flaws`, which compiles what a simulator's - /// negative gates select at run time, and `toyos-crypto`'s `portable`, - /// `wpa2` and `rustls-provider`, which `--ci host` tests as forms of its own. + /// negative gates select at run time, `toyos-crypto`'s `portable`, `wpa2` + /// and `rustls-provider`, which `--ci host` tests as forms of its own, and + /// of the guest libraries, `toyos-font`'s `ttf`, its TrueType rendering, + /// and `toyos-pci-claim`'s `host-grant`, which netstack's host tests build. const NOT_A_CONTROL: &[&str] = &[ "core", "default", "flaws", + "host-grant", "loom", "portable", "rustc-dep-of-std", @@ -2741,6 +2744,7 @@ mod tests { "sign", "std", "std-runtime", + "ttf", "wpa2", ]; @@ -2799,7 +2803,8 @@ mod tests { /// Every negative control the tree declares: every feature of the kernel's /// manifest but its builds and [`KERNEL_CARRIES`], and every feature of every - /// host workspace member's but [`NOT_A_CONTROL`]. + /// host workspace member's and guest library's (`hostws::GUEST_LIBRARIES`, + /// which a host tests too) but [`NOT_A_CONTROL`]. /// /// **Every manifest, and no list of the crates that hold a model.** A model /// of memory orderings, of the process table's interleavings, of a simulated @@ -2809,7 +2814,10 @@ mod tests { /// Each file's own comment beside a name carries the argument for it. fn declared_model_controls(root: &Path) -> BTreeSet { let mut out = BTreeSet::new(); - for dir in std::iter::once(crate::ci::KERNEL_DIR.to_string()).chain(crate::hostws::host_members(root)) { + let host_tested = crate::hostws::host_members(root) + .into_iter() + .chain(crate::hostws::GUEST_LIBRARIES.map(String::from)); + for dir in std::iter::once(crate::ci::KERNEL_DIR.to_string()).chain(host_tested) { let path = root.join(&dir).join("Cargo.toml"); let text = fs::read_to_string(&path) .unwrap_or_else(|e| panic!("Failed to read {}: {e}", path.display())); diff --git a/build/ci.rs b/build/ci.rs index abf46e1279a..516dd54eee9 100644 --- a/build/ci.rs +++ b/build/ci.rs @@ -450,7 +450,8 @@ pub(crate) const SDK_DIR: &str = "sdk/toyos"; const KERNEL_LOOM: &str = "kernel-loom"; const KERNEL_SIM: &str = "kernel-sim"; const BLOCKRING: &str = "toyos-blockring"; -const TRANSPORT: &str = "toyos-transport"; +/// The SDK package, whose `transport` module carries its own controls. +const SDK: &str = "toyos"; /// `toyos-crypto`, tested and linted in each of [`CRYPTO_FORMS`] rather than /// with the features the workspace's other members unify onto it. @@ -692,20 +693,20 @@ pub(crate) const CONTROLS: &[Control] = &[ red(BLOCKRING, "mutate-no-reissue-after-loss", None, &[ Fails("model::tests::what_a_flush_calls_durable_is_on_the_medium"), ]), - red(TRANSPORT, "publish-relaxed", Some("loom"), &[ + red(SDK, "transport-publish-relaxed", Some("transport_loom"), &[ Fails("a_published_entry_is_read_whole"), Fails("a_published_byte_is_read_as_written"), ]), - red(TRANSPORT, "head-relaxed", Some("loom"), &[Fails("a_published_byte_is_read_as_written")]), - red(TRANSPORT, "no-clamp", Some("loom"), &[Fails( + red(SDK, "transport-head-relaxed", Some("transport_loom"), &[Fails("a_published_byte_is_read_as_written")]), + red(SDK, "transport-no-clamp", Some("transport_loom"), &[Fails( "a_hostile_producer_yields_entries_or_a_violation", )]), - red(TRANSPORT, "no-clamp", None, &[Fails( - "stream::tests::a_hostile_producer_hands_the_consumer_at_most_the_ring", + red(SDK, "transport-no-clamp", None, &[Fails( + "transport::stream::tests::a_hostile_producer_hands_the_consumer_at_most_the_ring", )]), // The stream's lost wake: a sleeper whose exchange read the waker's clear // looks at a cursor from before it, and sleeps with nobody to ring. - red(TRANSPORT, "want-relaxed", Some("loom"), &[ + red(SDK, "transport-want-relaxed", Some("transport_loom"), &[ Fails("a_consumer_that_looked_again_is_rung_for_bytes_it_missed"), Fails("a_producer_that_looked_again_is_rung_for_room_it_missed"), Fails("two_sleepers_each_wake_when_room_is_released_first"), @@ -721,24 +722,24 @@ pub(crate) const CONTROLS: &[Control] = &[ // A session's lost wakes: a reader and a writer on one doorbell, a take // not passed on, a registration that neither arms nor rings, and a reset // a look that arms does not see. - red(TRANSPORT, "one-doorbell", Some("loom"), &[ + red(SDK, "transport-one-doorbell", Some("transport_loom"), &[ Fails("two_sleepers_each_wake_when_room_is_released_first"), Fails("two_sleepers_each_wake_when_data_is_published_first"), ]), - red(TRANSPORT, "no-pass-on", Some("loom"), &[ + red(SDK, "transport-no-pass-on", Some("transport_loom"), &[ Fails("a_poll_beside_a_blocking_read_is_answered_for_the_bytes_it_left"), Fails("a_poll_beside_a_blocking_read_is_answered_for_the_end_it_left"), Fails("a_poll_beside_a_blocking_write_is_answered_for_the_room_it_left"), ]), - red(TRANSPORT, "register-no-arm", Some("loom"), REGISTERED), - red(TRANSPORT, "register-no-ring", Some("loom"), REGISTERED), - red(TRANSPORT, "register-neither", Some("loom"), REGISTERED), - red(TRANSPORT, "head-only", Some("loom"), &[ + red(SDK, "transport-register-no-arm", Some("transport_loom"), REGISTERED), + red(SDK, "transport-register-no-ring", Some("transport_loom"), REGISTERED), + red(SDK, "transport-register-neither", Some("transport_loom"), REGISTERED), + red(SDK, "transport-head-only", Some("transport_loom"), &[ Fails("a_source_registered_over_a_full_ring_after_a_reset_gets_its_first_event"), Fails("a_source_registered_over_a_full_ring_as_a_reset_arrives_gets_its_first_event"), ]), - red(TRANSPORT, "end-keeps-inflight", None, &[Fails( - "inflight::tests::an_end_answers_every_tag_once_and_a_late_completion_nothing", + red(SDK, "transport-end-keeps-inflight", None, &[Fails( + "transport::inflight::tests::an_end_answers_every_tag_once_and_a_late_completion_nothing", )]), ]; diff --git a/build/clippy.rs b/build/clippy.rs index adaf9beefdf..409571a1153 100644 --- a/build/clippy.rs +++ b/build/clippy.rs @@ -166,10 +166,10 @@ const SHAPES: &[Shape] = &[ before: &["-p", CRYPTO, "--all-targets"], after: &["$ADOPTED", "-D", "warnings"], }, - // `--no-deps` because its dependency `toyos` is a guest crate no shape - // lints. Its own target directory because `--no-deps` checks without clippy - // the host crates the workspace shapes lint, which `toyos-abi`'s shape - // below says costs every run. + // `--no-deps` because its dependency `toyos` is a guest crate, linted by + // the shapes that build it. Its own target directory because `--no-deps` + // checks without clippy the host crates the workspace shapes lint, which + // `toyos-abi`'s shape below says costs every run. Shape { before: &[ "-p", @@ -208,6 +208,13 @@ const SHAPES: &[Shape] = &[ ], after: &["$ADOPTED", "-D", "warnings"], }, + // `toyos`'s tests, its transport's loom models among them: the workspace + // shapes exclude it and lint its library only as their members' + // dependency. + Shape { + before: &["-p", "toyos", "--all-targets"], + after: &["$ADOPTED", "-D", "warnings"], + }, // Its own target directory: cargo keeps one check of a unit, so a unit // linted under two sets of lints is checked again by each, every run, and // so is every crate depending on it. diff --git a/build/hostws.rs b/build/hostws.rs index ce1b943a9c9..4b8fba024cd 100644 --- a/build/hostws.rs +++ b/build/hostws.rs @@ -84,10 +84,9 @@ pub(crate) fn rel(root: &Path, path: &Path) -> String { } /// The library members built for a guest: the SDK, its window, font and -/// file-picker clients and the two shared crates that link the SDK, whose -/// `toyos` is not clippy-clean on a host. Their tests run in a step of their -/// own ([`guest_libraries`]). -const GUEST_LIBRARIES: [&str; 6] = [ +/// file-picker clients and the two shared crates that link the SDK. Their +/// tests run in a step of their own ([`guest_libraries`]). +pub(crate) const GUEST_LIBRARIES: [&str; 6] = [ crate::ci::SDK_DIR, "sdk/window", "sdk/font", diff --git a/build/sdkversion.rs b/build/sdkversion.rs index 3c9c0a6868b..80ad33b2004 100644 --- a/build/sdkversion.rs +++ b/build/sdkversion.rs @@ -20,9 +20,11 @@ pub struct Crate { } /// The published crates, in the order a publisher must take them. +/// +/// A crate joins only once crates.io holds it with `publish.yml` its trusted +/// publisher: the token that workflow publishes with cannot create a crate. pub const PUBLISHED: &[Crate] = &[ Crate { name: "toyos-abi", dir: "sdk/abi" }, - Crate { name: "toyos-transport", dir: "sdk/transport" }, Crate { name: "toyos-keymap", dir: "sdk/keymap" }, Crate { name: "toyos-font", dir: "sdk/font" }, Crate { name: "toyos", dir: "sdk/toyos" }, diff --git a/build/sysroot.rs b/build/sysroot.rs index 6ec7b64ce54..cf29263b7a9 100644 --- a/build/sysroot.rs +++ b/build/sysroot.rs @@ -65,13 +65,12 @@ use crate::toolchain::{self, host_triple, GuestTarget, Owner, Role, GUEST_TARGET use whole_toolchain::{whole, Whole}; /// The per-worktree sources that end up inside a sysroot: std links the -/// `toyos-abi`, `toyos` and `toyos-transport` [`std_patch`] hands it and -/// compiles its ToyOS backend from `sdk/std`, and `libtoyos_c.a` is `sdk/libc` -/// with `toyos-elf` and `toyos-osrelease`. -pub const SYSROOT_SOURCES: [&str; 8] = [ +/// `toyos-abi` and `toyos` [`std_patch`] hands it and compiles its ToyOS +/// backend from `sdk/std`, and `libtoyos_c.a` is `sdk/libc` with `toyos-elf` +/// and `toyos-osrelease`. +pub const SYSROOT_SOURCES: [&str; 7] = [ "sdk/abi/src", "sdk/toyos/src", - "sdk/transport/src", "sdk/std", "lib/elf/src", "sdk/osrelease/src", @@ -81,10 +80,9 @@ pub const SYSROOT_SOURCES: [&str; 8] = [ /// Their manifests, and the lockfile and cargo configuration libc is built /// under: the features, versions and flags of the same build. -pub(crate) const SYSROOT_MANIFESTS: [&str; 8] = [ +pub(crate) const SYSROOT_MANIFESTS: [&str; 7] = [ "sdk/abi/Cargo.toml", "sdk/toyos/Cargo.toml", - "sdk/transport/Cargo.toml", "lib/elf/Cargo.toml", "sdk/osrelease/Cargo.toml", "sdk/libc/Cargo.toml", @@ -95,7 +93,7 @@ pub(crate) const SYSROOT_MANIFESTS: [&str; 8] = [ /// Of [`SYSROOT_MANIFESTS`], the crates std names by version, which std's /// lockfile resolves with the fork's own through [`std_patch`]: what of a /// worktree can move a freestanding target's dependency versions. -const STD_MANIFESTS: [&str; 3] = ["sdk/abi/Cargo.toml", "sdk/toyos/Cargo.toml", "sdk/transport/Cargo.toml"]; +const STD_MANIFESTS: [&str; 2] = ["sdk/abi/Cargo.toml", "sdk/toyos/Cargo.toml"]; /// One of the crates std names by version, in a worktree. pub(crate) struct StdCrate { @@ -1276,7 +1274,7 @@ pub(crate) mod tests { /// The manifests [`std_crates`] reads, in a worktree at `root` that has /// no other source: what a bootstrap run is handed std's patch from. pub(crate) fn std_manifests(root: &Path) { - for (manifest, name) in STD_MANIFESTS.into_iter().zip(["toyos-abi", "toyos", "toyos-transport"]) { + for (manifest, name) in STD_MANIFESTS.into_iter().zip(["toyos-abi", "toyos"]) { write(&root.join(manifest), &format!("[package]\nname = \"{name}\"\n")); } } @@ -1317,8 +1315,6 @@ pub(crate) mod tests { format!("patch.crates-io.toyos-abi.path=\"{at}/sdk/abi\""), "--config".to_string(), format!("patch.crates-io.toyos.path=\"{at}/sdk/toyos\""), - "--config".to_string(), - format!("patch.crates-io.toyos-transport.path=\"{at}/sdk/transport\""), ], ); for name in ["work tree", "work\"tree", "work\\tree", "work\ttree"] { diff --git a/build/toolchain.rs b/build/toolchain.rs index 25907e1aa84..60a7db0ce6a 100644 --- a/build/toolchain.rs +++ b/build/toolchain.rs @@ -825,9 +825,8 @@ mod tests { let registry = |name: &str, version: &str| { format!("/home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/{name}-{version}/src/lib.rs") }; - let rest = ours("sdk/transport"); let built_from = |abi: &str, toyos: &str| { - fs::write(built.join("dist/libstd.d"), format!("{}/dist/libstd.rlib: {abi} {toyos} {rest}\n", built.display())) + fs::write(built.join("dist/libstd.d"), format!("{}/dist/libstd.rlib: {abi} {toyos}\n", built.display())) .unwrap(); std::panic::catch_unwind(|| assert_std_built_from(&root, &built)) .err() diff --git a/issues/a-compile-fail-case-names-an-error-code-rustdoc-never-reads.md b/issues/a-compile-fail-case-names-an-error-code-rustdoc-never-reads.md index 1effee81e3a..890e89e56d0 100644 --- a/issues/a-compile-fail-case-names-an-error-code-rustdoc-never-reads.md +++ b/issues/a-compile-fail-case-names-an-error-code-rustdoc-never-reads.md @@ -18,7 +18,7 @@ crate's doc-tests green: | case | names | `cargo test -p --doc` under `E0308` | |---|---|---| | `toyos_bootmap::DirectMapEnd` | `E0603` | EXIT=0 | -| `toyos_transport::Place`, three blocks | `E0080` | EXIT=0 | +| `toyos::transport::Place`, three blocks | `E0080` | EXIT=0 | What does hold a case to its reason in this tree is the block beside it that compiles (`system/netstack/wire/src/lib.rs`, `mod compile_fail`). `Place`'s three diff --git a/issues/a-crate-new-to-the-published-set-reds-every-publish-until-someone-puts-it-up-by-hand.md b/issues/a-crate-new-to-the-published-set-reds-every-publish-until-someone-puts-it-up-by-hand.md index 9f35678eda2..82e8c56305a 100644 --- a/issues/a-crate-new-to-the-published-set-reds-every-publish-until-someone-puts-it-up-by-hand.md +++ b/issues/a-crate-new-to-the-published-set-reds-every-publish-until-someone-puts-it-up-by-hand.md @@ -37,13 +37,14 @@ Only an account can create a crate, and only then can it name `publish.yml` the crate's trusted publisher. Nothing before a landing tells that a crate it adds to `PUBLISHED` is not on crates.io. -Owner: the owner's crates.io account for the first versions; `build/sdkversion.rs` +Owner: the orchestrator, for the runs after landing; `build/sdkversion.rs` for the rule. -`toyos-untrusted` has since been removed from the tree and from `PUBLISHED`, -so `toyos-transport` is the one crate still owed. +Neither crate is owed any more: #965 removed `toyos-untrusted`, and the +transport is now the `toyos` SDK's `transport` module, so `PUBLISHED` names +only crates the crates.io index holds (checked 2026-10-21). Its doc comment +says a crate joins only once crates.io holds it with `publish.yml` its trusted +publisher. Nothing yet shows a green run. -**Exit**: `toyos-transport` is on crates.io, naming `publish.yml` its trusted -publisher; a `publish` run at main's tip and the next -nightly's `release` are green; and `PUBLISHED`'s doc comment says a crate joins -it only once crates.io holds it under that publisher. +**Exit**: a `publish` run at main's tip after the transport merge lands, and +the next nightly's `release`, are green. diff --git a/issues/loom-arc-panics-in-its-drop-while-a-failed-model-unwinds.md b/issues/loom-arc-panics-in-its-drop-while-a-failed-model-unwinds.md index e7eae7246fc..2070d45b302 100644 --- a/issues/loom-arc-panics-in-its-drop-while-a-failed-model-unwinds.md +++ b/issues/loom-arc-panics-in-its-drop-while-a-failed-model-unwinds.md @@ -25,7 +25,8 @@ a_consumer_that_looked_again_is_rung_for_bytes_it_missed --exact` kills the test binary with SIGABRT, `panic in a destructor during cleanup`, through ` as Drop>::drop` and `rt::arc::Arc::ref_dec`. On std's `Arc` the same run prints the test's `FAILED` line and loom's -`deadlock; threads = ...`. +`deadlock; threads = ...`. That model is now `sdk/toyos/tests/transport_loom.rs`, +its control `transport-want-relaxed` of `toyos`. **Owner**: the orchestrator. Nobody holds it. diff --git a/issues/netstack-moves-every-byte-through-two-kernel-pipes.md b/issues/netstack-moves-every-byte-through-two-kernel-pipes.md index cfd14216169..495d571287b 100644 --- a/issues/netstack-moves-every-byte-through-two-kernel-pipes.md +++ b/issues/netstack-moves-every-byte-through-two-kernel-pipes.md @@ -52,7 +52,7 @@ The design and its roast are PR #863's body. `DeviceMemory`, and a receiver judges a lent region through `fstat` before it maps it. - Stage 2, done (PR #878): a byte ring with untrusted cursors and want words - in `toyos-transport` (`sdk/transport`), host-tested and modelled + in the SDK's `toyos::transport` (`sdk/toyos/src/transport`), host-tested and modelled in loom. - Stage 3, done (PR #905): a TCP stream, connected or accepted, is one region whose two rings are [tcp]'s buffers and the connection it was opened @@ -112,7 +112,7 @@ The design and its roast are PR #863's body. over a full send ring, each answers a refusal only netstack judges that lands meanwhile; each is red when the look reads `pending` before it arms, red when netstack exchanges the wants before it stores `pending`, and red - under `want-relaxed`, and the sender's is red when netstack rings only + under `transport-want-relaxed`, and the sender's is red when netstack rings only `rx_data`. In the other, [udp]'s queue starts full and a datagram leaves it as its own step, interleaved with netstack's looks, so netstack can find a published record while the queue is full and hold it; a sender that @@ -120,7 +120,7 @@ The design and its roast are PR #863's body. every record sent, in order, none refused. It is red when netstack refuses a record at the full queue, red when it rings `tx_room` on the connection, red when after the leave it waits for the client's next ring without - looking at the send ring again, and red under `want-relaxed`. + looking at the send ring again, and red under `transport-want-relaxed`. Owner: the orchestrator, who stages it. diff --git a/issues/the-transport-heads-orderings-have-no-oracle.md b/issues/the-transport-heads-orderings-have-no-oracle.md index 0bd386bd3b2..87bee5c4bd9 100644 --- a/issues/the-transport-heads-orderings-have-no-oracle.md +++ b/issues/the-transport-heads-orderings-have-no-oracle.md @@ -8,11 +8,11 @@ opened: 2026-09-27 A consumer stores its head `Release` after it has loaded the entries below it, and a producer loads the head `Acquire` before it writes over them -(`sdk/transport/src/queue.rs`, `Consumer::release` and `Producer::space`). +(`sdk/toyos/src/transport/queue.rs`, `Consumer::release` and `Producer::space`). No test reds if either is `Relaxed`: what the pair forbids is load buffering — a consumer's load of an entry reading the producer's later overwrite — which loom does not model, and every other test runs both ends on one thread. The -tail's edge has its control (`publish-relaxed`); the head's has none. +tail's edge has its control (`transport-publish-relaxed`); the head's has none. **Exit condition.** A control that relaxes the head's two orderings, and a model or a run on a weakly ordered CPU that goes red under it. diff --git a/issues/there-is-no-wifi.md b/issues/there-is-no-wifi.md index e81279f9410..be9ef18c6d7 100644 --- a/issues/there-is-no-wifi.md +++ b/issues/there-is-no-wifi.md @@ -110,7 +110,7 @@ and the ACPI read-out in `s1-r2/` alone. **S2, the link hop's cost on the T14.** A scout, never landing: two -processes over `toyos-transport`, one entry per 1,514-byte frame, batches of +processes over `toyos::transport`, one entry per 1,514-byte frame, batches of 1, 8 and 32, both directions, each side copying each frame once. Owner: the orchestrator. Depends on: nothing. Exit: cycles per frame and frames a second on one CPU, reported in P4's design round. Beside them stands the @@ -181,7 +181,7 @@ stage 3 of `issues/the-lan-is-not-yet-production-grade.md`, whole. - **Exit:** that stage's exit, a driver killed and restarted with the stack's connections intact (held in a guest) and an unlisted but matching NIC up with no configuration change; and with it: - - the `link` protocol on `toyos-transport`, adding no transport primitive; + - the `link` protocol on `toyos::transport`, adding no transport primitive; - `Card` gone; - the supervisor binding drivers from a declared match table by vendor, device and class; diff --git a/lib/blockring/Cargo.toml b/lib/blockring/Cargo.toml index f0f65fa972c..47a9b5efbdb 100644 --- a/lib/blockring/Cargo.toml +++ b/lib/blockring/Cargo.toml @@ -1,6 +1,6 @@ # A member of the host workspace (root `Cargo.toml`). What lives here is the # block protocol between a block service and its client — the shared session -# page's layout, where its two `toyos-transport` rings are, the words a request +# page's layout, where its two `toyos::transport` rings are, the words a request # and a completion are, the control frames a session is opened with, and every # decision either end makes about what a completion means — with nothing that # touches a device, a handle or a mapping. `system/diskserver` serves it and its @@ -9,7 +9,7 @@ # Its defects are orders — a completion racing a crash, a flush racing a reset, # a reconnect racing a reissue — so `src/model.rs` enumerates every ordering of # a scripted client against a server, a device and a crash; the rings' -# publication is `toyos-transport`'s, and checked under loom there. +# publication is `toyos::transport`'s, and checked under loom there. [package] name = "toyos-blockring" @@ -39,8 +39,8 @@ mutate-no-reissue-after-loss = [] # the server half's bookkeeping, decided where the kernel's block layer decides # it today. toyos-blockhold = { path = "../blockhold" } -# The rings on the session page. -toyos-transport = { path = "../../sdk/transport" } +# The rings on the session page: `toyos::transport`. +toyos = { path = "../../sdk/toyos" } [lints.rust] warnings = "deny" diff --git a/lib/blockring/src/client.rs b/lib/blockring/src/client.rs index 73c0485f2a1..8d65fec4894 100644 --- a/lib/blockring/src/client.rs +++ b/lib/blockring/src/client.rs @@ -36,7 +36,7 @@ use alloc::collections::{BTreeMap, VecDeque}; use alloc::vec::Vec; -use toyos_transport::{Inflight, Run, Violation}; +use toyos::transport::{Inflight, Run, Violation}; use crate::entry::{Completion, Op, Request, Status}; use crate::layout::DEPTH; diff --git a/lib/blockring/src/entry.rs b/lib/blockring/src/entry.rs index af9ee697dc1..67d29627b45 100644 --- a/lib/blockring/src/entry.rs +++ b/lib/blockring/src/entry.rs @@ -4,7 +4,7 @@ //! request is bounded here against the arena and the partition, and a word //! this protocol does not define is a refusal, never a default. -use toyos_transport::Run; +use toyos::transport::Run; use crate::layout::{ARENA, CQE_WORDS, MAX_REQUEST_BLOCKS, SQE_WORDS}; diff --git a/lib/blockring/src/layout.rs b/lib/blockring/src/layout.rs index 56f65d70163..97e65bd8c64 100644 --- a/lib/blockring/src/layout.rs +++ b/lib/blockring/src/layout.rs @@ -3,7 +3,7 @@ //! The four ring indices sit on cache lines of their own, so the client's //! stores to its two and the server's to its two never share a line. -use toyos_transport::{Consumer, Geometry, Place, Producer, Word}; +use toyos::transport::{Consumer, Geometry, Place, Producer, Word}; /// A session's whole region: the one size shared memory comes in. pub const SESSION_BYTES: usize = Geometry::BYTES as usize; diff --git a/lib/blockring/src/lib.rs b/lib/blockring/src/lib.rs index f4b3c78b85d..31a75f5355e 100644 --- a/lib/blockring/src/lib.rs +++ b/lib/blockring/src/lib.rs @@ -33,10 +33,10 @@ //! handed the sessions opened on them ([`disk`]); data moves between a //! driver's device and the client's region alone. //! -//! Pure: `alloc`, `toyos-blockhold` and `toyos-transport`, no `unsafe`. The +//! Pure: `alloc`, `toyos-blockhold` and `toyos::transport`, no `unsafe`. The //! ends that map the page — a disk driver (`lib/blockserve`) and the client //! (`system/diskserver`'s library) — hand it the page as words -//! ([`toyos_transport::Word`]) and act on what it answers. +//! ([`toyos::transport::Word`]) and act on what it answers. #![cfg_attr(not(test), no_std)] #![forbid(unsafe_code)] @@ -54,7 +54,7 @@ pub mod wire; mod model; pub use entry::{Completion, Op, Request, Status}; -pub use toyos_transport::Run; +pub use toyos::transport::Run; pub use layout::{BLOCK_BYTES, DEPTH, MAX_REQUEST_BLOCKS, SESSION_BYTES}; /// The name a block service is served under. A connection opens only what its diff --git a/lib/blockring/src/model.rs b/lib/blockring/src/model.rs index 3f431ee1daa..1553703623a 100644 --- a/lib/blockring/src/model.rs +++ b/lib/blockring/src/model.rs @@ -35,7 +35,7 @@ use core::sync::atomic::Ordering; use std::collections::HashSet; use toyos_blockhold::Holds; -use toyos_transport::{Consumer, Place, Producer, Violation, Word}; +use toyos::transport::{Consumer, Place, Producer, Violation, Word}; use crate::client::{Client, Outcome, Ticket, MAX_ATTEMPTS}; use crate::entry::{Completion, Op, Request}; diff --git a/lib/blockring/src/wire.rs b/lib/blockring/src/wire.rs index 9c9a0363d63..e907af7777f 100644 --- a/lib/blockring/src/wire.rs +++ b/lib/blockring/src/wire.rs @@ -18,7 +18,7 @@ //! server's ends and its answer together: nothing else makes the ends, and an //! answer is otherwise only read off the wire ([`Opened::decode`]). -use toyos_transport::Word; +use toyos::transport::Word; use crate::layout::{self, ServerRings, RING_WORDS}; diff --git a/rust b/rust index e1c5e93d342..11e31a52778 160000 --- a/rust +++ b/rust @@ -1 +1 @@ -Subproject commit e1c5e93d34236ee3e57d510920f94c0256c647eb +Subproject commit 11e31a52778f875f3e021db96d35a45fb0f22466 diff --git a/sdk/libc/Cargo.lock b/sdk/libc/Cargo.lock index f3b061534dd..2c169500537 100644 --- a/sdk/libc/Cargo.lock +++ b/sdk/libc/Cargo.lock @@ -30,7 +30,6 @@ name = "toyos" version = "0.18.0" dependencies = [ "toyos-abi", - "toyos-transport", ] [[package]] @@ -56,10 +55,6 @@ dependencies = [ name = "toyos-osrelease" version = "0.1.0" -[[package]] -name = "toyos-transport" -version = "0.1.0" - [[package]] name = "windows-link" version = "0.2.1" diff --git a/sdk/toyos/Cargo.toml b/sdk/toyos/Cargo.toml index ca94156bad5..54256318da2 100644 --- a/sdk/toyos/Cargo.toml +++ b/sdk/toyos/Cargo.toml @@ -3,11 +3,11 @@ name = "toyos" version = "0.18.0" # never bumped; crates.io's is assigned on publish (build/sdkversion.rs) edition = "2021" license = "MIT OR Apache-2.0" -description = "The ToyOS userland SDK: typed handles, IPC, ports, namespaces, surfaces and net." +description = "The ToyOS userland SDK: typed handles, IPC, ports, namespaces, surfaces, net and the transport its sessions run over." repository = "https://github.com/ToyOSOrg/ToyOS" [features] -rustc-dep-of-std = ["core", "toyos-abi/rustc-dep-of-std", "toyos-transport/rustc-dep-of-std"] +rustc-dep-of-std = ["core", "toyos-abi/rustc-dep-of-std"] # The negative control for the log ring's publication. It makes the store that # publishes a record `Relaxed`, and `kernel-loom`'s `log_ring` model must red: # @@ -21,6 +21,49 @@ log-ring-publish-relaxed = [] # swapped. log-ring-tail-relaxed = [] log-ring-loads-swapped = [] +# The transport's controls (`src/transport`), declared, never enabled by any +# build that ships: each reverts one decision so the model that refuses it is +# shown able to red at all. `build/ci.rs`'s `CONTROLS` runs each and demands +# the named test's own FAILED line; `tests/transport_loom.rs` holds the +# publication edge, the wake handshake, a hostile peer and a session's three +# waits. +# +# A producer publishes its tail `Relaxed`, so a consumer can see the index +# before the entry's words, or a stream's tail before its bytes. +transport-publish-relaxed = [] +# A stream's consumer stores its head `Relaxed` and its producer loads it +# `Relaxed`, so the producer can copy into a byte the consumer is still +# copying out. +transport-head-relaxed = [] +# A peer's cursor is believed however far it claims to be, so a hostile +# producer hands the consumer more entries, or bytes, than the ring holds. +transport-no-clamp = [] +# A stream's waker clears the peer's want `Relaxed`, so a sleeper whose +# exchange reads the clear need not see the cursor stored before it, and +# sleeps over bytes or room nobody rings for. +transport-want-relaxed = [] +# `Inflight::end` answers nothing and keeps every tag in flight, so a session's +# requests outlive it unanswered and a late completion answers one. +transport-end-keeps-inflight = [] +# A session's server rings a writer waiting for room on the connection, the +# doorbell a reader waits on, so either can take the other's wake. +transport-one-doorbell = [] +# A thread that took a direction's doorbell leaves it with another waiter +# counted and passes nothing on, so that waiter sleeps over what the take +# announced. +transport-no-pass-on = [] +# A registration looks without arming, so a direction that becomes ready after +# its look rings nobody. +transport-register-no-arm = [] +# A registration arms and looks but rings nobody for a direction it found +# ready, so its first event never comes. +transport-register-no-ring = [] +# Both of the two above. +transport-register-neither = ["transport-register-no-arm", "transport-register-no-ring"] +# A look that arms the send direction, and the server's ring back of it, read +# only the room and not a failure, so a writer over a full ring sleeps through +# a reset. +transport-head-only = [] [lints.rust] # The guest's target: its stream sinks are compiled only for it. @@ -29,4 +72,6 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(target_os, values("toyos") [dependencies] core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" } toyos-abi = { path = "../abi", default-features = false } -toyos-transport = { path = "../transport" } + +[dev-dependencies] +loom = "0.7" diff --git a/sdk/toyos/src/lib.rs b/sdk/toyos/src/lib.rs index 785701781e9..3166a3a3d0b 100644 --- a/sdk/toyos/src/lib.rs +++ b/sdk/toyos/src/lib.rs @@ -1,7 +1,8 @@ //! ToyOS userland SDK. //! //! Typed handles, IPC framing, ports and namespaces, shared memory, and -//! ergonomic wrappers over the kernel ABI defined in `toyos-abi`. +//! ergonomic wrappers over the kernel ABI defined in `toyos-abi`, and the +//! [`transport`] client/server sessions run over. // `not(test)` so this crate's own decisions can be unit-tested on the host. // [`net::hangup`] is a total function from a kernel word to a [`net::NetError`] @@ -33,6 +34,7 @@ pub mod spawn; pub mod syscap; pub mod sync; pub mod system; +pub mod transport; pub mod volatile; pub use ipc::Connection; diff --git a/sdk/toyos/src/log/stdio.rs b/sdk/toyos/src/log/stdio.rs index b8f3df6ddf6..a71f5863e39 100644 --- a/sdk/toyos/src/log/stdio.rs +++ b/sdk/toyos/src/log/stdio.rs @@ -470,9 +470,9 @@ mod tests { use std::sync::{Arc, Mutex}; let held = Arc::new(Held::new(Stream::Err)); let got = Arc::new(Mutex::new(Vec::new())); - let writers: Vec<_> = [b'a', b'b'] - .into_iter() - .map(|tag| { + let writers: Vec<_> = b"ab" + .iter() + .map(|&tag| { let (held, got) = (Arc::clone(&held), Arc::clone(&got)); std::thread::spawn(move || { for _ in 0..200 { diff --git a/sdk/toyos/src/net/stream.rs b/sdk/toyos/src/net/stream.rs index 2ee3c2fbe4b..be1129cf542 100644 --- a/sdk/toyos/src/net/stream.rs +++ b/sdk/toyos/src/net/stream.rs @@ -26,18 +26,18 @@ use core::sync::atomic::{AtomicU32, Ordering}; use toyos_abi::syscall::{self, SyscallError}; use toyos_abi::RawHandle; -use toyos_transport::session::{self, Bells, Doorbell, Layout, Mode, Polled, Receiving, Refused as Answer, Sending, Took, Waited}; -use toyos_transport::stream::Place; -use toyos_transport::Span; use crate::ipc::Connection; use crate::shm::SharedMemory; use crate::sync::Lock; +use crate::transport::session::{self, Bells, Doorbell, Layout, Mode, Polled, Receiving, Refused as Answer, Sending, Took, Waited}; +use crate::transport::stream::Place; +use crate::transport::Span; use crate::OwnedHandle; use super::{MsgType, NetError, NetstackConn, TcpAcceptRequest, TcpAcceptResponse, TcpConnectRequest, TcpConnectResponse, TcpSocketId}; -pub use toyos_transport::session::Way; +pub use crate::transport::session::Way; // ---- the region ---- diff --git a/sdk/toyos/src/relocate/tests.rs b/sdk/toyos/src/relocate/tests.rs index f217e6372a0..f4a9a3e6d9a 100644 --- a/sdk/toyos/src/relocate/tests.rs +++ b/sdk/toyos/src/relocate/tests.rs @@ -271,7 +271,7 @@ fn a_header_this_does_not_know_is_refused() { fn image_with(edit: impl Fn(usize, &mut [u8])) -> Result { let header = Header::parse(&X86_64[..HEADER_SIZE]).unwrap(); let mut phdrs = X86_64[header.phoff..header.phoff + header.phlen].to_vec(); - for (i, entry) in phdrs.chunks_exact_mut(PHDR_SIZE).enumerate() { + for (i, entry) in phdrs.as_chunks_mut::().0.iter_mut().enumerate() { edit(i, entry); } Image::new(header, &phdrs).map(|image| image.header_vaddr) diff --git a/sdk/transport/src/arena.rs b/sdk/toyos/src/transport/arena.rs similarity index 99% rename from sdk/transport/src/arena.rs rename to sdk/toyos/src/transport/arena.rs index 63686e3247a..d76e9a6b260 100644 --- a/sdk/transport/src/arena.rs +++ b/sdk/toyos/src/transport/arena.rs @@ -6,7 +6,7 @@ use core::fmt; -use crate::Violation; +use super::Violation; /// Bytes `offset..offset + len` of the region. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] diff --git a/sdk/transport/src/inflight.rs b/sdk/toyos/src/transport/inflight.rs similarity index 97% rename from sdk/transport/src/inflight.rs rename to sdk/toyos/src/transport/inflight.rs index b692dce6097..1f1042009fe 100644 --- a/sdk/transport/src/inflight.rs +++ b/sdk/toyos/src/transport/inflight.rs @@ -12,7 +12,7 @@ use core::fmt; -use crate::Violation; +use super::Violation; #[derive(Clone, PartialEq, Eq, Hash)] struct Slot { @@ -76,9 +76,9 @@ impl Inflight { /// it carried, and none of them is answered again. pub fn end(&mut self, mut each: impl FnMut(u32, T)) { for (index, slot) in (0u32..).zip(self.slots.iter_mut()) { - #[cfg(not(feature = "end-keeps-inflight"))] + #[cfg(not(feature = "transport-end-keeps-inflight"))] let value = slot.value.take(); - #[cfg(feature = "end-keeps-inflight")] + #[cfg(feature = "transport-end-keeps-inflight")] let value = None; if let Some(value) = value { each(Self::tag(slot.seq, index), value); diff --git a/sdk/transport/src/lib.rs b/sdk/toyos/src/transport/mod.rs similarity index 76% rename from sdk/transport/src/lib.rs rename to sdk/toyos/src/transport/mod.rs index 7ba61e6677e..3fcab3ffa2f 100644 --- a/sdk/transport/src/lib.rs +++ b/sdk/toyos/src/transport/mod.rs @@ -5,24 +5,25 @@ //! [`Producer`]/[`Consumer`] queues of fixed-size entries and an arena of //! [`Run`]s, or byte [`stream`]s and the wants that say when to ring, two of //! which with their `end`s are a duplex [`session`]. A protocol says what the -//! entries and bytes mean; this crate says only what is safe. +//! entries and bytes mean; this module says only what is safe. //! -//! **Nothing here holds the region.** An adapter hands every call the `N` -//! [`Word`]s an end was made over, and copies bytes itself, through a run's -//! [`Span`], once: no reference to the peer's bytes is formed. +//! **Nothing here holds the region or makes a syscall.** An adapter, such as +//! [`crate::net::stream`], hands every call the `N` [`Word`]s an end was made +//! over, and copies bytes itself, through a run's [`Span`], once: no reference +//! to the peer's bytes is formed. //! //! **What the peer writes is untrusted until decoded.** An entry comes out as -//! the peer's words, for the protocol's decoder to bound; a peer's cursor is bounded against the ring before it -//! is believed, and the variant of [`Violation`] names what it claimed; a run -//! is bounded against the [`Geometry`], and a tag against the [`Inflight`] -//! table. An end keeps its own cursor locally and only stores the shared one, -//! so nothing the peer writes moves it, and a cursor the peer moves backwards -//! within bounds costs only the peer. +//! the peer's words, for the protocol's decoder to bound; a peer's cursor is +//! bounded against the ring before it is believed, and the variant of +//! [`Violation`] names what it claimed; a run is bounded against the +//! [`Geometry`], and a tag against the [`Inflight`] table. An end keeps its +//! own cursor locally and only stores the shared one, so nothing the peer +//! writes moves it, and a cursor the peer moves backwards within bounds costs +//! only the peer. //! //! **A restart is seen only as a hang-up**, and [`Inflight::end`] answers every //! tag that was in flight, once; a tag from before it answers nothing. -#![cfg_attr(not(test), no_std)] #![forbid(unsafe_code)] #![cfg_attr(not(test), forbid(clippy::arithmetic_side_effects, clippy::unwrap_used, clippy::expect_used, clippy::panic))] #![cfg_attr(not(test), deny(clippy::indexing_slicing, clippy::as_conversions))] @@ -60,9 +61,9 @@ impl Word for AtomicU32 { } /// How an end stores the cursor that hands what it wrote to the peer. -#[cfg(not(feature = "publish-relaxed"))] +#[cfg(not(feature = "transport-publish-relaxed"))] const PUBLISH: Ordering = Ordering::Release; -#[cfg(feature = "publish-relaxed")] +#[cfg(feature = "transport-publish-relaxed")] const PUBLISH: Ordering = Ordering::Relaxed; /// Word `at` of an end's words, and the one index into them: every `at` an end @@ -75,7 +76,7 @@ fn word(page: &[W; N], at: usize) -> &W { /// A distance the peer's cursor claims, believed only up to `cap`. fn clamp(claimed: u32, cap: u32, broken: Violation) -> Result { - let cap = if cfg!(feature = "no-clamp") { u32::MAX } else { cap }; + let cap = if cfg!(feature = "transport-no-clamp") { u32::MAX } else { cap }; if claimed > cap { Err(broken) } else { Ok(claimed) } } diff --git a/sdk/transport/src/queue.rs b/sdk/toyos/src/transport/queue.rs similarity index 98% rename from sdk/transport/src/queue.rs rename to sdk/toyos/src/transport/queue.rs index a9b3afed68a..3bfda212b8c 100644 --- a/sdk/transport/src/queue.rs +++ b/sdk/toyos/src/transport/queue.rs @@ -8,31 +8,31 @@ use core::sync::atomic::Ordering; -use crate::{clamp, word, Violation, Word, PUBLISH}; +use super::{clamp, word, Violation, Word, PUBLISH}; /// Where a queue of `E`-word entries, `D` deep, is among `N` words: the word /// its consumer stores its head in, the word its producer stores its tail in, /// and its first entry. /// /// ``` -/// use toyos_transport::Place; +/// use toyos::transport::Place; /// const QUEUE: Place<2, 4, 10> = Place::new::<0, 1, 2>(); /// ``` /// /// A place with a word at `N` or past it does not compile: /// /// ```compile_fail,E0080 -/// use toyos_transport::Place; +/// use toyos::transport::Place; /// const QUEUE: Place<2, 4, 10> = Place::new::<0, 1, 3>(); /// ``` /// /// ```compile_fail,E0080 -/// use toyos_transport::Place; +/// use toyos::transport::Place; /// const QUEUE: Place<2, 4, 10> = Place::new::<10, 1, 2>(); /// ``` /// /// ```compile_fail,E0080 -/// use toyos_transport::Place; +/// use toyos::transport::Place; /// const QUEUE: Place<2, 4, 10> = Place::new::<0, 10, 2>(); /// ``` #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] diff --git a/sdk/transport/src/session.rs b/sdk/toyos/src/transport/session.rs similarity index 97% rename from sdk/transport/src/session.rs rename to sdk/toyos/src/transport/session.rs index 99a64e0bac6..f717d57af3f 100644 --- a/sdk/transport/src/session.rs +++ b/sdk/toyos/src/transport/session.rs @@ -1,5 +1,6 @@ -//! A duplex byte stream between a client and a server: two [`stream`] rings -//! and two `end` words among `N` words, and two doorbells the server rings. +//! A duplex byte stream between a client and a server: two +//! [`stream`](super::stream) rings and two `end` words among `N` words, and +//! two doorbells the server rings. //! //! **Nothing here is any protocol's.** The receive ring carries the server's //! bytes to the client and the send ring the client's to the server. The @@ -32,8 +33,8 @@ use core::sync::atomic::Ordering; -use crate::stream::{Consumer, Place, Producer, WAKE}; -use crate::{word, Span, Violation, Word}; +use super::stream::{Consumer, Place, Producer, WAKE}; +use super::{word, Span, Violation, Word}; /// An `end` while its ring runs. pub const OPEN: u32 = 0; @@ -67,7 +68,7 @@ pub enum Doorbell { pub const fn doorbell(way: Way) -> Doorbell { match way { Way::Receive => Doorbell::Connection, - Way::Send if cfg!(feature = "one-doorbell") => Doorbell::Connection, + Way::Send if cfg!(feature = "transport-one-doorbell") => Doorbell::Connection, Way::Send => Doorbell::Bell, } } @@ -336,7 +337,7 @@ impl Half for Sending { fn arm(&mut self, page: &[W; N]) -> Result, Violation> { let room = self.ring.arm(page)?; - Ok(self.seen(page, room, !cfg!(feature = "head-only"))) + Ok(self.seen(page, room, !cfg!(feature = "transport-head-only"))) } } @@ -364,7 +365,7 @@ fn leave, W: Word>(half: &mut H, page: &[W; N], count let waiters = half.waiters(); *waiters = waiters.saturating_sub(1); } - let pass_on = !cfg!(feature = "no-pass-on") && took && *half.waiters() > 0; + let pass_on = !cfg!(feature = "transport-no-pass-on") && took && *half.waiters() > 0; pass_on && matches!(half.arm(page), Ok(Some(_))) } @@ -587,7 +588,7 @@ where /// What a registration found of one half. fn registered, W: Word>(half: &mut H, page: &[W; N]) -> Result { - let seen = if cfg!(feature = "register-no-arm") { half.look(page)? } else { half.arm(page)? }; + let seen = if cfg!(feature = "transport-register-no-arm") { half.look(page)? } else { half.arm(page)? }; Ok(seen.is_some()) } @@ -613,7 +614,7 @@ where if let Some(lock) = send { ready |= lock.with(|half| registered(half, page))?; } - if ready && !cfg!(feature = "register-no-ring") { + if ready && !cfg!(feature = "transport-register-no-ring") { bells.ring(); } Ok(()) @@ -732,7 +733,7 @@ impl Server { /// for 0, and the doorbells of those that were set. pub fn rung(&mut self, page: &[W; N]) -> Result { let receive = self.rx.ring_back(page, self.ended != OPEN)?; - let send = self.tx.ring_back(page, fatal(self.ended) && !cfg!(feature = "head-only"))?; + let send = self.tx.ring_back(page, fatal(self.ended) && !cfg!(feature = "transport-head-only"))?; Ok(Rings::of(Way::Receive, receive).and(Rings::of(Way::Send, send))) } } diff --git a/sdk/transport/src/stream.rs b/sdk/toyos/src/transport/stream.rs similarity index 97% rename from sdk/transport/src/stream.rs rename to sdk/toyos/src/transport/stream.rs index 193a2b09ac0..50d9b554093 100644 --- a/sdk/transport/src/stream.rs +++ b/sdk/toyos/src/transport/stream.rs @@ -29,19 +29,19 @@ use core::sync::atomic::Ordering; -use crate::{clamp, word, Span, Violation, Word, PUBLISH}; +use super::{clamp, word, Span, Violation, Word, PUBLISH}; /// A sleeper's exchange: what the waker published before clearing the want is /// seen by the look that follows. const ARM: Ordering = Ordering::Acquire; -#[cfg(not(feature = "want-relaxed"))] -pub(crate) const WAKE: Ordering = Ordering::Release; -#[cfg(feature = "want-relaxed")] -pub(crate) const WAKE: Ordering = Ordering::Relaxed; +#[cfg(not(feature = "transport-want-relaxed"))] +pub(super) const WAKE: Ordering = Ordering::Release; +#[cfg(feature = "transport-want-relaxed")] +pub(super) const WAKE: Ordering = Ordering::Relaxed; /// The head's edge: a producer that loads the head copies into a byte only /// after the consumer's copy out of it, which the consumer's store carries. -const GIVE_BACK: Ordering = if cfg!(feature = "head-relaxed") { Ordering::Relaxed } else { Ordering::Release }; -const TAKE_BACK: Ordering = if cfg!(feature = "head-relaxed") { Ordering::Relaxed } else { Ordering::Acquire }; +const GIVE_BACK: Ordering = if cfg!(feature = "transport-head-relaxed") { Ordering::Relaxed } else { Ordering::Release }; +const TAKE_BACK: Ordering = if cfg!(feature = "transport-head-relaxed") { Ordering::Relaxed } else { Ordering::Acquire }; /// `n` as an offset into the region; `Place::new` holds `usize` to every `u32`. #[allow(clippy::as_conversions)] @@ -53,14 +53,14 @@ const fn bytes(n: u32) -> usize { /// of its first byte. /// /// ``` -/// use toyos_transport::stream::Place; +/// use toyos::transport::stream::Place; /// const RING: Place<4096, 64> = Place::new::<0, 16, 32, 48, 4096>(); /// ``` /// /// A place with a word at `N` or past it does not compile: /// /// ```compile_fail,E0080 -/// use toyos_transport::stream::Place; +/// use toyos::transport::stream::Place; /// const RING: Place<4096, 64> = Place::new::<0, 16, 32, 64, 4096>(); /// ``` #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -94,7 +94,7 @@ impl Place { } /// Its four words: head, tail and the two wants. - pub(crate) const fn words(&self) -> [usize; 4] { + pub(super) const fn words(&self) -> [usize; 4] { [self.head, self.tail, self.data, self.room] } diff --git a/sdk/transport/tests/loom.rs b/sdk/toyos/tests/transport_loom.rs similarity index 94% rename from sdk/transport/tests/loom.rs rename to sdk/toyos/tests/transport_loom.rs index b898f11f0ba..3cb90967482 100644 --- a/sdk/transport/tests/loom.rs +++ b/sdk/toyos/tests/transport_loom.rs @@ -2,16 +2,16 @@ //! runs both ends on one thread can reach. //! //! - **Publication**: a consumer that sees a tail sees every word of the -//! entries below it. `publish-relaxed` takes the edge away. +//! entries below it. `transport-publish-relaxed` takes the edge away. //! - **A hostile peer** leaves the honest end with entries, nothing, or a -//! named violation, and never more entries than the ring holds. `no-clamp` -//! believes the peer. +//! named violation, and never more entries than the ring holds. +//! `transport-no-clamp` believes the peer. //! - **A session's three waits**, each over `session`'s own sequences: a //! reader and a writer asleep at once, a poll beside a blocking call of one //! direction, and a source registered while its direction is ready. Each //! case names the controls that red it. //! -//! cargo test -p toyos-transport --features --test loom +//! cargo test -p toyos --features --test transport_loom use core::sync::atomic::Ordering; use std::rc::Rc; @@ -19,7 +19,7 @@ use std::rc::Rc; use loom::cell::Cell; use loom::sync::atomic::AtomicU32; use loom::sync::{Arc, Notify}; -use toyos_transport::{stream, Consumer, Place, Producer, Violation, Word}; +use toyos::transport::{stream, Consumer, Place, Producer, Violation, Word}; /// A loom atomic as a region word: the trait is this crate's and the type is /// loom's, so the two meet through a wrapper. @@ -207,7 +207,7 @@ fn receive(s: &Stream, rx: &mut stream::Consumer) -> u32 { /// A consumer that polls reads each byte as the producer wrote it, through a /// ring that wraps under it: its copy out follows the producer's copy in, and /// the producer's next copy into that byte follows its copy out. -/// `publish-relaxed` takes the first edge away. +/// `transport-publish-relaxed` takes the first edge away. #[test] fn a_published_byte_is_read_as_written() { loom::model(|| { @@ -234,8 +234,9 @@ fn a_published_byte_is_read_as_written() { } /// A consumer sleeps whenever the look after arming finds the ring empty, and -/// the byte published around its sleep still reaches it. `want-relaxed` lets -/// it miss the tail stored before the want it read cleared. +/// the byte published around its sleep still reaches it. +/// `transport-want-relaxed` lets it miss the tail stored before the want it +/// read cleared. #[test] fn a_consumer_that_looked_again_is_rung_for_bytes_it_missed() { loom::model(|| { @@ -253,8 +254,9 @@ fn a_consumer_that_looked_again_is_rung_for_bytes_it_missed() { } /// A producer sleeps whenever the look after arming finds the ring full, and -/// the room released around its sleep still reaches it. `want-relaxed` lets -/// it miss the head stored before the want it read cleared. +/// the room released around its sleep still reaches it. +/// `transport-want-relaxed` lets it miss the head stored before the want it +/// read cleared. #[test] fn a_producer_that_looked_again_is_rung_for_room_it_missed() { loom::model(|| { @@ -289,7 +291,7 @@ fn a_producer_that_looked_again_is_rung_for_room_it_missed() { use loom::sync::atomic::AtomicBool; use loom::sync::{Condvar, Mutex}; -use toyos_transport::session::{ +use toyos::transport::session::{ self, Bells, Doorbell, Layout, Lock, Mode, Polled, Receiving, Refused, Rings, Sending, Server, Took, Waited, Way, }; @@ -527,8 +529,8 @@ enum First { /// A reader asleep over an empty receive ring and a writer asleep over a full /// send ring, while the server releases room and publishes data in either /// order: each wakes, as each waits on a doorbell of its own. -/// `one-doorbell` lets the reader take the writer's wake, or the writer the -/// reader's. +/// `transport-one-doorbell` lets the reader take the writer's wake, or the +/// writer the reader's. fn two_sleepers(first: First) { bounded(move || { let (s, mut server) = Session::new(); @@ -579,9 +581,10 @@ enum Left { } /// A blocking call and a `poll` of one direction, the server making the -/// direction ready once and then answering each ring: the poll is answered -/// with the direction ready, whichever of them took the doorbell. -/// `no-pass-on` lets the blocking call take the wake the poll sleeps over. +/// direction ready once and then answering each ring: the poll is answered with +/// the direction ready, whichever of them took the doorbell. +/// `transport-no-pass-on` lets the blocking call take the wake the poll sleeps +/// over. fn same_direction(left: Left) { bounded(move || { let (s, mut server) = Session::new(); @@ -661,13 +664,13 @@ enum Ready { } /// A source registered while its direction is ready, or racing the server -/// making it so, gets its first event: its doorbell is watched, as mio's -/// select watches it, and each event leads to the nonblocking operation, -/// until that finds the direction ready. `register-no-arm`, -/// `register-no-ring` and `register-neither` take the registration's arm or -/// ring or both away; `want-relaxed` lets a racing server's clear go unseen; -/// `head-only` makes the reset no readiness to the arm's look or the ring -/// back. +/// making it so, gets its first event: its doorbell is watched, as mio's select +/// watches it, and each event leads to the nonblocking operation, until that +/// finds the direction ready. `transport-register-no-arm`, +/// `transport-register-no-ring` and `transport-register-neither` take the +/// registration's arm or ring or both away; `transport-want-relaxed` lets a +/// racing server's clear go unseen; `transport-head-only` makes the reset no +/// readiness to the arm's look or the ring back. fn registered(ready: Ready, racing: bool) { bounded(move || { let (s, mut server) = Session::new(); diff --git a/sdk/transport/Cargo.toml b/sdk/transport/Cargo.toml deleted file mode 100644 index 8bfca088cb6..00000000000 --- a/sdk/transport/Cargo.toml +++ /dev/null @@ -1,69 +0,0 @@ -# A member of the host workspace (root `Cargo.toml`). The one transport every -# client/server session runs over: the rings, the byte streams and their wants, -# the arena's runs and the tag table, as decisions over words an adapter hands -# in. Nothing here maps, copies or blocks. -# -# Its defects are orders, lost wakes and hostile peers, so `tests/loom.rs` -# holds the publication edge, the wake handshake, a hostile peer and a -# session's three waits. - -[package] -name = "toyos-transport" -version = "0.1.0" # never bumped; crates.io's is assigned on publish (build/sdkversion.rs) -edition = "2021" -description = "The transport every client/server session runs over: entry rings, byte streams, sessions and their wakes, an arena of runs and a tag table, decided over words an adapter hands in." -license = "MIT OR Apache-2.0" -repository = "https://github.com/ToyOSOrg/ToyOS" - -[features] -# std links it through `toyos`, whose stream end is a session's client. -rustc-dep-of-std = ["core"] -# Declared, never enabled by any build that ships: each reverts one decision so -# the model that refuses it is shown able to red at all. `build/ci.rs`'s -# `CONTROLS` runs each and demands the named test's own FAILED line. -# -# A producer publishes its tail `Relaxed`, so a consumer can see the index -# before the entry's words, or a stream's tail before its bytes. -publish-relaxed = [] -# A stream's consumer stores its head `Relaxed` and its producer loads it -# `Relaxed`, so the producer can copy into a byte the consumer is still -# copying out. -head-relaxed = [] -# A peer's cursor is believed however far it claims to be, so a hostile -# producer hands the consumer more entries, or bytes, than the ring holds. -no-clamp = [] -# A stream's waker clears the peer's want `Relaxed`, so a sleeper whose -# exchange reads the clear need not see the cursor stored before it, and -# sleeps over bytes or room nobody rings for. -want-relaxed = [] -# `Inflight::end` answers nothing and keeps every tag in flight, so a session's -# requests outlive it unanswered and a late completion answers one. -end-keeps-inflight = [] -# A session's server rings a writer waiting for room on the connection, the -# doorbell a reader waits on, so either can take the other's wake. -one-doorbell = [] -# A thread that took a direction's doorbell leaves it with another waiter -# counted and passes nothing on, so that waiter sleeps over what the take -# announced. -no-pass-on = [] -# A registration looks without arming, so a direction that becomes ready after -# its look rings nobody. -register-no-arm = [] -# A registration arms and looks but rings nobody for a direction it found -# ready, so its first event never comes. -register-no-ring = [] -# Both of the two above. -register-neither = ["register-no-arm", "register-no-ring"] -# A look that arms the send direction, and the server's ring back of it, read -# only the room and not a failure, so a writer over a full ring sleeps through -# a reset. -head-only = [] - -[dependencies] -core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" } - -[dev-dependencies] -loom = "0.7" - -[lints.rust] -warnings = "deny" diff --git a/system/netstack/Cargo.toml b/system/netstack/Cargo.toml index 70b3399235e..6fb3a3a3b0a 100644 --- a/system/netstack/Cargo.toml +++ b/system/netstack/Cargo.toml @@ -24,7 +24,6 @@ toyos-net-shard = { path = "shard" } toyos-net-tcp = { path = "shard/tcp" } toyos-net-udp = { path = "udp" } toyos-net-wire = { path = "wire" } -toyos-transport = { path = "../../sdk/transport" } [dev-dependencies] toyos-pci-claim = { path = "../../lib/pci-claim", features = ["host-grant"] } diff --git a/system/netstack/node/Cargo.toml b/system/netstack/node/Cargo.toml index bbb656a926a..bfb126b45cd 100644 --- a/system/netstack/node/Cargo.toml +++ b/system/netstack/node/Cargo.toml @@ -20,7 +20,6 @@ toyos-net-udp = { path = "../udp" } toyos-net-wire = { path = "../wire" } # The stream protocol's one declaration, the region's layout, and the session's server end. toyos = { path = "../../../sdk/toyos" } -toyos-transport = { path = "../../../sdk/transport" } [dev-dependencies] # The outside reader of every frame the node emits: a published Ethernet, ARP, diff --git a/system/netstack/node/src/streams.rs b/system/netstack/node/src/streams.rs index ffee6ac25de..fa795965020 100644 --- a/system/netstack/node/src/streams.rs +++ b/system/netstack/node/src/streams.rs @@ -27,8 +27,8 @@ use toyos::net::stream::{Server, Words, LAYOUT, NODELAY, RECEIVE_BYTES, RESET, T use toyos_net_shard::ConnectError; use toyos_net_tcp::{ConnId, Endpoint, Error, Failure, Lent, Options, State, Status}; use toyos_net_wire::{Instant, Port}; -use toyos_transport::session::Rings; -use toyos_transport::Violation; +use toyos::transport::session::Rings; +use toyos::transport::Violation; use crate::lease::Stack; use crate::Node; diff --git a/system/netstack/node/tests/common/client.rs b/system/netstack/node/tests/common/client.rs index a952dbcba0d..0580297df7c 100644 --- a/system/netstack/node/tests/common/client.rs +++ b/system/netstack/node/tests/common/client.rs @@ -1,4 +1,4 @@ -//! A client's end of a stream: `toyos_transport::session`'s own client sequences over a region +//! A client's end of a stream: `toyos::transport::session`'s own client sequences over a region //! on the heap, laid out as `toyos::net::stream` lays one out, and the session the node is lent. //! No call waits: a doorbell is a count, the client's ring of netstack a flag the test reads to //! say which streams a pass is owed. @@ -10,7 +10,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; use toyos::net::stream::{Words, LAYOUT, NODELAY, RECEIVE_BYTES, RECEIVE_RING, REGION_BYTES, SEND_BYTES, SEND_RING}; use toyos_net_node::Session; use toyos_net_tcp::Lent; -use toyos_transport::session::{self, Bells, Doorbell, Lock, Mode, Receiving, Refused, Rings, Sending, Took, Waited}; +use toyos::transport::session::{self, Bells, Doorbell, Lock, Mode, Receiving, Refused, Rings, Sending, Took, Waited}; /// The region and its doorbells, which the client and the node share. pub struct Region { diff --git a/system/netstack/node/tests/listeners.rs b/system/netstack/node/tests/listeners.rs index 8861d4cf57c..d5c640a37df 100644 --- a/system/netstack/node/tests/listeners.rs +++ b/system/netstack/node/tests/listeners.rs @@ -1,5 +1,5 @@ //! Listeners and the node's places, against peers the test scripts, at 192.0.2.2 unless a test -//! puts one elsewhere, and clients that are `toyos_transport::session`'s own client end over a +//! puts one elsewhere, and clients that are `toyos::transport::session`'s own client end over a //! region on the heap (`common::client`). No scenario ids: the specifications' listening //! scenarios are `toyos-net-tcp`'s; these are what the node does between [tcp]'s queues, an //! owner's wakes and its accepts. @@ -24,8 +24,8 @@ use common::client::Client; use common::{arp, batch, terms, Segment, Wire, A, BUDGET, ELSEWHERE, MAC, MAC_R, R}; use etherparse::{ArpOperation, LinkSlice, NetSlice, PacketBuilder, SlicedPacket, TcpOptionElement, TransportSlice}; use toyos_net_node::{AcceptRefused, Accepted, ConnectRefused, ListenRefused, ListenerId, Node, Refused, StreamEvent, StreamId, Wake, WriteRefusal}; -use toyos_transport::session::Refused as Read; -use toyos_transport::Violation; +use toyos::transport::session::Refused as Read; +use toyos::transport::Violation; use toyos_net_tcp::{limits, Counter, Endpoint}; use toyos_net_wire::{Instant, Port}; diff --git a/system/netstack/node/tests/streams.rs b/system/netstack/node/tests/streams.rs index 7e26bf820f2..037066ec350 100644 --- a/system/netstack/node/tests/streams.rs +++ b/system/netstack/node/tests/streams.rs @@ -1,5 +1,5 @@ //! Streams on the node, against peers the test scripts, at 192.0.2.2:80 unless a test puts one -//! elsewhere, and clients that are `toyos_transport::session`'s own client end over a region on +//! elsewhere, and clients that are `toyos::transport::session`'s own client end over a region on //! the heap (`common::client`). No scenario ids: the specifications' TCP scenarios are //! `toyos-net-tcp`'s; these are what the node does between [tcp] and a client's region. //! @@ -24,8 +24,8 @@ use toyos_net_node::{ConnectRefused, Node, StreamEvent, StreamId}; use toyos_net_shard::ConnectError; use toyos_net_tcp::{Counter, Endpoint, Failure}; use toyos_net_wire::{Instant, Port}; -use toyos_transport::session::Refused; -use toyos_transport::Violation; +use toyos::transport::session::Refused; +use toyos::transport::Violation; const B: Ipv4Addr = Ipv4Addr::new(192, 0, 2, 2); /// A second peer's address, and its MAC. diff --git a/system/netstack/src/lent.rs b/system/netstack/src/lent.rs index b8419f9827d..0c0c71ea666 100644 --- a/system/netstack/src/lent.rs +++ b/system/netstack/src/lent.rs @@ -21,7 +21,7 @@ use toyos::shm::{self, SharedMemory}; use toyos::{Connection, Pipe}; use toyos_net_node::Session; use toyos_net_tcp::Lent; -use toyos_transport::session::Rings; +use toyos::transport::session::Rings; /// The region's mapping. struct Mapping(SharedMemory); diff --git a/tests/guest/rust-tests/Cargo.lock b/tests/guest/rust-tests/Cargo.lock index 53fd760422a..070dae5d7c8 100644 --- a/tests/guest/rust-tests/Cargo.lock +++ b/tests/guest/rust-tests/Cargo.lock @@ -890,7 +890,6 @@ name = "toyos" version = "0.18.0" dependencies = [ "toyos-abi", - "toyos-transport", ] [[package]] @@ -994,10 +993,6 @@ dependencies = [ "toyos-abi", ] -[[package]] -name = "toyos-transport" -version = "0.1.0" - [[package]] name = "toyos-virtio" version = "0.1.0"