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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//! The rayforce event loop.
//!
//! [`Poll`] is the core's `ray_poll_t` — the same loop the REPL and native IPC
//! use. On its own it does nothing interesting; it exists so a *subscription*
//! can hand it a connected socket and have every inbound frame read and
//! dispatched. See [`crate::q::Subscription`].
//!
//! There is one poll per runtime, owned by the runtime and torn down with it.
//! [`Poll::install`] returns a handle to it, creating it on first use, so a
//! [`crate::TcpClient`] and a subscription never fight over it.
//!
//! ```no_run
//! use rayforce::{Poll, Runtime};
//! Runtime::scope(|_rt| {
//! let poll = Poll::install()?;
//! poll.run_for(200)?; // one 200 ms slice
//! Ok(())
//! })?;
//! # Ok::<(), rayforce::RayError>(())
//! ```
use c_void;
use PhantomData;
use rayforce_sys as sys;
use crate;
/// The runtime's event loop.
///
/// `!Send`/`!Sync` like the rest of the crate: every frame this loop reads is
/// decoded into engine objects, so it must run on the thread that owns the
/// [`crate::Runtime`].
///
/// # Ownership and teardown
///
/// The poll belongs to the [`crate::Runtime`], not to this handle: closing a
/// selector releases engine objects held for it, so the loop has to be torn
/// down while the heap is still alive, which is what `Runtime`'s `Drop` does.
/// A `Poll` is therefore just a borrow of it, and any number of them may exist
/// at once — [`crate::TcpClient`] installs one the same way.
///
/// What keeps a `Poll` inside its runtime is that it is `!Send`, which is the
/// bound [`crate::Runtime::scope`] reads: returning one from the closure, or
/// assigning it into a variable declared outside, does not compile. So the
/// ordinary way to get one past its runtime does not exist.
///
/// The `on_runtime_thread()` check on every call is for the way that does — a
/// `thread_local!` stash, which captures nothing and so satisfies the bound.
/// A `Poll` that gets out that way is inert rather than dangerous: every method
/// returns an error instead of touching a destroyed loop. The check is safe to
/// trust because it happens before the pointer is dereferenced and nothing can
/// drop the runtime in between — this is all one thread.
///
/// # Safety
///
/// `!Send`/`!Sync`, and must stay so, for two reasons that have to hold at
/// once. Every frame this loop reads is decoded into engine objects, which
/// belong to the thread that owns the runtime — and the liveness check above is
/// only trustworthy while nothing else can tear the runtime down between it and
/// the dereference. On top of that, `!Send` is now what confines a `Poll` to
/// its scope: make this type `Send` and `Runtime::scope` would happily let one
/// out, with no error until the first call on a destroyed loop.
///
/// ```compile_fail
/// fn assert_send<T: Send>() {}
/// assert_send::<rayforce::Poll>();
/// ```
/// ```compile_fail
/// fn assert_sync<T: Sync>() {}
/// assert_sync::<rayforce::Poll>();
/// ```
/// Control — `compile_fail` passes on *any* build failure, a rename included:
/// ```
/// fn assert_exists<T>() {}
/// assert_exists::<rayforce::Poll>();
/// ```