Skip to content

Commit

Permalink
[ci skip] comment for commit be1bbd5
Browse files Browse the repository at this point in the history
  • Loading branch information
shyouhei committed Dec 8, 2023
1 parent 352a885 commit 51ab9eb
Showing 1 changed file with 105 additions and 5 deletions.
110 changes: 105 additions & 5 deletions include/ruby/ruby.h
Expand Up @@ -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")
Expand Down

0 comments on commit 51ab9eb

Please sign in to comment.