-
Notifications
You must be signed in to change notification settings - Fork 189
/
Copy paththread.h
499 lines (455 loc) · 18.2 KB
/
thread.h
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
#ifndef RBIMPL_INTERN_THREAD_H /*-*-C++-*-vi:se ft=cpp:*/
#define RBIMPL_INTERN_THREAD_H
/**
* @file
* @author Ruby developers <ruby-core@ruby-lang.org>
* @copyright This file is a part of the programming language Ruby.
* Permission is hereby granted, to either redistribute and/or
* modify this file, provided that the conditions mentioned in the
* file COPYING are met. Consult the file for details.
* @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
* implementation details. Don't take them as canon. They could
* rapidly appear then vanish. The name (path) of this header file
* is also an implementation detail. Do not expect it to persist
* at the place it is now. Developers are free to move it anywhere
* anytime at will.
* @note To ruby-core: remember that this header can be possibly
* recursively included from extension libraries written in C++.
* Do not expect for instance `__VA_ARGS__` is always available.
* We assume C99 for ruby itself but we don't assume languages of
* extension libraries. They could be written in C++98.
* @brief Public APIs related to ::rb_cThread.
*/
#include "ruby/internal/attr/nonnull.h"
#include "ruby/internal/cast.h"
#include "ruby/internal/config.h"
#include "ruby/internal/dllexport.h"
#include "ruby/internal/value.h"
RBIMPL_SYMBOL_EXPORT_BEGIN()
struct timeval;
/* thread.c */
/**
* Tries to switch to another thread. This function blocks until the current
* thread re-acquires the GVL.
*
* @exception rb_eInterrupt Operation interrupted.
*/
void rb_thread_schedule(void);
/**
* Blocks the current thread until the given file descriptor is ready to be
* read.
*
* @param[in] fd A file descriptor.
* @exception rb_eIOError Closed stream.
* @exception rb_eSystemCallError Situations like EBADF.
*/
int rb_thread_wait_fd(int fd);
/**
* Identical to rb_thread_wait_fd(), except it blocks the current thread until
* the given file descriptor is ready to be written.
*
* @param[in] fd A file descriptor.
* @exception rb_eIOError Closed stream.
* @exception rb_eSystemCallError Situations like EBADF.
*/
int rb_thread_fd_writable(int fd);
/**
* Notifies a closing of a file descriptor to other threads. Multiple threads
* can wait for the given file descriptor at once. If such file descriptor is
* closed, threads need to start propagating their exceptions. This is the API
* to kick that process.
*
* @param[in] fd A file descriptor.
* @note This function blocks until all the threads waiting for such fd
* have woken up.
*/
void rb_thread_fd_close(int fd);
/**
* Checks if the thread this function is running is the only thread that is
* currently alive.
*
* @retval 1 Yes it is.
* @retval 0 No it isn't.
*
* @internal
*
* Above description is in fact inaccurate. There are Ractors these days.
*/
int rb_thread_alone(void);
/**
* Blocks for the given period of time.
*
* @warning This function can be interrupted by signals.
* @param[in] sec Duration in seconds.
* @exception rb_eInterrupt Interrupted.
*/
void rb_thread_sleep(int sec);
/**
* Blocks indefinitely.
*
* @exception rb_eInterrupt Interrupted.
*/
void rb_thread_sleep_forever(void);
/**
* Identical to rb_thread_sleep_forever(), except the thread calling this
* function is considered "dead" when our deadlock checker is triggered.
*
* @exception rb_eInterrupt Interrupted.
*/
void rb_thread_sleep_deadly(void);
/**
* Stops the current thread. This is not the end of the thread's lifecycle. A
* stopped thread can later be woken up.
*
* @exception rb_eThreadError Stopping this thread would deadlock.
* @retval ::RUBY_Qnil Always.
*
* @internal
*
* The return value makes no sense at all.
*/
VALUE rb_thread_stop(void);
/**
* Marks a given thread as eligible for scheduling.
*
* @note It may still remain blocked on I/O.
* @note This does not invoke the scheduler itself.
*
* @param[out] thread Thread in question to wake up.
* @exception rb_eThreadError Stop flogging a dead horse.
* @return The passed thread.
* @post The passed thread is made runnable.
*/
VALUE rb_thread_wakeup(VALUE thread);
/**
* Identical to rb_thread_wakeup(), except it doesn't raise on an already
* killed thread.
*
* @param[out] thread A thread to wake up.
* @retval RUBY_Qnil `thread` is already killed.
* @retval otherwise `thread` is alive.
* @post The passed thread is made runnable, unless killed.
*/
VALUE rb_thread_wakeup_alive(VALUE thread);
/**
* This is a rb_thread_wakeup() + rb_thread_schedule() combo.
*
* @note There is no guarantee that this function yields to the passed
* thread. It may still remain blocked on I/O.
* @param[out] thread Thread in question to wake up.
* @exception rb_eThreadError Stop flogging a dead horse.
* @return The passed thread.
*/
VALUE rb_thread_run(VALUE thread);
/**
* Terminates the given thread. Unlike a stopped thread, a killed thread could
* never be revived. This function does return, when passed e.g. an already
* killed thread. But if the passed thread is the only one, or a special
* thread called "main", then it also terminates the entire process.
*
* @param[out] thread The thread to terminate.
* @exception rb_eFatal The passed thread is the running thread.
* @exception rb_eSystemExit The passed thread is the last thread.
* @return The passed thread.
* @post Either the passed thread, or the process entirely, is killed.
*
* @internal
*
* It seems killing the main thread also kills the entire process even if there
* are multiple running ractors. No idea why.
*/
VALUE rb_thread_kill(VALUE thread);
RBIMPL_ATTR_NONNULL((1))
/**
* Creates a Ruby thread that is backended by a C function.
*
* @param[in] f The function to run on a thread.
* @param[in,out] g Passed through to `f`.
* @exception rb_eThreadError Could not create a ruby thread.
* @exception rb_eSystemCallError Situations like `EPERM`.
* @return Allocated instance of ::rb_cThread.
* @note This doesn't wait for anything.
*/
VALUE rb_thread_create(VALUE (*f)(void *g), void *g);
/**
* Identical to rb_thread_sleep(), except it takes struct `timeval` instead.
*
* @warning This function can be interrupted by signals.
* @param[in] time Duration.
* @exception rb_eInterrupt Interrupted.
*/
#ifdef TRUFFLERUBY
void rb_tr_thread_wait_for(struct timeval* time);
static inline void rb_thread_wait_for(struct timeval time) {
rb_tr_thread_wait_for(&time);
}
#else
void rb_thread_wait_for(struct timeval time);
#endif
/**
* Obtains the "current" thread.
*
* @return The current thread of the current ractor of the current execution
* context.
* @pre This function must be called from a thread controlled by ruby.
*/
VALUE rb_thread_current(void);
/**
* Obtains the "main" thread. There are threads called main. Historically the
* (only) main thread was the one which runs when the process boots. Now that
* we have Ractor, there are more than one main threads.
*
* @return The main thread of the current ractor of the current execution
* context.
* @pre This function must be called from a thread controlled by ruby.
*/
VALUE rb_thread_main(void);
/**
* This badly named function reads from a Fiber local storage. When this
* function was born there was no such thing like a Fiber. The world was
* innocent. But now... This is a Fiber local storage. Sorry.
*
* @param[in] thread Thread that the target Fiber is running.
* @param[in] key The name of the Fiber local storage to read.
* @retval RUBY_Qnil No such storage.
* @retval otherwise The value stored at `key`.
* @note There in fact are "true" thread local storage, but Ruby doesn't
* provide any interface of them to you, C programmers.
*/
VALUE rb_thread_local_aref(VALUE thread, ID key);
/**
* This badly named function writes to a Fiber local storage. When this
* function was born there was no such thing like a Fiber. The world was
* innocent. But now... This is a Fiber local storage. Sorry.
*
* @param[in] thread Thread that the target Fiber is running.
* @param[in] key The name of the Fiber local storage to write.
* @param[in] val The new value of the storage.
* @exception rb_eFrozenError `thread` is frozen.
* @return The passed `val` as-is.
* @post Fiber local storage `key` has value of `val`.
* @note There in fact are "true" thread local storage, but Ruby doesn't
* provide any interface of them to you, C programmers.
*/
VALUE rb_thread_local_aset(VALUE thread, ID key, VALUE val);
/**
* A `pthread_atfork(3posix)`-like API. Ruby expects its child processes to
* call this function at the very beginning of their processes. If you plan to
* fork a process don't forget to call it.
*/
void rb_thread_atfork(void);
/**
* :FIXME: situation of this function is unclear. It seems nobody uses it.
* Maybe a good idea to KonMari.
*/
void rb_thread_atfork_before_exec(void);
/**
* "Recursion" API entry point. This basically calls the given function with
* the given arguments, but additionally with recursion flag. The flag is set
* to 1 if the execution have already experienced the passed `g` parameter
* before.
*
* @param[in] f The function that possibly recurs.
* @param[in,out] g Passed as-is to `f`.
* @param[in,out] h Passed as-is to `f`.
* @return The return value of f.
*/
VALUE rb_exec_recursive(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h);
/**
* Identical to rb_exec_recursive(), except it checks for the recursion on the
* ordered pair of `{ g, p }` instead of just `g`.
*
* @param[in] f The function that possibly recurs.
* @param[in,out] g Passed as-is to `f`.
* @param[in] p Paired object for recursion detection.
* @param[in,out] h Passed as-is to `f`.
*/
VALUE rb_exec_recursive_paired(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h);
/**
* Identical to rb_exec_recursive(), except it calls `f` for outermost
* recursion only. Inner recursions yield calls to rb_throw_obj().
*
* @param[in] f The function that possibly recurs.
* @param[in,out] g Passed as-is to `f`.
* @param[in,out] h Passed as-is to `f`.
* @return The return value of f.
*
* @internal
*
* It seems nobody uses the "it calls rb_throw_obj()" part of this function.
* @shyouhei doesn't understand the needs.
*/
VALUE rb_exec_recursive_outer(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h);
/**
* Identical to rb_exec_recursive_outer(), except it checks for the recursion
* on the ordered pair of `{ g, p }` instead of just `g`. It can also be seen
* as a routine identical to rb_exec_recursive_paired(), except it calls `f`
* for outermost recursion only. Inner recursions yield calls to
* rb_throw_obj().
*
* @param[in] f The function that possibly recurs.
* @param[in,out] g Passed as-is to `f`.
* @param[in] p Paired object for recursion detection.
* @param[in,out] h Passed as-is to `f`.
*
* @internal
*
* It seems nobody uses the "it calls rb_throw_obj()" part of this function.
* @shyouhei doesn't understand the needs.
*/
VALUE rb_exec_recursive_paired_outer(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h);
/**
* This is the type of UBFs. An UBF is a function that unblocks a blocking
* region. For instance when a thread is blocking due to `pselect(3posix)`, it
* is highly expected that `pthread_kill(3posix)` can interrupt the system call
* and the thread could revive. Or when a thread is blocking due to
* `waitpid(3posix)`, it is highly expected that killing the waited process
* should suffice. An UBF is a function that does such things. Designing your
* own UBF needs deep understanding of why your blocking region blocks, how
* threads work in ruby, and a matter of luck. It often is the case you simply
* cannot cancel something that had already begun.
*
* @see rb_thread_call_without_gvl()
*/
typedef void rb_unblock_function_t(void *);
/**
* @private
*
* This is an implementation detail. Must be a mistake to be here.
*
* @internal
*
* Why is this function type different from what rb_thread_call_without_gvl()
* takes?
*/
typedef VALUE rb_blocking_function_t(void *);
/**
* Checks for interrupts. In ruby, signals are masked by default. You can
* call this function at will to check if there are pending signals. In case
* there are, they would be handled in this function.
*
* If your extension library has a function that takes a long time, consider
* calling it periodically.
*
* @note It might switch to another thread.
*/
void rb_thread_check_ints(void);
/**
* Checks if the thread's execution was recently interrupted. If called from
* that thread, this function can be used to detect spurious wake-ups.
*
* @param[in] thval Thread in question.
* @retval 0 The thread was not interrupted.
* @retval otherwise The thread was interrupted recently.
*
* @internal
*
* Above description is not a lie. But actually the return value is an opaque
* trap vector. If you know which bit means which, you can know what happened.
*/
int rb_thread_interrupted(VALUE thval);
/**
* A special UBF for blocking IO operations. You need deep understanding of
* what this actually do before using. Basically you should not use it from
* extension libraries. It is too easy to mess up.
*/
#define RUBY_UBF_IO RBIMPL_CAST((rb_unblock_function_t *)-1)
/**
* A special UBF for blocking process operations. You need deep understanding
* of what this actually do before using. Basically you should not use it from
* extension libraries. It is too easy to mess up.
*/
#define RUBY_UBF_PROCESS RBIMPL_CAST((rb_unblock_function_t *)-1)
/* thread_sync.c */
/**
* Creates a mutex.
*
* @return An allocated instance of rb_cMutex.
*/
VALUE rb_mutex_new(void);
/**
* Queries if there are any threads that holds the lock.
*
* @param[in] mutex The mutex in question.
* @retval RUBY_Qtrue The mutex is locked by someone.
* @retval RUBY_Qfalse The mutex is not locked by anyone.
*/
VALUE rb_mutex_locked_p(VALUE mutex);
/**
* Attempts to lock the mutex, without waiting for other threads to unlock it.
* Failure in locking the mutex can be detected by the return value.
*
* @param[out] mutex The mutex to lock.
* @retval RUBY_Qtrue Successfully locked by the current thread.
* @retval RUBY_Qfalse Otherwise.
* @note This function also returns ::RUBY_Qfalse when the mutex is
* already owned by the calling thread itself.
*/
VALUE rb_mutex_trylock(VALUE mutex);
/**
* Attempts to lock the mutex. It waits until the mutex gets available.
*
* @param[out] mutex The mutex to lock.
* @exception rb_eThreadError Recursive deadlock situation.
* @return The passed mutex.
* @post The mutex is owned by the current thread.
*/
VALUE rb_mutex_lock(VALUE mutex);
/**
* Releases the mutex.
*
* @param[out] mutex The mutex to unlock.
* @exception rb_eThreadError The mutex is not owned by the current thread.
* @return The passed mutex.
* @post Upon successful return the passed mutex is no longer owned by
* the current thread.
*/
VALUE rb_mutex_unlock(VALUE mutex);
/**
* Releases the lock held in the mutex and waits for the period of time;
* reacquires the lock on wakeup.
*
* @pre The lock has to be owned by the current thread beforehand.
* @param[out] self The target mutex.
* @param[in] timeout Duration, in seconds, in ::rb_cNumeric.
* @exception rb_eArgError `timeout` is negative.
* @exception rb_eRangeError `timeout` is out of range of `time_t`.
* @exception rb_eThreadError The mutex is not owned by the current thread.
* @return Number of seconds it actually slept.
* @warning It is a failure not to check the return value. This function
* can return spuriously for various reasons. Maybe other threads
* can rb_thread_wakeup(). Maybe an end user can press the
* Control and C key from the interactive console. On the other
* hand it can also take longer than the specified. The mutex
* could be locked by someone else. It waits then.
* @post Upon successful return the passed mutex is owned by the current
* thread.
*
* @internal
*
* This function is called from `ConditionVariable#wait`. So it is not a
* deprecated feature. However @shyouhei have never seen any similar mutex
* primitive available in any other languages than Ruby.
*
* EDIT: In 2021, @shyouhei asked @ko1 in person about this API. He answered
* that it is his invention. The motivation behind its design is to eliminate
* needs of condition variables as primitives. Unlike other languages, Ruby's
* `ConditionVariable` class was written in pure-Ruby initially. We don't have
* to implement machine-native condition variables in assembly each time we
* port Ruby to a new architecture. This function made it possible. "I felt I
* was a genius when this idea came to me", said @ko1.
*
* `rb_cConditionVariable` is now written in C for speed, though.
*/
VALUE rb_mutex_sleep(VALUE self, VALUE timeout);
/**
* Obtains the lock, runs the passed function, and releases the lock when it
* completes.
*
* @param[out] mutex The mutex to lock.
* @param[in] func What to do during the mutex is locked.
* @param[in,out] arg Passed as-is to `func`.
*/
VALUE rb_mutex_synchronize(VALUE mutex, VALUE (*func)(VALUE arg), VALUE arg);
RBIMPL_SYMBOL_EXPORT_END()
#endif /* RBIMPL_INTERN_THREAD_H */