qr_reader-native 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE-mruby.txt +22 -0
- data/LICENSE-spinel.txt +19 -0
- data/LICENSE.txt +21 -0
- data/README.md +61 -0
- data/ext/qr_reader_native/SPINEL_VERSION +1 -0
- data/ext/qr_reader_native/extconf.rb +8 -0
- data/ext/qr_reader_native/mruby/error.h +13 -0
- data/ext/qr_reader_native/mruby/internal.h +145 -0
- data/ext/qr_reader_native/mruby/string.h +22 -0
- data/ext/qr_reader_native/mruby.h +93 -0
- data/ext/qr_reader_native/mruby_shim.h +118 -0
- data/ext/qr_reader_native/qr_reader_native.c +13513 -0
- data/ext/qr_reader_native/qr_reader_native.h +29 -0
- data/ext/qr_reader_native/qr_reader_native_ext.c +598 -0
- data/ext/qr_reader_native/re_cased.h +71 -0
- data/ext/qr_reader_native/re_compile.c +5784 -0
- data/ext/qr_reader_native/re_ctype.h +3540 -0
- data/ext/qr_reader_native/re_exec.c +2190 -0
- data/ext/qr_reader_native/re_internal.h +671 -0
- data/ext/qr_reader_native/re_prop.h +4629 -0
- data/ext/qr_reader_native/re_spinel.c +363 -0
- data/ext/qr_reader_native/re_spinel.h +22 -0
- data/ext/qr_reader_native/re_spinel_utf8.inc +97 -0
- data/ext/qr_reader_native/re_utf8.c +156 -0
- data/ext/qr_reader_native/sp_alloc.c +1447 -0
- data/ext/qr_reader_native/sp_alloc.h +1196 -0
- data/ext/qr_reader_native/sp_argf.h +33 -0
- data/ext/qr_reader_native/sp_array.c +1052 -0
- data/ext/qr_reader_native/sp_array.h +456 -0
- data/ext/qr_reader_native/sp_bigint.c +5826 -0
- data/ext/qr_reader_native/sp_bigint.h +117 -0
- data/ext/qr_reader_native/sp_cold.c +5259 -0
- data/ext/qr_reader_native/sp_compat.h +402 -0
- data/ext/qr_reader_native/sp_core.c +818 -0
- data/ext/qr_reader_native/sp_core.h +56 -0
- data/ext/qr_reader_native/sp_crypto.c +877 -0
- data/ext/qr_reader_native/sp_crypto.h +169 -0
- data/ext/qr_reader_native/sp_dtoa.c +1533 -0
- data/ext/qr_reader_native/sp_dtoa.h +29 -0
- data/ext/qr_reader_native/sp_encoding_names.h +172 -0
- data/ext/qr_reader_native/sp_enum.h +86 -0
- data/ext/qr_reader_native/sp_exc.c +1185 -0
- data/ext/qr_reader_native/sp_exc.h +186 -0
- data/ext/qr_reader_native/sp_exc_ctx.h +56 -0
- data/ext/qr_reader_native/sp_fiber.c +889 -0
- data/ext/qr_reader_native/sp_fiber.h +162 -0
- data/ext/qr_reader_native/sp_fiber_ctx.h +84 -0
- data/ext/qr_reader_native/sp_format.c +695 -0
- data/ext/qr_reader_native/sp_format.h +83 -0
- data/ext/qr_reader_native/sp_gc.c +1793 -0
- data/ext/qr_reader_native/sp_gc.h +762 -0
- data/ext/qr_reader_native/sp_hash.c +131 -0
- data/ext/qr_reader_native/sp_hash.h +131 -0
- data/ext/qr_reader_native/sp_inspect.c +215 -0
- data/ext/qr_reader_native/sp_inspect.h +128 -0
- data/ext/qr_reader_native/sp_io.c +1676 -0
- data/ext/qr_reader_native/sp_io.h +272 -0
- data/ext/qr_reader_native/sp_iobuffer.c +1014 -0
- data/ext/qr_reader_native/sp_iobuffer.h +167 -0
- data/ext/qr_reader_native/sp_marshal.c +487 -0
- data/ext/qr_reader_native/sp_marshal.h +60 -0
- data/ext/qr_reader_native/sp_net.c +1029 -0
- data/ext/qr_reader_native/sp_net.h +205 -0
- data/ext/qr_reader_native/sp_pack.c +1254 -0
- data/ext/qr_reader_native/sp_poly_cold.c +1220 -0
- data/ext/qr_reader_native/sp_proc.c +410 -0
- data/ext/qr_reader_native/sp_proc.h +327 -0
- data/ext/qr_reader_native/sp_process.c +547 -0
- data/ext/qr_reader_native/sp_process_status.c +211 -0
- data/ext/qr_reader_native/sp_process_status.h +66 -0
- data/ext/qr_reader_native/sp_random.c +287 -0
- data/ext/qr_reader_native/sp_random.h +52 -0
- data/ext/qr_reader_native/sp_range.h +144 -0
- data/ext/qr_reader_native/sp_re.c +1379 -0
- data/ext/qr_reader_native/sp_re.h +186 -0
- data/ext/qr_reader_native/sp_re_names.h +59 -0
- data/ext/qr_reader_native/sp_sched.c +4044 -0
- data/ext/qr_reader_native/sp_sched.h +295 -0
- data/ext/qr_reader_native/sp_slab.c +1507 -0
- data/ext/qr_reader_native/sp_str.c +1462 -0
- data/ext/qr_reader_native/sp_str.h +306 -0
- data/ext/qr_reader_native/sp_str_crypt.c +23 -0
- data/ext/qr_reader_native/sp_string.c +92 -0
- data/ext/qr_reader_native/sp_string.h +253 -0
- data/ext/qr_reader_native/sp_system.c +78 -0
- data/ext/qr_reader_native/sp_system.h +18 -0
- data/ext/qr_reader_native/sp_time.c +860 -0
- data/ext/qr_reader_native/sp_time.h +100 -0
- data/ext/qr_reader_native/sp_types.h +292 -0
- data/ext/qr_reader_native/spinel_rt.h +19017 -0
- data/ext/qr_reader_native/unicase.c +399 -0
- data/ext/qr_reader_native/unicase.h +901 -0
- data/lib/qr_reader/native.rb +6 -0
- metadata +153 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/* sp_sched.h -- cooperative M:N thread scheduler, Phase 0 (N=1).
|
|
2
|
+
*
|
|
3
|
+
* A Ruby Thread is a green thread (sp_thread) wrapping an sp_Fiber that the
|
|
4
|
+
* codegen builds exactly like a Fiber.new block (so all the capture/self/cell
|
|
5
|
+
* machinery is shared); the block's result lands in fiber->yielded_value. The
|
|
6
|
+
* main program runs on the root fiber and IS the scheduler hub: it executes
|
|
7
|
+
* top-level code directly and, whenever it blocks (#join / #value /
|
|
8
|
+
* Thread.pass) or at program exit (drain), it pumps a FIFO run queue of
|
|
9
|
+
* runnable green threads. A spawned thread yields by transferring back to the
|
|
10
|
+
* root fiber, where the pump loop -- or the fiber trampoline, on termination --
|
|
11
|
+
* resumes.
|
|
12
|
+
*
|
|
13
|
+
* With a single OS worker there is no concurrent heap mutation, so the GC and
|
|
14
|
+
* allocator are untouched. This is the Phase 0 cooperative core of
|
|
15
|
+
* docs/thread-mn-design.md; N>1 parallelism, preemption, and the
|
|
16
|
+
* Mutex/Queue/ConditionVariable primitives build on this scaffolding later.
|
|
17
|
+
*/
|
|
18
|
+
#ifndef SP_SCHED_H
|
|
19
|
+
#define SP_SCHED_H
|
|
20
|
+
|
|
21
|
+
#include <poll.h>
|
|
22
|
+
#include "sp_fiber.h"
|
|
23
|
+
|
|
24
|
+
typedef enum { SP_TH_RUNNABLE, SP_TH_RUNNING, SP_TH_BLOCKED, SP_TH_DEAD } sp_thread_state;
|
|
25
|
+
|
|
26
|
+
/* A thread's membership in one descriptor's waiter list. idx -1 names the
|
|
27
|
+
thread's own io_fd / io_events; idx >= 0 names io_set[idx]. */
|
|
28
|
+
typedef struct sp_ev_waiter {
|
|
29
|
+
struct sp_thread *t;
|
|
30
|
+
int idx;
|
|
31
|
+
struct sp_ev_waiter *next;
|
|
32
|
+
} sp_ev_waiter;
|
|
33
|
+
|
|
34
|
+
typedef struct sp_sched_timer sp_sched_timer;
|
|
35
|
+
|
|
36
|
+
typedef struct sp_thread {
|
|
37
|
+
sp_Fiber *fiber; /* the green thread's coroutine; NULL for the main thread (root) */
|
|
38
|
+
sp_Fiber *at; /* the Fiber it resumed and stopped inside, or NULL when it
|
|
39
|
+
stopped in its own: run_thread_once switches back there */
|
|
40
|
+
sp_RbVal arg; /* Thread.new(arg) -> the block's first param, on first run */
|
|
41
|
+
sp_RbVal retval; /* block result (copied from fiber->yielded_value at death) */
|
|
42
|
+
sp_RbVal name; /* #name / #name= (a string or nil) */
|
|
43
|
+
const char *birth_file; /* creation site for #inspect (#3126); rodata */
|
|
44
|
+
sp_int birth_line;
|
|
45
|
+
sp_thread_state state;
|
|
46
|
+
int has_exc; /* body left an unhandled exception (re-raised at #join/#value) */
|
|
47
|
+
const char *exc_cls, *exc_msg;
|
|
48
|
+
void *exc_obj;
|
|
49
|
+
unsigned char report_on_exception;
|
|
50
|
+
unsigned char off_cpu; /* set by the worker once this thread has fully switched off its
|
|
51
|
+
stack; a waker may only enqueue it once off_cpu, otherwise it
|
|
52
|
+
could be run on a second worker mid-context-switch */
|
|
53
|
+
unsigned char wake_pending; /* a wake arrived while still on-cpu; the worker enqueues it */
|
|
54
|
+
unsigned char repark_front; /* next sp_sched_block puts this thread at the HEAD of the wait
|
|
55
|
+
list: a mutex waiter woken but beaten to the exchange keeps
|
|
56
|
+
its place in the arrival order */
|
|
57
|
+
unsigned char preempt_request; /* sysmon set this thread over its timeslice; it yields at its
|
|
58
|
+
next safepoint poll (cooperative preemption, §5) */
|
|
59
|
+
short home_wid; /* worker this thread FIRST ran on, -1 before its first run.
|
|
60
|
+
A started green thread is pinned there: its live C frames
|
|
61
|
+
hold compiler-cached addresses of that worker's __thread
|
|
62
|
+
data (GC shadow stack, exception stack, ...), so resuming
|
|
63
|
+
it on another worker corrupts both workers' TLS. */
|
|
64
|
+
double readied_at; /* SPINEL_SCHED_STATS=2: when it was last put on a run queue */
|
|
65
|
+
struct sp_thread *rq_next; /* run-queue link while RUNNABLE */
|
|
66
|
+
struct sp_thread *joiners; /* threads parked in #join/#value on this one */
|
|
67
|
+
sp_ev_waiter ev0; /* its entry in the per-DESCRIPTOR waiter list for io_fd: a
|
|
68
|
+
readiness event names an fd, and every thread parked on
|
|
69
|
+
that fd is woken from it (#4306) */
|
|
70
|
+
sp_ev_waiter *ev_set; /* one entry per descriptor of a SET wait (IO.select over
|
|
71
|
+
several handles), heap-allocated for the wait's duration */
|
|
72
|
+
struct pollfd *io_set; /* that set: fd + events per entry, the caller's array; io_fd
|
|
73
|
+
is -1 while it is in use (#4528) */
|
|
74
|
+
int io_nset;
|
|
75
|
+
struct sp_thread *wait_next; /* link within the wait list it is parked on */
|
|
76
|
+
struct sp_thread **wait_head; /* head of that wait list, so #kill/#raise can unpark it */
|
|
77
|
+
struct sp_thread *all_next, *all_prev; /* registry of live threads (GC roots) */
|
|
78
|
+
void *tls; /* thread-local storage (Thread#[] / #[]=); lazily allocated */
|
|
79
|
+
sp_sched_timer *timer; /* active scheduler timer for this wait, if any */
|
|
80
|
+
unsigned char timer_expired; /* set when a generic timed wait reaches its deadline */
|
|
81
|
+
unsigned char woken; /* Thread#wakeup ended its sleep early */
|
|
82
|
+
int io_fd; /* fd this thread is parked on for I/O (-1 = none, scheduler-aware I/O) */
|
|
83
|
+
short io_events; /* poll events it is waiting for (POLLIN/POLLOUT) */
|
|
84
|
+
short io_revents; /* poll result the monitor delivered when the fd became ready */
|
|
85
|
+
unsigned id;
|
|
86
|
+
} sp_thread;
|
|
87
|
+
|
|
88
|
+
/* Called once from main()'s prologue (on the root fiber, after sp_tu_init) when
|
|
89
|
+
the program uses threads. Adopts the running context as the main thread and
|
|
90
|
+
chains a GC mark hook that roots every live green thread. */
|
|
91
|
+
void sp_sched_init(void);
|
|
92
|
+
|
|
93
|
+
/* Thread.new { ... }: wrap a fiber (built by the codegen via emit_fiber_new) in
|
|
94
|
+
a green thread and enqueue it RUNNABLE. It runs the next time the current
|
|
95
|
+
thread yields, or at drain. Returns the thread (boxed SP_BUILTIN_THREAD). */
|
|
96
|
+
sp_thread *sp_Thread_spawn_fiber(sp_Fiber *f, sp_RbVal arg);
|
|
97
|
+
sp_thread *sp_Thread_spawn_fiber_at(sp_Fiber *f, sp_RbVal arg, const char *file, sp_int line);
|
|
98
|
+
|
|
99
|
+
/* #join: block until the thread has finished, re-raise its unhandled exception
|
|
100
|
+
in the caller, then return the thread. #value: same, but return its result. */
|
|
101
|
+
sp_thread *sp_Thread_join(sp_thread *t);
|
|
102
|
+
sp_RbVal sp_Thread_value(sp_thread *t);
|
|
103
|
+
|
|
104
|
+
sp_thread *sp_Thread_kill(sp_thread *t); /* #kill / #exit / #terminate */
|
|
105
|
+
sp_thread *sp_Thread_raise(sp_thread *t, const char *cls, const char *msg, void *obj); /* #raise */
|
|
106
|
+
void sp_Thread_pass(void); /* Thread.pass: cooperative yield */
|
|
107
|
+
void sp_Thread_stop(void); /* Thread.stop: sleep until #wakeup */
|
|
108
|
+
sp_thread *sp_Thread_wakeup(sp_thread *t); /* #wakeup */
|
|
109
|
+
sp_thread *sp_Thread_run(sp_thread *t); /* #run: #wakeup, then pass */
|
|
110
|
+
sp_bool sp_Thread_stop_p(sp_thread *t); /* #stop?: dead or asleep */
|
|
111
|
+
void sp_sched_sleep_forever(void); /* a bare Kernel#sleep */
|
|
112
|
+
/* Kernel#sleep(seconds): park the calling thread and free its OS worker for other
|
|
113
|
+
green threads; a monitor thread wakes it after the duration. Falls back to a
|
|
114
|
+
plain blocking sleep only in the single-threaded build (spinel_rt.h). */
|
|
115
|
+
void sp_sched_sleep(double seconds);
|
|
116
|
+
int sp_sched_other_threads_live(void);
|
|
117
|
+
int sp_sched_wait_child(int pid, int *status);
|
|
118
|
+
void sp_sleep(sp_float s); /* Kernel#sleep; relocated from spinel_rt.h to lib/sp_cold.c */
|
|
119
|
+
void sp_sleep_forever(void); /* a bare Kernel#sleep, until Thread#wakeup */
|
|
120
|
+
/* Scheduler-aware blocking I/O: park the calling green thread until `fd` is ready
|
|
121
|
+
for `events` (POLLIN/POLLOUT), freeing its OS worker for other threads; the
|
|
122
|
+
monitor polls the fd and wakes it. Returns 1 to retry the syscall (fd ready or
|
|
123
|
+
errored), 0 to give up (shutdown). Falls back to a plain blocking poll in the
|
|
124
|
+
single-threaded build / before the monitor starts. */
|
|
125
|
+
int sp_sched_wait_io(int fd, short events);
|
|
126
|
+
/* The same over a SET of descriptors (IO.select over several handles): park
|
|
127
|
+
until any entry is ready or the timeout passes (negative: none). Returns 1
|
|
128
|
+
when something is ready -- the caller polls the set with a zero timeout to
|
|
129
|
+
learn which -- and 0 on the timeout. A blocking poll here held the OS
|
|
130
|
+
worker for the wait, and a worker in a syscall never reaches a safepoint,
|
|
131
|
+
so a collection waited on it (#4528). */
|
|
132
|
+
int sp_sched_wait_io_set(struct pollfd *set, int n, double timeout_s);
|
|
133
|
+
/* A handle is closing: drop any persistent readiness registration for `fd`
|
|
134
|
+
while the descriptor still names the right thing. Safe to call for a
|
|
135
|
+
descriptor that was never registered, and on a build with no event set. */
|
|
136
|
+
void sp_sched_ev_forget(int fd);
|
|
137
|
+
/* The same park with a deadline: wake when `fd` is ready for `events` OR when
|
|
138
|
+
`timeout_s` seconds have passed, whichever comes first (a negative timeout
|
|
139
|
+
is no deadline, i.e. sp_sched_wait_io). Returns 1 when the fd is ready or
|
|
140
|
+
errored, 0 on the deadline (or shutdown). The monitor watches the fd and the
|
|
141
|
+
clock together, so a timed readiness wait -- IO#wait_readable(t),
|
|
142
|
+
IO.select([io], nil, nil, t) -- costs the calling green thread, not its OS
|
|
143
|
+
worker; a blocking select(2) here pins the worker for the whole timeout,
|
|
144
|
+
and with every worker pinned the peer that would make the fd ready never
|
|
145
|
+
gets to run. */
|
|
146
|
+
int sp_sched_wait_io_timeout(int fd, short events, double timeout_s);
|
|
147
|
+
/* sp_sched_wait_io for a handle another thread may close meanwhile: `cancel`
|
|
148
|
+
is the handle's closed flag, read under the scheduler lock before the park
|
|
149
|
+
registers. A close that landed after the caller's own readiness probe is
|
|
150
|
+
then answered at once (returns 1; the caller finds the handle closed),
|
|
151
|
+
where the registration would otherwise go in after the close's
|
|
152
|
+
sp_sched_ev_forget and never be readied. */
|
|
153
|
+
int sp_sched_wait_io_unless(int fd, short events, const unsigned char *cancel);
|
|
154
|
+
sp_thread *sp_Thread_current(void); /* Thread.current */
|
|
155
|
+
sp_bool sp_Thread_alive(sp_thread *t); /* #alive? */
|
|
156
|
+
sp_bool sp_Thread_set_report_default(sp_bool v); /* Thread.report_on_exception= */
|
|
157
|
+
sp_bool sp_Thread_get_report_default(void); /* Thread.report_on_exception */
|
|
158
|
+
sp_bool sp_Thread_set_report(sp_thread *t, sp_bool v); /* #report_on_exception= */
|
|
159
|
+
sp_bool sp_Thread_get_report(sp_thread *t); /* #report_on_exception */
|
|
160
|
+
sp_thread *sp_Thread_main(void); /* Thread.main */
|
|
161
|
+
sp_int sp_Thread_list_count(void); /* Thread.list (built by the TU) */
|
|
162
|
+
sp_thread *sp_Thread_list_at(sp_int i);
|
|
163
|
+
sp_RbVal sp_Thread_get_name(sp_thread *t); /* #name */
|
|
164
|
+
sp_RbVal sp_Thread_set_name(sp_thread *t, sp_RbVal v); /* #name= */
|
|
165
|
+
sp_RbVal sp_Thread_status(sp_thread *t); /* #status: "run"/"sleep"/false/nil */
|
|
166
|
+
const char *sp_Thread_inspect(sp_thread *t); /* #inspect / #to_s */
|
|
167
|
+
sp_RbVal sp_Thread_tls_get(sp_thread *t, sp_sym k); /* Thread#[] */
|
|
168
|
+
sp_RbVal sp_Thread_tls_set(sp_thread *t, sp_sym k, sp_RbVal v); /* Thread#[]= */
|
|
169
|
+
sp_bool sp_Thread_tls_key(sp_thread *t, sp_sym k); /* Thread#key? */
|
|
170
|
+
|
|
171
|
+
/* Run any remaining runnable threads to completion. Emitted at the end of
|
|
172
|
+
main() so a fire-and-forget Thread still runs its body. */
|
|
173
|
+
void sp_sched_drain(void);
|
|
174
|
+
|
|
175
|
+
/* Safepoint (design 5.1). codegen emits, at loop back-edges of a threaded
|
|
176
|
+
program, `if (SP_UNLIKELY(SP_SAFEPOINT_POLL())) sp_safepoint();` so a worker
|
|
177
|
+
in a long-running loop periodically checks whether a stop-the-world (GC)
|
|
178
|
+
wants it to park. The flag is set only in the threaded runtime while a
|
|
179
|
+
collector has requested STW; at N=1 it is never set, so the poll is a single
|
|
180
|
+
predicted-not-taken load. Declared unconditionally (not under SP_THREADS) so
|
|
181
|
+
the emitted poll also compiles when a threaded program's generated C is built
|
|
182
|
+
against the single-threaded archive (e.g. the test harness's manual cc path),
|
|
183
|
+
where it is an inert no-op. sp_safepoint() parks the worker at the GC barrier
|
|
184
|
+
(the real body lands with the workers + STW).
|
|
185
|
+
|
|
186
|
+
The flag is written by the collector (and the preempt signal handler) and
|
|
187
|
+
read bare by every mutator; under SP_THREADS both sides go through relaxed
|
|
188
|
+
atomics so the cross-thread poll is defined behavior (and TSan-clean).
|
|
189
|
+
Relaxed is enough: a mutator that misses one update just polls again next
|
|
190
|
+
back-edge, and sp_safepoint() itself acquires the sched lock, which orders
|
|
191
|
+
everything that matters. */
|
|
192
|
+
extern volatile int sp_safepoint_flag;
|
|
193
|
+
#ifdef SP_THREADS
|
|
194
|
+
#define SP_SAFEPOINT_POLL() SP_ATOMIC_LOAD(&sp_safepoint_flag, __ATOMIC_RELAXED)
|
|
195
|
+
#define SP_SAFEPOINT_SET(v) SP_ATOMIC_STORE(&sp_safepoint_flag, (v), __ATOMIC_RELAXED)
|
|
196
|
+
#else
|
|
197
|
+
#define SP_SAFEPOINT_POLL() (sp_safepoint_flag)
|
|
198
|
+
#define SP_SAFEPOINT_SET(v) (sp_safepoint_flag = (v))
|
|
199
|
+
#endif
|
|
200
|
+
void sp_safepoint(void);
|
|
201
|
+
/* A blocking native call (an ffi_func declared `blocking: true`) is bracketed
|
|
202
|
+
by these: the worker leaves the world, so a collection raised while the
|
|
203
|
+
call runs does not wait for it, and a collection in progress is waited out
|
|
204
|
+
on the way back. The call must touch no Ruby object. No-ops without the
|
|
205
|
+
threaded runtime or before the worker pool exists. */
|
|
206
|
+
#ifdef SP_THREADS
|
|
207
|
+
void sp_native_enter(void);
|
|
208
|
+
void sp_native_leave(void);
|
|
209
|
+
#else
|
|
210
|
+
static inline void sp_native_enter(void) {}
|
|
211
|
+
static inline void sp_native_leave(void) {}
|
|
212
|
+
#endif
|
|
213
|
+
|
|
214
|
+
/* Optional hook the generated TU installs so a worker parking at a safepoint
|
|
215
|
+
also publishes its per-worker in-flight GC roots that live in the TU (pending
|
|
216
|
+
exception objects, proc-return home values) onto its shadow stack, for the
|
|
217
|
+
stop-the-world collector to mark. NULL when the program has none. */
|
|
218
|
+
extern void (*sp_safepoint_publish_hook)(void);
|
|
219
|
+
|
|
220
|
+
/* ---- Queue (thread-safe FIFO) ----
|
|
221
|
+
* A producer/consumer hand-off. #pop on an empty queue blocks the calling green
|
|
222
|
+
* thread (parking it, yielding to the scheduler) until a #push wakes it; this is
|
|
223
|
+
* the coordination the lazy Thread model could not express. */
|
|
224
|
+
typedef struct sp_queue {
|
|
225
|
+
sp_RbVal *buf; /* ring buffer of queued values */
|
|
226
|
+
sp_int head, len, cap;
|
|
227
|
+
sp_int max; /* SizedQueue capacity; 0 = unbounded Queue */
|
|
228
|
+
struct sp_thread *pop_waiters; /* threads blocked in #pop on an empty queue */
|
|
229
|
+
struct sp_thread *push_waiters; /* threads blocked in #push on a full SizedQueue */
|
|
230
|
+
int closed;
|
|
231
|
+
} sp_queue;
|
|
232
|
+
|
|
233
|
+
sp_queue *sp_Queue_new(void);
|
|
234
|
+
sp_queue *sp_SizedQueue_new(sp_int max); /* SizedQueue.new(max) */
|
|
235
|
+
void sp_Queue_push(sp_queue *q, sp_RbVal v); /* #push / #<< / #enq (blocks when full) */
|
|
236
|
+
void sp_Queue_push_nb(sp_queue *q, sp_RbVal v); /* SizedQueue#push(non_block: true) */
|
|
237
|
+
void sp_Queue_push_options_check(sp_queue *q, int has_non_block, int has_timeout);
|
|
238
|
+
sp_bool sp_Queue_push_timeout(sp_queue *q, sp_RbVal v, double seconds); /* false on timeout */
|
|
239
|
+
sp_RbVal sp_Queue_pop(sp_queue *q); /* #pop / #shift / #deq (blocks when empty) */
|
|
240
|
+
sp_RbVal sp_Queue_pop_nb(sp_queue *q); /* #pop(truthy): no_wait, raises ThreadError on empty */
|
|
241
|
+
sp_RbVal sp_Queue_pop_timeout(sp_queue *q, double seconds); /* nil on timeout */
|
|
242
|
+
sp_int sp_Queue_size(sp_queue *q); /* #size / #length */
|
|
243
|
+
sp_bool sp_Queue_empty(sp_queue *q); /* #empty? */
|
|
244
|
+
sp_int sp_Queue_max(sp_queue *q); /* SizedQueue#max */
|
|
245
|
+
void sp_Queue_close(sp_queue *q); /* #close */
|
|
246
|
+
sp_bool sp_Queue_closed(sp_queue *q); /* #closed? */
|
|
247
|
+
sp_int sp_Queue_num_waiting(sp_queue *q); /* #num_waiting */
|
|
248
|
+
void sp_Queue_clear(sp_queue *q); /* #clear */
|
|
249
|
+
|
|
250
|
+
/* ---- Mutex (non-recursive lock; owner + wait list) ----
|
|
251
|
+
* Threads are kept alive by the scheduler registry, so neither struct needs a
|
|
252
|
+
* GC scan over its owner/waiter pointers. */
|
|
253
|
+
typedef struct sp_mutex {
|
|
254
|
+
struct sp_thread *owner; /* NULL = unlocked */
|
|
255
|
+
struct sp_thread *waiters; /* threads blocked in #lock */
|
|
256
|
+
/* Monitor is a REENTRANT mutex, and that is the whole reason it exists:
|
|
257
|
+
`m.synchronize { m.synchronize { } }` is the ordinary way to write a
|
|
258
|
+
method that is safe to call from another synchronized method of the same
|
|
259
|
+
object. A Mutex must still raise ThreadError there, so reentrancy is a
|
|
260
|
+
per-object flag rather than the lock's behaviour. `depth` counts the
|
|
261
|
+
re-acquisitions the owner holds beyond the first. */
|
|
262
|
+
int reentrant;
|
|
263
|
+
int depth;
|
|
264
|
+
/* Threads parked in #lock, counted so an uncontended #unlock can see that
|
|
265
|
+
there is nobody to hand off to without taking the global scheduler lock
|
|
266
|
+
(#4346). Mutated only under that lock; read atomically outside it. */
|
|
267
|
+
int nwaiters;
|
|
268
|
+
} sp_mutex;
|
|
269
|
+
|
|
270
|
+
sp_mutex *sp_Mutex_new(void);
|
|
271
|
+
sp_mutex *sp_Monitor_new(void); /* a Mutex with reentrancy on */
|
|
272
|
+
const char *sp_Mutex_class_name(sp_mutex *m); /* "Monitor" or "Thread::Mutex" */
|
|
273
|
+
const char *sp_Queue_class_name(sp_queue *q); /* "Thread::SizedQueue" or "Thread::Queue" */
|
|
274
|
+
void sp_Mutex_lock(sp_mutex *m);
|
|
275
|
+
void sp_Mutex_unlock(sp_mutex *m);
|
|
276
|
+
sp_RbVal sp_Mutex_sleep(sp_mutex *m, int has_timeout, double timeout); /* #sleep / #sleep(t) */
|
|
277
|
+
sp_bool sp_Mutex_try_lock(sp_mutex *m); /* #try_lock: true if acquired */
|
|
278
|
+
sp_bool sp_Mutex_locked(sp_mutex *m); /* #locked? */
|
|
279
|
+
sp_bool sp_Mutex_owned(sp_mutex *m); /* #owned? */
|
|
280
|
+
|
|
281
|
+
/* ---- ConditionVariable (wait/signal/broadcast over a Mutex) ---- */
|
|
282
|
+
typedef struct sp_condvar {
|
|
283
|
+
struct sp_thread *waiters; /* threads blocked in #wait */
|
|
284
|
+
} sp_condvar;
|
|
285
|
+
|
|
286
|
+
sp_condvar *sp_CondVar_new(void);
|
|
287
|
+
/* #wait answers what CRuby's Mutex#sleep answers: nil when a timeout ran out,
|
|
288
|
+
otherwise the whole seconds slept (time(2) after less time(2) before). */
|
|
289
|
+
sp_RbVal sp_CondVar_wait(sp_condvar *cv, sp_mutex *m); /* release m, park, re-acquire m */
|
|
290
|
+
sp_RbVal sp_CondVar_wait_nb(sp_condvar *cv, sp_mutex *m); /* release m, do not park, re-acquire m */
|
|
291
|
+
sp_RbVal sp_CondVar_wait_timeout(sp_condvar *cv, sp_mutex *m, double seconds); /* timed #wait */
|
|
292
|
+
void sp_CondVar_signal(sp_condvar *cv); /* #signal */
|
|
293
|
+
void sp_CondVar_broadcast(sp_condvar *cv); /* #broadcast */
|
|
294
|
+
|
|
295
|
+
#endif /* SP_SCHED_H */
|