summaryrefslogtreecommitdiff
path: root/include/ruby/thread.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/ruby/thread.h')
-rw-r--r--include/ruby/thread.h291
1 files changed, 287 insertions, 4 deletions
diff --git a/include/ruby/thread.h b/include/ruby/thread.h
index b05537badb..337f477fd0 100644
--- a/include/ruby/thread.h
+++ b/include/ruby/thread.h
@@ -10,33 +10,316 @@
* modify this file, provided that the conditions mentioned in the
* file COPYING are met. Consult the file for details.
*/
-#include "ruby/intern.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/intern/thread.h" /* rb_unblock_function_t */
#include "ruby/internal/dllexport.h"
-/* flags for rb_nogvl */
+/**
+ * @name Flags for rb_nogvl()
+ *
+ * @{
+ */
+
+/**
+ * Passing this flag to rb_nogvl() prevents it from checking interrupts.
+ * Interrupts can impact your program negatively. For instance consider
+ * following callback function:
+ *
+ * ```CXX
+ * static inline int fd; // set elsewhere.
+ * static inline auto callback(auto buf) {
+ * auto tmp = ruby_xmalloc(BUFSIZ);
+ * auto ret = ruby_xmalloc(sizeof(ssize_t)); // (a)
+ * auto n = read(fd, tmp, BUFSIZ); // (b)
+ * memcpy(buf, tmp, n); // (c)
+ * memcpy(ret, n, sizeof(n));
+ * ruby_xfree(tmp);
+ * return ret;
+ * }
+ * ```
+ *
+ * Here, if it gets interrupted at (a) or (b), `read(2)` is cancelled and this
+ * function leaks memory (which is not a good thing of course, but...). But if
+ * it gets interrupted at (c), where `read(2)` is already done, interruption is
+ * way more catastrophic because what was read gets lost. To reroute this kind
+ * of problem you should set this flag. And check interrupts elsewhere at your
+ * own risk.
+ */
#define RB_NOGVL_INTR_FAIL (0x1)
+
+/**
+ * Passing this flag to rb_nogvl() indicates that the passed UBF is
+ * async-signal-safe. An UBF could be async safe, and that makes things
+ * simpler. However async unsafe UBFs are just okay. If unsure, you can
+ * safely leave it unspecified.
+ *
+ * @internal
+ *
+ * This makes sense only in case of POSIX threads.
+ */
#define RB_NOGVL_UBF_ASYNC_SAFE (0x2)
+/** @} */
+
RBIMPL_SYMBOL_EXPORT_BEGIN()
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * (Re-)acquires the GVL. This manoeuvre makes it possible for an out-of-GVL
+ * routine to one-shot call a ruby method.
+ *
+ * What this function does:
+ *
+ * 1. Blocks until it acquires the GVL.
+ * 2. Calls the passed function.
+ * 3. Releases the GVL.
+ * 4. Returns what was returned form the passed function.
+ *
+ * @param[in] func What to call with GVL.
+ * @param[in,out] data1 Passed as-is to `func`.
+ * @return What was returned from `func`.
+ * @warning `func` must not return a Ruby object. If it did such return
+ * value would escape from GC's scope; would not be marked.
+ * @warning Global escapes from this function just yield whatever fatal
+ * undefined behaviours. You must make sure that `func` does
+ * not raise, by properly rescuing everything using
+ * e.g. rb_protect().
+ * @warning You cannot convert a non-Ruby thread into a Ruby thread
+ * using this API. This function makes sense only from inside
+ * of a rb_thread_call_without_gvl()'s callback.
+ */
void *rb_thread_call_with_gvl(void *(*func)(void *), void *data1);
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Allows the passed function to run in parallel with other Ruby threads.
+ *
+ * What this function does:
+ *
+ * 1. Checks (and handles) pending interrupts.
+ * 2. Releases the GVL. (Others can run here in parallel...)
+ * 3. Calls the passed function.
+ * 4. Blocks until it re-acquires the GVL.
+ * 5. Checks interrupts that happened between 2 to 4.
+ *
+ * In case other threads interfaced with this thread using rb_thread_kill()
+ * etc., the passed UBF is additionally called. See ::rb_unblock_function_t
+ * for details.
+ *
+ * Unlike rb_thread_call_without_gvl2() this function also reacts to signals
+ * etc.
+ *
+ * @param[in] func A function to call without GVL.
+ * @param[in,out] data1 Passed as-is to `func`.
+ * @param[in] ubf An UBF to cancel `func`.
+ * @param[in,out] data2 Passed as-is to `ubf`.
+ * @return What `func` returned, or 0 in case `ubf` cancelled `func`.
+ * @warning You cannot use most of Ruby C APIs like calling methods or
+ * raising exceptions from any of the functions passed to it.
+ * If that is dead necessary use rb_thread_call_with_gvl() to
+ * re-acquire the GVL.
+ * @warning In short, this API is difficult. @ko1 recommends you to use
+ * other ways if any. We lack experiences to use this API. If
+ * you find any corner cases etc., please report it to the
+ * devs.
+ * @warning Releasing and re-acquiring the GVL are expensive operations.
+ * For a short-running `func`, it might be faster to just call
+ * `func` with blocking everything else. Be sure to benchmark
+ * your code to see if it is actually worth releasing the GVL.
+ */
void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
- rb_unblock_function_t *ubf, void *data2);
+ rb_unblock_function_t *ubf, void *data2);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Identical to rb_thread_call_without_gvl(), except it does not interface with
+ * signals etc. As described in #RB_NOGVL_INTR_FAIL, interrupts can hurt you.
+ * In case this function detects an interrupt, it returns immediately. You can
+ * record progress of your callback and check it after returning from this
+ * function.
+ *
+ * What this function does:
+ *
+ * 1. Checks for pending interrupts and if any, just returns.
+ * 2. Releases the GVL. (Others can run here in parallel...)
+ * 3. Calls the passed function.
+ * 4. Blocks until it re-acquires the GVL.
+ *
+ * @param[in] func A function to call without GVL.
+ * @param[in,out] data1 Passed as-is to `func`.
+ * @param[in] ubf An UBF to cancel `func`.
+ * @param[in,out] data2 Passed as-is to `ubf`.
+ * @return What `func` returned, or 0 in case `func` did not return.
+ */
void *rb_thread_call_without_gvl2(void *(*func)(void *), void *data1,
- rb_unblock_function_t *ubf, void *data2);
+ rb_unblock_function_t *ubf, void *data2);
/*
* XXX: unstable/unapproved - out-of-tree code should NOT not depend
* on this until it hits Ruby 2.6.1
*/
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Identical to rb_thread_call_without_gvl(), except it additionally takes
+ * "flags" that change the behaviour.
+ *
+ * @param[in] func A function to call without GVL.
+ * @param[in,out] data1 Passed as-is to `func`.
+ * @param[in] ubf An UBF to cancel `func`.
+ * @param[in,out] data2 Passed as-is to `ubf`.
+ * @param[in] flags Flags.
+ * @return What `func` returned, or 0 in case `func` did not return.
+ */
void *rb_nogvl(void *(*func)(void *), void *data1,
rb_unblock_function_t *ubf, void *data2,
int flags);
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
#define RUBY_CALL_WO_GVL_FLAG_SKIP_CHECK_INTS_AFTER 0x01
+
+/**
+ * @private
+ * @deprecated It seems even in the old days it made no sense...?
+ */
#define RUBY_CALL_WO_GVL_FLAG_SKIP_CHECK_INTS_
+/**
+ * Declare the current Ruby thread should acquire a dedicated
+ * native thread on M:N thread scheduler.
+ *
+ * If a C extension (or a library which the extension relies on) should
+ * keep to run on a native thread (e.g. using thread-local-storage),
+ * this function allocates a dedicated native thread for the thread.
+ *
+ * @return `false` if the thread already running on a dedicated native
+ * thread. Otherwise `true`.
+ */
+bool rb_thread_lock_native_thread(void);
+
+/**
+ * Triggered when a new thread is started.
+ *
+ * @note The callback will be called *without* the GVL held.
+ */
+#define RUBY_INTERNAL_THREAD_EVENT_STARTED 1 << 0
+
+/**
+* Triggered when a thread attempt to acquire the GVL.
+*
+* @note The callback will be called *without* the GVL held.
+*/
+#define RUBY_INTERNAL_THREAD_EVENT_READY 1 << 1 /** acquiring GVL */
+
+/**
+ * Triggered when a thread successfully acquired the GVL.
+ *
+ * @note The callback will be called *with* the GVL held.
+ */
+#define RUBY_INTERNAL_THREAD_EVENT_RESUMED 1 << 2 /** acquired GVL */
+
+/**
+ * Triggered when a thread released the GVL.
+ *
+ * @note The callback will be called *without* the GVL held.
+ */
+#define RUBY_INTERNAL_THREAD_EVENT_SUSPENDED 1 << 3 /** released GVL */
+
+/**
+ * Triggered when a thread exits.
+ *
+ * @note The callback will be called *without* the GVL held.
+ */
+#define RUBY_INTERNAL_THREAD_EVENT_EXITED 1 << 4 /** thread terminated */
+
+#define RUBY_INTERNAL_THREAD_EVENT_MASK 0xff /** All Thread events */
+
+typedef struct rb_internal_thread_event_data {
+ VALUE thread;
+} rb_internal_thread_event_data_t;
+
+typedef void (*rb_internal_thread_event_callback)(rb_event_flag_t event,
+ const rb_internal_thread_event_data_t *event_data,
+ void *user_data);
+typedef struct rb_internal_thread_event_hook rb_internal_thread_event_hook_t;
+
+/**
+ * Registers a thread event hook function.
+ *
+ * @param[in] func A callback.
+ * @param[in] events A set of events that `func` should run.
+ * @param[in] data Passed as-is to `func`.
+ * @return An opaque pointer to the hook, to unregister it later.
+ * @note This functionality is a noop on Windows and WebAssembly.
+ * @note The callback will be called without the GVL held, except for the
+ * RESUMED event.
+ * @note Callbacks are not guaranteed to be executed on the native threads
+ * that corresponds to the Ruby thread. To identify which Ruby thread
+ * the event refers to, you must use `event_data->thread`.
+ * @warning This function MUST not be called from a thread event callback.
+ */
+rb_internal_thread_event_hook_t *rb_internal_thread_add_event_hook(
+ rb_internal_thread_event_callback func, rb_event_flag_t events,
+ void *data);
+
+
+/**
+ * Unregister the passed hook.
+ *
+ * @param[in] hook. The hook to unregister.
+ * @return Whether the hook was found and unregistered.
+ * @note This functionality is a noop on Windows and WebAssembly.
+ * @warning This function MUST not be called from a thread event callback.
+*/
+bool rb_internal_thread_remove_event_hook(
+ rb_internal_thread_event_hook_t * hook);
+
+
+typedef int rb_internal_thread_specific_key_t;
+#define RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX 8
+/**
+ * Create a key to store thread specific data.
+ *
+ * These APIs are designed for tools using
+ * rb_internal_thread_event_hook APIs.
+ *
+ * Note that only `RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX` keys
+ * can be created. raises `ThreadError` if exceeded.
+ *
+ * Usage:
+ * // at initialize time:
+ * int tool_key; // gvar
+ * Init_tool() {
+ * tool_key = rb_internal_thread_specific_key_create();
+ * }
+ *
+ * // at any timing:
+ * rb_internal_thread_specific_set(thread, tool_key, per_thread_data);
+ * ...
+ * per_thread_data = rb_internal_thread_specific_get(thread, tool_key);
+ */
+rb_internal_thread_specific_key_t rb_internal_thread_specific_key_create(void);
+
+/**
+ * Get thread and tool specific data.
+ *
+ * This function is async signal safe and thread safe.
+ */
+void *rb_internal_thread_specific_get(VALUE thread_val, rb_internal_thread_specific_key_t key);
+
+/**
+ * Set thread and tool specific data.
+ *
+ * This function is async signal safe and thread safe.
+ */
+void rb_internal_thread_specific_set(VALUE thread_val, rb_internal_thread_specific_key_t key, void *data);
+
RBIMPL_SYMBOL_EXPORT_END()
#endif /* RUBY_THREAD_H */