4 Commits
Author SHA1 Message Date
Markk116 535f7bcc68 feat(serve): give connection actors a 256 KiB stack via smarm SpawnOpts
Connection actors were still spawned with a bare smarm::spawn(), which
gets the runtime's fixed 64 KiB default stack regardless of smarm
v0.6.0's RFC 019 SpawnOpts/stack_reserve work landing one crate down.
Any handler that leans on app code with real stack needs (DB drivers,
(de)compression, ...) blows the guard page and the connection just
dies with no response - reproduced with a CCC handler that decompresses
gzip on the identity-encoding path.

Add Config::conn_stack_reserve (default DEFAULT_CONN_STACK_RESERVE =
256 KiB) and thread it through listener_loop into a
smarm::spawn_with(SpawnOpts { stack_reserve: Some(_), .. }, ...) call
for every accepted connection. Existing Config { ..Config::new(addr) }
call sites (tests/integration.rs) pick up the new field automatically
via struct-update syntax; no call-site churn beyond that.

Bump to 0.2.2.
2026-08-08 22:39:37 +02:00
Claude (sandbox)andClaude b137c646b0 release: v0.2.1 — switch smarm to the pinned v0.6.0 git tag
RFC 019 lands upstream: per-actor stacks, park-path shrink, recycle zap,
SIGSEGV diagnostics, introspect surface. No urus code changes required —
E1 interleaved A/B on the box shows every ka cell within +0.3..+2.9% of
the v0.5.0 pin (t8-c4 close-mode control is bistable either side; see
smarm v0.6.0 release notes). Tag must exist upstream before this builds:
push smarm master + v0.6.0 first.
2026-08-08 22:17:32 +02:00
Markk116 792897d3e4 License under MIT
- LICENSE: MIT text
- Cargo.toml: license = "MIT"
2026-08-08 16:37:57 +02:00
Markk116 b77448191e release: v0.2.0 — switch smarm to the pinned v0.5.0 git tag
The gen_server API port itself already landed upstream (078072b, tracking
smarm HEAD efbc254 pre-git-dep). This just moves the dependency off the
local path checkout onto the git remote, pinned to the smarm v0.5.0
release tag (a03a7ca) rather than a floating branch HEAD.

Verified: 90 lib + 45 integration + 2 doc tests green, examples build,
under default and --all-features.
2026-08-08 11:50:51 +02:00
3 changed files with 46 additions and 6 deletions
+3 -2
View File
@@ -1,12 +1,13 @@
[package] [package]
name = "urus" name = "urus"
version = "0.1.0" version = "0.2.2"
edition = "2021" edition = "2021"
rust-version = "1.95" rust-version = "1.95"
description = "Cowboy/bandit-style HTTP library for the smarm actor runtime" description = "Cowboy/bandit-style HTTP library for the smarm actor runtime"
license = "MIT"
[dependencies] [dependencies]
smarm = { path = "../smarm" } smarm = { git = "https://git.kalsbeek.dev/Markk116/smarm", tag = "v0.6.0" }
httparse = "1.9" httparse = "1.9"
libc = "0.2" libc = "0.2"
sha1_smol = "1" sha1_smol = "1"
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Mark Kalsbeek
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 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.
+22 -4
View File
@@ -57,8 +57,19 @@ pub struct Config {
/// (one per CPU). Set this to a small fixed number in tests so multiple /// (one per CPU). Set this to a small fixed number in tests so multiple
/// concurrent test servers don't oversubscribe the host. /// concurrent test servers don't oversubscribe the host.
pub scheduler_threads: Option<usize>, pub scheduler_threads: Option<usize>,
/// Stack reserve (RFC 019 `smarm::SpawnOpts::stack_reserve`) given to
/// each per-connection actor. Request handlers routinely pull in
/// application code — DB drivers, (de)compression, templating — whose
/// stack needs comfortably exceed smarm's bare-actor default of 64 KiB
/// (the exact shape of bug this exists to head off; see smarm RFC 019).
/// Default: 256 KiB. The reserve is virtual/demand-paged, so raising it
/// costs address space, not RSS, until a handler actually uses it.
pub conn_stack_reserve: usize,
} }
/// Default per-connection actor stack reserve (see [`Config::conn_stack_reserve`]).
pub const DEFAULT_CONN_STACK_RESERVE: usize = 256 * 1024;
impl Config { impl Config {
pub fn new(addr: SocketAddr) -> Self { pub fn new(addr: SocketAddr) -> Self {
let pool = std::thread::available_parallelism() let pool = std::thread::available_parallelism()
@@ -78,6 +89,7 @@ impl Config {
max_frame_payload: 1024 * 1024, max_frame_payload: 1024 * 1024,
max_message_bytes: 4 * 1024 * 1024, max_message_bytes: 4 * 1024 * 1024,
scheduler_threads: None, scheduler_threads: None,
conn_stack_reserve: DEFAULT_CONN_STACK_RESERVE,
} }
} }
@@ -125,6 +137,7 @@ fn listener_loop(
listener: Arc<OwnedFd>, listener: Arc<OwnedFd>,
pipeline: Pipeline, pipeline: Pipeline,
limits: ConnLimits, limits: ConnLimits,
conn_stack_reserve: usize,
registry: GenServerRef<ConnRegistry>, registry: GenServerRef<ConnRegistry>,
shutdown: Arc<AtomicBool>, shutdown: Arc<AtomicBool>,
) { ) {
@@ -157,7 +170,11 @@ fn listener_loop(
let p = pipeline.clone(); let p = pipeline.clone();
let l = limits; let l = limits;
let r = registry.clone(); let r = registry.clone();
smarm::spawn(move || run_connection(client, p, l, r)); let opts = smarm::SpawnOpts {
stack_reserve: Some(conn_stack_reserve),
..smarm::SpawnOpts::default()
};
smarm::spawn_with(opts, move || run_connection(client, p, l, r));
} }
Err(e) if e.kind() == ErrorKind::WouldBlock => { Err(e) if e.kind() == ErrorKind::WouldBlock => {
// No pending connection. Park until the listener is // No pending connection. Park until the listener is
@@ -305,8 +322,9 @@ pub fn serve_with_shutdown(
listener_fds.push(Arc::new(dup)); listener_fds.push(Arc::new(dup));
} }
let limits = config.to_conn_limits(); let limits = config.to_conn_limits();
let drain_timeout = config.drain_timeout; let conn_stack_reserve = config.conn_stack_reserve;
let drain_timeout = config.drain_timeout;
let smarm_cfg = match config.scheduler_threads { let smarm_cfg = match config.scheduler_threads {
Some(n) => smarm::Config::exact(n), Some(n) => smarm::Config::exact(n),
@@ -327,7 +345,7 @@ pub fn serve_with_shutdown(
let sf = shutdown_flag.clone(); let sf = shutdown_flag.clone();
sup = sup.child(ChildSpec::new(Restart::Transient, move || { sup = sup.child(ChildSpec::new(Restart::Transient, move || {
println!("urus: listener {} starting", i); println!("urus: listener {} starting", i);
listener_loop(lfd.clone(), p.clone(), limits, r.clone(), sf.clone()); listener_loop(lfd.clone(), p.clone(), limits, conn_stack_reserve, r.clone(), sf.clone());
})); }));
} }
// Default intensity (3 per 5s) applies; a listener crash-looping // Default intensity (3 per 5s) applies; a listener crash-looping