[ci skip] comment for commit be1bbd5b7d40ad863ab35097765d3754726bbd54

This commit is contained in:
卜部昌平 2023-12-08 13:14:19 +09:00
parent 352a885a0f
commit 51ab9ebca1

View File

@ -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")