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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! RAII attach guard for OS threads that did not originate inside Lean.
//!
//! Code that calls into Lean from a thread the Lean runtime did not start
//! itself must attach that thread before any Lean work and detach it
//! before the thread exits. [`LeanThreadGuard`] is the RAII type that
//! does both: [`LeanThreadGuard::attach`] performs the attach,
//! [`LeanThreadGuard::drop`] performs the detach.
//!
//! Every thread that successfully called [`super::LeanRuntime::init`] is
//! implicitly marked as permitted to invoke Lean from that thread. The
//! [`debug_assert_attached`] check at the host-call funnel is therefore
//! a defensive instrument: in correctly-typed code it always passes
//! (every host call requires a `&'lean LeanRuntime`, which is `!Send`,
//! so the holding thread necessarily went through `init` on this
//! thread). The assertion catches unsafe-code misuse and accidental
//! `Send` impls.
// SAFETY DOC: each `unsafe { ... }` block carries its own `// SAFETY:`
// comment. The blanket allow keeps the unsafe surface in the smallest
// `pub(crate)` scope that compiles, per the safety model in
// `docs/architecture/01-safety-model.md`.
use Cell;
use PhantomData;
use LeanRuntime;
thread_local!
/// Mark the calling thread as permitted to invoke Lean from this thread.
///
/// Called from [`super::LeanRuntime::init`] on every successful return,
/// not only the first. Subsequent calls on the same thread are no-ops:
/// the depth never drops below the permanent floor of `1` that `init`
/// installs. The init thread is, by this rule, also covered — it called
/// `init` on itself like every other client.
pub
/// Debug-mode assertion that the current thread is attached to Lean.
///
/// Inserted at every host call funnel (notably
/// [`crate::module::LeanExported::call`]) so that a worker thread that
/// forgets to construct a [`LeanThreadGuard`] panics with a clear Rust
/// message instead of tripping a Lean-side assertion. Compiles to a
/// no-op in release builds.
///
/// The `stage` argument is the textual label included in the panic
/// message; pass a `&'static str` identifying the call site.
pub
/// RAII handle that attaches the current OS thread to the Lean runtime.
///
/// When constructed via [`LeanThreadGuard::attach`], the guard registers
/// the calling thread with the Lean runtime so that thread may invoke
/// compiled Lean code. When the guard is dropped (or its scope ends), the
/// thread is detached again. Threads spawned by Lean itself are already
/// attached and do **not** need a guard; the thread that called
/// [`crate::LeanRuntime::init`] is likewise treated as attached for its
/// entire lifetime.
///
/// The guard is neither [`Send`] nor [`Sync`]: it represents a per-thread
/// resource, and shipping it to a different OS thread to be dropped would
/// detach the wrong thread. The `'lean` lifetime parameter ties the guard
/// to a [`LeanRuntime`] borrow, so a guard cannot outlive the runtime it
/// is bound to.
///
/// Nested attaches are legal: a worker thread already inside an outer
/// `LeanThreadGuard` may construct another (e.g. inside a callback) and
/// the per-thread attach depth tracks the pairs so the host-call
/// attach assertion does not fire prematurely.
///
/// # Example
///
/// ```ignore
/// // Inside a worker thread you spawned yourself:
/// let runtime = lean_rs::LeanRuntime::init()?;
/// let _guard = lean_rs::LeanThreadGuard::attach(runtime);
/// // ... call into Lean from this thread ...
/// // `_guard` is dropped at scope exit, detaching the thread cleanly.
/// ```