1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//! Receive-side store-and-forward poll for the `chat` session (epic #200, issue #274).
//!
//! `bingle_local`'s [`poll_mailbox`](bingle_local::api::bingle_local_api::BingleLocalApi::poll_mailbox)
//! is caller-driven: it drains this account's Sidewinder Mailbox, decrypts and stores each held
//! message, but decides nothing about *when* to run. `bingle_jsi` drives it from the app foreground
//! lifecycle; `bingle_cli chat` has no such lifecycle, so this module supplies the CLI analogue — a
//! single poll the session runs on connect and again on a backstop cycle while it is up.
//!
//! The network drain itself (and its at-most-once / sorted-by-sent-time guarantees) is proven by the
//! `bingle_local` gated read-on-reconnect tests (#215); this module is only the *when*, kept small and
//! pure so the interval resolution and the receive-gate no-op are unit-testable without a live node.
use ;
use BingleApiLocalImpl;
use Duration;
use crateChatState;
/// Default period of the receive-side backstop Mailbox poll when `--poll-interval` is not given: 20s.
/// A backstop, not the primary path — real-time delivery normally beats it — so a short, responsive
/// cadence is fine for the interactive CLI (the JSI client uses a much longer production period).
pub const DEFAULT_MAILBOX_POLL_SECS: u64 = 20;
/// Persist a shared local store handle to `state_file`, for the shutdown path (issue #276).
///
/// A no-op when no state file is configured. Runs on the shared `Arc<BingleApiLocalImpl>` so the
/// Ctrl-C/SIGTERM handler saves without taking the session lock, which a background poll can hold for
/// the length of its network wait; blocking the handler there was why the session would not exit.
/// Resolve the backstop poll period from the optional `--poll-interval <secs>` (issue #274), falling
/// back to [`DEFAULT_MAILBOX_POLL_SECS`]. A zero value is clamped to the default: a 0s cycle would
/// busy-spin the poller thread.
/// Poll this account's Sidewinder Mailbox once, persist anything read, and return the batch read this
/// poll (sorted by sent time) for the caller to display like a real-time message.
///
/// A no-op returning an empty vector when the receive gate is off, so it is safe to call
/// unconditionally. Best-effort: a node/keypair problem surfaces as an empty batch (bingle_local logs
/// the detail); it never errors, so a poll failure cannot tear down the chat session. Read messages
/// are also recorded on the local history by `poll_mailbox`; on a non-empty read we save the state
/// file so they survive a restart. Real-time-delivered messages are not duplicated: the sender only
/// posts to the Mailbox on give-up (so a directly-delivered message was never posted), and the FIFO
/// read-and-drop is at-most-once, so a polled message is not re-read on the next cycle.
/// Poll a shared local store handle once, as [`poll_once`] does, but taking the store directly rather
/// than through a [`ChatState`].
///
/// This is the form the background poller (issue #274) uses: it holds a clone of the session's
/// `Arc<BingleApiLocalImpl>` (via [`ChatState::local_handle`](crate::chat_state::ChatState::local_handle))
/// and polls off the session lock. A poll's network wait can be up to 120s; running it on the shared
/// handle instead of under the session mutex keeps the REPL responsive and, crucially, lets
/// Ctrl-C/SIGTERM shut the session down promptly instead of blocking behind an in-flight poll
/// (issue #276). The store is fully interior-mutable, so a poll and a concurrent interactive send do
/// not corrupt each other.