aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author卜部昌平 <shyouhei@ruby-lang.org>2023-12-08 13:14:19 +0900
committer卜部昌平 <shyouhei@ruby-lang.org>2023-12-08 13:17:56 +0900
commit51ab9ebca18f3b9a0b8f2bfc420454983259c600 (patch)
tree57b306511b262b369200eed1dff80573e10248fd
parent352a885a0f1a4d4576c686301ee71ea887a345e5 (diff)
downloadruby-51ab9ebca18f3b9a0b8f2bfc420454983259c600.tar.gz
[ci skip] comment for commit be1bbd5b7d40ad863ab35097765d3754726bbd54
-rw-r--r--include/ruby/ruby.h110
1 files changed, 105 insertions, 5 deletions
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index 91e24bcebd..d3db2d07fa 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -272,23 +272,123 @@ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 0)
*/
int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap);
-// TODO: doc
-
#include <errno.h>
+/**
+ * @name Errno handling routines for userland threads
+ * @note POSIX chapter 2 section 3 states that for each thread of a process,
+ * the value of `errno` shall not be affected by function calls or
+ * assignments to `errno` by other threads.
+ *
+ * Soooo this `#define errno` below seems like a noob mistake at first sight.
+ * If you look at its actual implementation, the functions are just adding one
+ * level of indirection. It doesn't make any sense sorry? But yes! @ko1 told
+ * @shyouhei that this is invevitable.
+ *
+ * The ultimate reason is because Ruby now has N:M threads implemented.
+ * Threads of that sort change their context in user land. A function can be
+ * "transferred" between threads in middle of their executions. Let us for
+ * instance consider:
+ *
+ * ```cxx
+ * void foo()
+ * {
+ * auto i = errno;
+ * close(0);
+ * errno = i;
+ * }
+ * ```
+ *
+ * This function (if ran under our Ractor) could change its running thread at
+ * the `close` function. But the two `errno` invokations are different! Look
+ * how the source code above is compiled by clang 17 with `-O3` flag @ Linux:
+ *
+ * ```
+ * foo(int): # @foo(int)
+ * push rbp
+ * push r14
+ * push rbx
+ * mov ebx, edi
+ * call __errno_location@PLT
+ * mov r14, rax
+ * mov ebp, dword ptr [rax]
+ * mov edi, ebx
+ * call close@PLT
+ * mov dword ptr [r14], ebp
+ * pop rbx
+ * pop r14
+ * pop rbp
+ * ret
+ * ```
+ *
+ * Notice how `__errno_location@PLT` is `call`-ed only once. The compiler
+ * assumes that the location of `errno` does not change during a function call.
+ * Sadly this is no longer true for us. The `close@PLT` now changes threads,
+ * which should also change where `errno` is stored.
+ *
+ * With the `#define errno` below the compilation result changes to this:
+ *
+ * ```
+ * foo(int): # @foo(int)
+ * push rbp
+ * push rbx
+ * push rax
+ * mov ebx, edi
+ * call rb_errno_ptr()@PLT
+ * mov ebp, dword ptr [rax]
+ * mov edi, ebx
+ * call close@PLT
+ * call rb_errno_ptr()@PLT
+ * mov dword ptr [rax], ebp
+ * add rsp, 8
+ * pop rbx
+ * pop rbp
+ * ret
+ * ```
+ *
+ * Which fixes the problem.
+ */
+
+/**
+ * Identical to system `errno`.
+ *
+ * @return The last set `errno` number.
+ */
int rb_errno(void);
-void rb_errno_set(int);
+
+/**
+ * Set the errno.
+ *
+ * @param err New `errno`.
+ * @post `errno` is now set to `err`.
+ */
+void rb_errno_set(int err);
+
+/**
+ * The location of `errno`
+ *
+ * @return The (thread-specific) location of `errno`.
+ */
int *rb_errno_ptr(void);
+/**
+ * Not sure if it is necessary for extension libraries but this is where the
+ * "bare" errno is located.
+ *
+ * @return The location of `errno`.
+ */
static inline int *
rb_orig_errno_ptr(void)
{
return &errno;
}
-#define rb_orig_errno errno
+#define rb_orig_errno errno /**< System-provided original `errno`. */
#undef errno
-#define errno (*rb_errno_ptr())
+#define errno (*rb_errno_ptr()) /**< Ractor-aware version of `errno`. */
+
+/** @} */
+
/** @cond INTERNAL_MACRO */
#if RBIMPL_HAS_WARNING("-Wgnu-zero-variadic-macro-arguments")