Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -316,11 +316,22 @@ jobs:
run: python3 scripts/ci/install-redis.py
- name: Pull MongoDB before the bounded fixture startup wait
run: docker pull mongo:8
- name: Install WASI 0.2 adapter runtime and C toolchain
run: |
rustup target add --toolchain nightly-2026-08-20 wasm32-wasip2
bash scripts/ci/install-wasmtime.sh
python3 scripts/ci/install-wasm-toolchain.py
- name: Required real-server protocol suites with the shared fixture contract
env:
POSTGRES_CONTAINER: ${{ job.services.postgres.id }}
MYSQL_CONTAINER: ${{ job.services.mysql.id }}
run: scripts/test-servers.py --ci-services run python3 scripts/ci/run-tests.py protocol
- name: Required WASI 0.2 real-server adapters
if: '!cancelled()'
env:
POSTGRES_CONTAINER: ${{ job.services.postgres.id }}
MYSQL_CONTAINER: ${{ job.services.mysql.id }}
run: scripts/test-servers.py --ci-services run python3 scripts/ci/run-tests.py protocol-wasi --target wasm32-wasip2 --real-servers
- name: Fixture logs and cleanup
if: always()
run: scripts/test-servers.py --ci-services stop
Expand Down
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

335 changes: 335 additions & 0 deletions LANE_REPORT.md

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions crates/turnloop-contract/src/bin/native_child.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ fn main() {
let args: Vec<_> = std::env::args_os().collect();
let mode = args.get(1).and_then(|s| s.to_str()).expect("fixture mode");
match mode {
#[cfg(any(
target_vendor = "apple",
target_os = "linux",
target_os = "android",
target_os = "freebsd"
))]
"services-no-spin" => {
turnloop_contract::native_surface::services_no_spin::<turnloop::backend::Platform>(
&args[0],
turnloop::Signal::Usr2,
);
println!("60 service timer expiries; no-spin bounds passed");
}
"exit" => std::process::exit(23),
"sleep" => std::thread::sleep(Duration::from_secs(60)),
"environment" => {
Expand Down
25 changes: 22 additions & 3 deletions crates/turnloop-contract/tests/native_surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
))]
use turnloop::*;

// A fan-out fixture must consume only its own SIGUSR1. Another fixture's send
// can release its workers before its own send, after they restore SIG_DFL.
static SIGNAL_FANOUT: std::sync::Mutex<()> = std::sync::Mutex::new(());

#[test]
fn local_echo_and_descriptor_ownership() {
let path = std::env::temp_dir().join(format!("tl-ipc-{}.sock", std::process::id()));
Expand All @@ -31,6 +35,7 @@ fn spawned_child_stdio_uses_the_driver() {
}
#[test]
fn signals_reach_four_loops_on_four_threads() {
let _guard = SIGNAL_FANOUT.lock().expect("serialize SIGUSR1 fixtures");
turnloop_contract::native_surface::signal_fanout::<backend::Platform>(Signal::Usr1, || {
// SAFETY: SIGUSR1 is subscribed by all four loops before the barrier opens.
assert_eq!(unsafe { libc::kill(libc::getpid(), libc::SIGUSR1) }, 0);
Expand All @@ -57,6 +62,7 @@ fn pending_signal_cannot_outlive_kqueue_unsubscribe() {

#[test]
fn signal_subscribe_raise_unsubscribe_stress() {
let _guard = SIGNAL_FANOUT.lock().expect("serialize SIGUSR1 fixtures");
// Each round proves delivery and Stopped/Closed on all four owning threads;
// the next round exercises restoration followed by fresh subscriptions.
for _ in 0..256 {
Expand All @@ -78,9 +84,22 @@ fn kills_live_child_and_grandchild_as_a_group() {
}
#[test]
fn registered_processes_and_signals_do_not_spin() {
turnloop_contract::native_surface::services_no_spin::<backend::Platform>(
std::ffi::OsStr::new(env!("CARGO_BIN_EXE_native_child")),
Signal::Usr2,
// Every owned child subscribes to process-wide SIGCHLD. Parallel tests
// exiting unrelated children notify this loop too, invalidating the quiet
// premise. A fresh process gives the unchanged contract its own dispatcher.
let output = std::process::Command::new(env!("CARGO_BIN_EXE_native_child"))
.arg("services-no-spin")
.output()
.expect("no-spin fixture");
assert!(
output.status.success(),
"no-spin fixture: {:?}; {}",
output.status,
String::from_utf8_lossy(&output.stderr)
);
assert_eq!(
output.stdout,
b"60 service timer expiries; no-spin bounds passed\n"
);
}
#[test]
Expand Down
3 changes: 3 additions & 0 deletions crates/turnloop-io/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ wasi-p3-experimental = ["turnloop/wasi-p3-experimental"]
[[test]]
name = "allocations"
harness = false

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "=0.61.2", features = ["Win32_NetworkManagement_Dns", "Win32_Foundation", "Win32_Networking_WinSock"] }
14 changes: 14 additions & 0 deletions crates/turnloop-io/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,17 @@ one absolute backend-clock deadline for a whole exchange.
Native TCP/pipes and WASI sockets use the identical generic stream code. Browser
raw sockets/listeners return Unsupported; browser HTTP uses host fetch. Windows
uses the production Backend implementation when integrated.

## Pools and DNS

`pool::Pool` schedules the existing protocol pool policies with one manager task,
retained waiters and real executor deadlines. A lease owns the connection across
awaits. Cancelling a waiter removes its request; returning an incomplete exchange
retires the transport. Pool replacement/end observes the backend's physical close
acknowledgement through `ExecutorHandle::close`.

`resolve` uses native blocking DNS or WASI 0.2 `ip-name-lookup`. `dns::query` returns
native SRV/TXT records through `ExecutorHandle::blocking`, keeping system resolver
calls off the loop thread. Native worker count/queue bounds remain those of the
host's turnloop configuration. WASI exposes no SRV/TXT capability; use resolved
seeds when the protocol needs those records.
Loading