summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
author卜部昌平 <shyouhei@ruby-lang.org>2021-06-09 13:59:32 +0900
committer卜部昌平 <shyouhei@ruby-lang.org>2021-09-10 20:00:06 +0900
commita50287ab03ce9cf871d307cc619e394d9a736466 (patch)
tree0a2aa940b2cd440a2da9886a5fab6238f61849ca /include
parent53e0d7eec71302eacd370426d0af29f8adcfe19d (diff)
include/ruby/thread.h: add doxygen
Must not be a bad idea to improve documents. [ci skip]
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/4815
Diffstat (limited to 'include')
-rw-r--r--include/ruby/thread.h157
1 files changed, 155 insertions, 2 deletions
diff --git a/include/ruby/thread.h b/include/ruby/thread.h
index b05537badb..18c792b386 100644
--- a/include/ruby/thread.h
+++ b/include/ruby/thread.h
@@ -10,19 +10,147 @@
* 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);
+
+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);
@@ -30,11 +158,36 @@ void *rb_thread_call_without_gvl2(void *(*func)(void *), void *data1,
* 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_
RBIMPL_SYMBOL_EXPORT_END()