From 234b75651fffca24a1528d2aefc8531f4c9f482a Mon Sep 17 00:00:00 2001 From: randogoth Date: Tue, 29 Sep 2026 09:59:45 +0300 Subject: [PATCH] fix: join the RNS loop thread before exit and keep the response slot per-request --- ...c54afd29ca3fe771bc27a64e989b395f01fc9ba0e9 | 1 + ...31f544453549589423759c216e35c31bee4c5413fc | 1 + ...0914488d3062fb0d19bcab66e0c6f68063b488a168 | 1 + ...996781692de3d681a1bfe32c48e1dad0ed37c4b3eb | 1 + cli/src/main.rs | 7 +++- core/shim/smolmail_rns.cpp | 35 ++++++++++++++++-- core/shim/smolmail_rns.h | 8 ++++ core/src/rns/ffi.rs | 10 +++++ core/src/rns/mod.rs | 30 ++++++++++++--- hashlist_store/index.dat | Bin 0 -> 5194 bytes hashlist_store/seg0.dat | Bin 0 -> 5600 bytes hashlist_store/seg1.dat | Bin 0 -> 2016 bytes known_store/index.dat | Bin 0 -> 33 bytes known_store/seg0.dat | Bin 0 -> 152 bytes path_store/index.dat | Bin 0 -> 33 bytes path_store/seg0.dat | Bin 0 -> 294 bytes transport_identity | 1 + 17 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 cache/23913df1956165db1fde57c54afd29ca3fe771bc27a64e989b395f01fc9ba0e9 create mode 100644 cache/4892c693602a28c2f4f30931f544453549589423759c216e35c31bee4c5413fc create mode 100644 cache/b053eb65854a98dc4037b10914488d3062fb0d19bcab66e0c6f68063b488a168 create mode 100644 cache/c3529808c5c5a39bba63a0996781692de3d681a1bfe32c48e1dad0ed37c4b3eb create mode 100644 hashlist_store/index.dat create mode 100644 hashlist_store/seg0.dat create mode 100644 hashlist_store/seg1.dat create mode 100644 known_store/index.dat create mode 100644 known_store/seg0.dat create mode 100644 path_store/index.dat create mode 100644 path_store/seg0.dat create mode 100644 transport_identity diff --git a/cache/23913df1956165db1fde57c54afd29ca3fe771bc27a64e989b395f01fc9ba0e9 b/cache/23913df1956165db1fde57c54afd29ca3fe771bc27a64e989b395f01fc9ba0e9 new file mode 100644 index 0000000..66bb60b --- /dev/null +++ b/cache/23913df1956165db1fde57c54afd29ca3fe771bc27a64e989b395f01fc9ba0e9 @@ -0,0 +1 @@ +{"raw":"0f0018d0b427deefc9e827d8f905811ec679055626d3978c15abe954935d2f3e0eb4b6ac55db9f0318110b4463818f60e91d7596c2a32b033f0b0e37f17ea7da2690bf092bf719e2bed7111127c107abe61985","sent_at":1.790665185e9} \ No newline at end of file diff --git a/cache/4892c693602a28c2f4f30931f544453549589423759c216e35c31bee4c5413fc b/cache/4892c693602a28c2f4f30931f544453549589423759c216e35c31bee4c5413fc new file mode 100644 index 0000000..5a98f93 --- /dev/null +++ b/cache/4892c693602a28c2f4f30931f544453549589423759c216e35c31bee4c5413fc @@ -0,0 +1 @@ +{"raw":"0f007dcb759264a15f6ba66c335a2f3e8ace05739b67970ea95221dfb2c4e87b395bd124fae0f8c71e0f5a26f28c4c393f7695e2434e223e5d152b14a6b689e9c448b03fb6f2cab9e8fafa73efba7d6df5e58b","sent_at":1.790666247e9} \ No newline at end of file diff --git a/cache/b053eb65854a98dc4037b10914488d3062fb0d19bcab66e0c6f68063b488a168 b/cache/b053eb65854a98dc4037b10914488d3062fb0d19bcab66e0c6f68063b488a168 new file mode 100644 index 0000000..e84c8cf --- /dev/null +++ b/cache/b053eb65854a98dc4037b10914488d3062fb0d19bcab66e0c6f68063b488a168 @@ -0,0 +1 @@ +{"raw":"0f001bac2103482a7558d16836454bee96a4059bc6f761475deb9d31eb16c966e95aeaded51b951f7b4e71be6f9ce139bce6fd0de49ff495d48717b3fc4f99a76063d9fa0892f40ea53f5f1ed430ac6804de55","sent_at":1.790665991e9} \ No newline at end of file diff --git a/cache/c3529808c5c5a39bba63a0996781692de3d681a1bfe32c48e1dad0ed37c4b3eb b/cache/c3529808c5c5a39bba63a0996781692de3d681a1bfe32c48e1dad0ed37c4b3eb new file mode 100644 index 0000000..3f217cc --- /dev/null +++ b/cache/c3529808c5c5a39bba63a0996781692de3d681a1bfe32c48e1dad0ed37c4b3eb @@ -0,0 +1 @@ +{"raw":"0f0041f7951ed2f222f77ee7a5def7be3b2d05ee03605065c6ca397b44e77add0187344e3c523b0c1999e0ac7987def42d47c6358b09de502cc3158040391e21cafd42fe8099371189158099517e5abd3dccbb","sent_at":1.790666418e9} \ No newline at end of file diff --git a/cli/src/main.rs b/cli/src/main.rs index 92cb8de..a24a7b2 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -154,7 +154,12 @@ enum Command { fn main() { let cli = Cli::parse(); - if let Err(e) = run(cli) { + let result = run(cli); + // The RNS carrier owns a loop thread that must be joined before the + // process exits, or it races the teardown of the statics it calls into. + #[cfg(feature = "rns")] + fumi::rns::stop(); + if let Err(e) = result { eprintln!("error: {e}"); hint(e.downcast_ref::()); std::process::exit(1); diff --git a/core/shim/smolmail_rns.cpp b/core/shim/smolmail_rns.cpp index d4499da..df41604 100644 --- a/core/shim/smolmail_rns.cpp +++ b/core/shim/smolmail_rns.cpp @@ -41,6 +41,10 @@ static RNS::Link active_link({RNS::Type::NONE}); static microStore::FileSystem filesystem{microStore::Adapters::UniversalFileSystem()}; static volatile bool running = false; +// Joinable, never detached: the loop thread must be joined by +// smolmail_rns_stop before the process tears down statics, or it keeps +// calling reticulum.loop() while their destructors run. +static std::thread loop_thread; // The single response slot (plan: one request in flight at a time). static std::mutex slot_mutex; @@ -94,9 +98,11 @@ static void on_failed(const RNS::RequestReceipt& receipt) { slot_cv.notify_all(); } -// The unwrapped large-response payload, filled in by smolmail_rns_request. -static RNS::Bytes unwrapped; - +// The unwrapped large-response payload, filled in per request below. It is +// deliberately local: a static here kept the last resource-path response +// alive past its request, so every later bare response -- an empty fetch +// page, an ack -- was misread as that stale page and the client looped +// FETCH/DELETE pairs forever. static void loop_thread_main() { while (running) { reticulum.loop(); @@ -139,7 +145,7 @@ extern "C" int smolmail_rns_start(const char* storage_dir, reticulum.start(); running = true; - std::thread(loop_thread_main).detach(); + loop_thread = std::thread(loop_thread_main); return 0; } @@ -256,6 +262,7 @@ extern "C" int smolmail_rns_request(const uint8_t* request, size_t request_len, // raw remainder to handle_response on that path). The wrapper is // unambiguous: a smolmail status is a single byte below 16, and the // msgpack bin headers are 0xC4..0xC6, so only those are unwrapped. + RNS::Bytes unwrapped; const RNS::Bytes& payload = [&]() -> const RNS::Bytes& { if (slot_response.size() > 0 && slot_response.data()[0] >= 0xC4 && slot_response.data()[0] <= 0xC6) { @@ -286,3 +293,23 @@ extern "C" void smolmail_rns_close(void) { active_link = RNS::Link({RNS::Type::NONE}); } } + +extern "C" void smolmail_rns_stop(void) { + if (!running) { + return; + } + // Order matters: halt and join the loop thread first, so nothing is + // inside Reticulum, Transport or the filesystem while they are torn + // down; only then take the link, the interface and the instance apart. + running = false; + if (loop_thread.joinable()) { + loop_thread.join(); + } + if (active_link) { + active_link.teardown(); + active_link = RNS::Link({RNS::Type::NONE}); + } + RNS::Transport::deregister_interface(udp_interface); + udp_interface.stop(); + reticulum = RNS::Reticulum({RNS::Type::NONE}); +} diff --git a/core/shim/smolmail_rns.h b/core/shim/smolmail_rns.h index 9cbe732..65cf269 100644 --- a/core/shim/smolmail_rns.h +++ b/core/shim/smolmail_rns.h @@ -52,6 +52,14 @@ int smolmail_rns_request(const uint8_t *request, size_t request_len, * minutes (upstream spec sec 13.10). */ void smolmail_rns_close(void); +/* Stops the Reticulum stack: halts and joins the loop thread, tears the link + * down, deregisters and stops the UDP interface, and releases the + * Reticulum instance. The loop thread must be joined before the process (or + * embedding host) tears down statics, or it keeps calling into Reticulum + * while their destructors run. A no-op when the stack is not running. + * The stack can be started again afterwards. */ +void smolmail_rns_stop(void); + #ifdef __cplusplus } #endif diff --git a/core/src/rns/ffi.rs b/core/src/rns/ffi.rs index e06f367..86335a9 100644 --- a/core/src/rns/ffi.rs +++ b/core/src/rns/ffi.rs @@ -42,6 +42,8 @@ mod inner { ) -> c_int; pub fn smolmail_rns_close(); + + pub fn smolmail_rns_stop(); } } @@ -150,6 +152,14 @@ pub fn close() { unsafe { inner::smolmail_rns_close() }; } +/// Stops the Reticulum stack: joins the loop thread, tears the link down, +/// stops the UDP interface and releases the instance. Must run before the +/// embedding process tears down statics; a no-op when never started, and +/// the stack can be started again afterwards. +pub fn stop() { + unsafe { inner::smolmail_rns_stop() }; +} + /// The destination hash length, for callers checking address shapes. pub const DEST_LEN: usize = 16; const _: () = assert!(DEST_LEN == 16 && KEY_LEN == 32); diff --git a/core/src/rns/mod.rs b/core/src/rns/mod.rs index e183f07..b209446 100644 --- a/core/src/rns/mod.rs +++ b/core/src/rns/mod.rs @@ -2,7 +2,10 @@ //! the C shim, dispatched through the same operations as TCP. //! //! The carrier starts lazily, on the first `smol+rns://` dial, so local-only -//! commands stay usable offline and start instantly. A client creates no +//! commands stay usable offline and start instantly, and stops via `stop()`, +//! which every embedding process must call before it tears down: the +//! Reticulum loop thread otherwise races the destruction of the statics it +//! is calling into. A client creates no //! Reticulum identity (upstream spec sec 13.8): the shim never calls //! `Link::identify`, and the flags below configure only the storage path and //! the UDP interface. @@ -11,6 +14,7 @@ pub mod ffi; pub mod transport; use std::path::Path; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Mutex, OnceLock}; use crate::error::Error; @@ -44,7 +48,7 @@ impl Default for RnsConfig { } static CONFIG: OnceLock = OnceLock::new(); -static STARTED: OnceLock<()> = OnceLock::new(); +static STARTED: AtomicBool = AtomicBool::new(false); static START_MUTEX: Mutex<()> = Mutex::new(()); /// Records the carrier configuration; the first dial wins, as in a CLI the @@ -53,19 +57,33 @@ pub fn configure(config: RnsConfig) { let _ = CONFIG.set(config); } -/// Starts the Reticulum stack at most once, on the first RNS dial. +/// Starts the Reticulum stack at most once between calls to `stop`, on the +/// first RNS dial. pub fn ensure_started() -> Result<(), Error> { - if STARTED.get().is_some() { + if STARTED.load(Ordering::Acquire) { return Ok(()); } let _guard = START_MUTEX.lock().unwrap(); - if STARTED.get().is_some() { + if STARTED.load(Ordering::Acquire) { return Ok(()); } let config = CONFIG.get().cloned().unwrap_or_default(); std::fs::create_dir_all(Path::new(&config.storage_dir)) .map_err(|e| Error::Other(format!("cannot create {}: {e}", config.storage_dir)))?; ffi::start(&config)?; - let _ = STARTED.set(()); + STARTED.store(true, Ordering::Release); Ok(()) } + +/// Stops the Reticulum stack and joins its loop thread. The shim's loop +/// thread otherwise outlives the caller and races the teardown of its statics +/// at process exit — a nondeterministic hang the RNS carrier hit on a +/// first-run provisioning pass. Embedders call this when they are done with +/// the carrier; the CLI calls it before exiting. A no-op when never started, +/// and the stack starts again on the next dial. +pub fn stop() { + if !STARTED.swap(false, Ordering::AcqRel) { + return; + } + ffi::stop(); +} diff --git a/hashlist_store/index.dat b/hashlist_store/index.dat new file mode 100644 index 0000000000000000000000000000000000000000..7df472d972f45a19d7c437b6e9e8708b5cd17121 GIT binary patch literal 5194 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 Rl5SUaUup(v0M*kl4FCyOs)qmo literal 0 HcmV?d00001 diff --git a/hashlist_store/seg0.dat b/hashlist_store/seg0.dat new file mode 100644 index 0000000000000000000000000000000000000000..daf1d6456666da124721ed41c1233d4b599fa0d3 GIT binary patch literal 5600 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 literal 0 HcmV?d00001 diff --git a/hashlist_store/seg1.dat b/hashlist_store/seg1.dat new file mode 100644 index 0000000000000000000000000000000000000000..0020b8574b5ba6e43c25948e339463d6dc87f776 GIT binary patch literal 2016 zcmcb2;NAfS1qKHBwB1<@P%y2tYmU*p{NJ-wS}iwiQ#@AT`?BPX+sAo-+;{M2PrUBL zv*Fw?Z2G<*^t^t4VN&<=)UbEY-*_!j*%*-7{A+@l%TC4zA*F|J;L_(W_i*hl+qR{< zpLC{F_7_fktnirC<*52y1&yyoXf8EXx z4ml_D4nL~B`KBZ*Lmrnt%ZjqhU9*22k<7H5u=0Sr9?Kr%rzdLnp7^q1$;Tk;A3j|K@gXz?G_v0Y`A@o3OC=;|t+k3-c{jr=&K#z8n(CGEa1N91Pilzw9GO^&q_uH_`NZ{4!$-Ro7j^o6+W4H0F( zxN(#AxfJ;|Ix9{ej=U(F6gG87>wy&>f7AEi(iih~{&Q2UWj}WNe7N~cs^zeIa_JEd zIiW2Z-*;rsT@w5dmp)xCiB3sP zeSNwl^~Qr=9~m4JuB&(do0ViBkHEw_Jj@&N&?extj?0mPSv<*y? zaOq3&Q;pdyxH|p7^XvClK3z#xb#g6I9+>a`BR1T#g+AS zM_t8(wSE@dxw^S&5j)Gv=^7tAaOqo+mXz^}z2|WC#n>M|rmj4>k|*`-`8+mFBc2!87IAKwb!grU z*G1RaQhe5@-4i**_GoKGzjng6)t{`maOv}zB7C<@c-e&1a2@M8=Re#|INZH?`iuut z zJ7R)+r3D*D=e)S>e~elJ_C5Jyv}o?EvnmI?-{R6YO~LHLt804~yKwwiyu|*@^T%H< zUdt@~yW!B*Lw@sRCi>vgC*vE#zOu1e|5lp--y0dTnd_dew3~Zx@`DvTi$8w*AEbdx zUvf;d`AW%;X?Oeh9d8<~QDBp})>D*Lh!}mxtW1T7LPpnas1;<#YVG znWy2>XMEFf&V_=ueV1jYeS7ig*QcgQ-d~w4?k=$ty(xO-O~(~n`ao$~f7r8uh-{%nKQtvpmY5k?ka@UN!zI8o&G4uWP zUWhMaG{hDIN8*q*X{H6!CKW8BlUt-DcS>l?~= z+VR##bqQPF&?5rqswN)c66Ji?B*&1oJMqC0g=vST{O>5f+U61Wp=AQAy?6MI-m+-j z7$dfjbqkMcgzY-AoRI#p)Gr-yyi!znC#E-QSNO-pT!yo5o_+DJ$gArD!YNkv*-1;6 zTQtV-&DB&7O24rEoBM&h#f%qkKi~Oc-)7TLhbO_GF1^$KQu01=MKa%Vm)Z7;$3INl Ty5N(%N}1<*877_$=Y9bI>XL