diff options
Diffstat (limited to 'include')
57 files changed, 972 insertions, 547 deletions
diff --git a/include/ruby/assert.h b/include/ruby/assert.h index 0c052363bc..e9edd9e640 100644 --- a/include/ruby/assert.h +++ b/include/ruby/assert.h @@ -22,6 +22,7 @@ */ #include "ruby/internal/assume.h" #include "ruby/internal/attr/cold.h" +#include "ruby/internal/attr/format.h" #include "ruby/internal/attr/noreturn.h" #include "ruby/internal/cast.h" #include "ruby/internal/dllexport.h" @@ -132,6 +133,11 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() RBIMPL_ATTR_NORETURN() RBIMPL_ATTR_COLD() void rb_assert_failure(const char *file, int line, const char *name, const char *expr); + +RBIMPL_ATTR_NORETURN() +RBIMPL_ATTR_COLD() +RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 5, 6) +void rb_assert_failure_detail(const char *file, int line, const char *name, const char *expr, const char *fmt, ...); RBIMPL_SYMBOL_EXPORT_END() #ifdef RUBY_FUNCTION_NAME_STRING @@ -147,8 +153,28 @@ RBIMPL_SYMBOL_EXPORT_END() * * @param mesg The message to display. */ -#define RUBY_ASSERT_FAIL(mesg) \ +#if defined(HAVE___VA_OPT__) +# if RBIMPL_HAS_WARNING("-Wgnu-zero-variadic-macro-arguments") +/* __VA_OPT__ is to be used for the zero variadic macro arguments + * cases. */ +RBIMPL_WARNING_IGNORED(-Wgnu-zero-variadic-macro-arguments) +# endif +# define RBIMPL_VA_OPT_ARGS(...) __VA_OPT__(,) __VA_ARGS__ + +# define RUBY_ASSERT_FAIL(mesg, ...) \ + rb_assert_failure##__VA_OPT__(_detail)( \ + __FILE__, __LINE__, RBIMPL_ASSERT_FUNC, mesg RBIMPL_VA_OPT_ARGS(__VA_ARGS__)) +#elif !defined(__cplusplus) +# define RBIMPL_VA_OPT_ARGS(...) + +# define RUBY_ASSERT_FAIL(mesg, ...) \ + rb_assert_failure(__FILE__, __LINE__, RBIMPL_ASSERT_FUNC, mesg) +#else +# undef RBIMPL_VA_OPT_ARGS + +# define RUBY_ASSERT_FAIL(mesg) \ rb_assert_failure(__FILE__, __LINE__, RBIMPL_ASSERT_FUNC, mesg) +#endif /** * Asserts that the expression is truthy. If not aborts with the message. @@ -156,15 +182,25 @@ RBIMPL_SYMBOL_EXPORT_END() * @param expr What supposedly evaluates to true. * @param mesg The message to display on failure. */ -#define RUBY_ASSERT_MESG(expr, mesg) \ +#if defined(RBIMPL_VA_OPT_ARGS) +# define RUBY_ASSERT_MESG(expr, ...) \ + (RB_LIKELY(expr) ? RBIMPL_ASSERT_NOTHING : RUBY_ASSERT_FAIL(__VA_ARGS__)) +#else +# define RUBY_ASSERT_MESG(expr, mesg) \ (RB_LIKELY(expr) ? RBIMPL_ASSERT_NOTHING : RUBY_ASSERT_FAIL(mesg)) +#endif /** * A variant of #RUBY_ASSERT that does not interface with #RUBY_DEBUG. * * @copydetails #RUBY_ASSERT */ -#define RUBY_ASSERT_ALWAYS(expr) RUBY_ASSERT_MESG((expr), #expr) +#if defined(RBIMPL_VA_OPT_ARGS) +# define RUBY_ASSERT_ALWAYS(expr, ...) \ + RUBY_ASSERT_MESG(expr, #expr RBIMPL_VA_OPT_ARGS(__VA_ARGS__)) +#else +# define RUBY_ASSERT_ALWAYS(expr) RUBY_ASSERT_MESG((expr), #expr) +#endif /** * Asserts that the given expression is truthy if and only if #RUBY_DEBUG is truthy. @@ -172,9 +208,18 @@ RBIMPL_SYMBOL_EXPORT_END() * @param expr What supposedly evaluates to true. */ #if RUBY_DEBUG -# define RUBY_ASSERT(expr) RUBY_ASSERT_MESG((expr), #expr) +# if defined(RBIMPL_VA_OPT_ARGS) +# define RUBY_ASSERT(expr, ...) \ + RUBY_ASSERT_MESG((expr), #expr RBIMPL_VA_OPT_ARGS(__VA_ARGS__)) +# else +# define RUBY_ASSERT(expr) RUBY_ASSERT_MESG((expr), #expr) +# endif #else -# define RUBY_ASSERT(expr) RBIMPL_ASSERT_NOTHING +# if defined(RBIMPL_VA_OPT_ARGS) +# define RUBY_ASSERT(/* expr, */...) RBIMPL_ASSERT_NOTHING +# else +# define RUBY_ASSERT(expr) RBIMPL_ASSERT_NOTHING +# endif #endif /** @@ -187,9 +232,18 @@ RBIMPL_SYMBOL_EXPORT_END() /* Currently `RUBY_DEBUG == ! defined(NDEBUG)` is always true. There is no * difference any longer between this one and `RUBY_ASSERT`. */ #if defined(NDEBUG) -# define RUBY_ASSERT_NDEBUG(expr) RBIMPL_ASSERT_NOTHING +# if defined(RBIMPL_VA_OPT_ARGS) +# define RUBY_ASSERT_NDEBUG(/* expr, */...) RBIMPL_ASSERT_NOTHING +# else +# define RUBY_ASSERT_NDEBUG(expr) RBIMPL_ASSERT_NOTHING +# endif #else -# define RUBY_ASSERT_NDEBUG(expr) RUBY_ASSERT_MESG((expr), #expr) +# if defined(RBIMPL_VA_OPT_ARGS) +# define RUBY_ASSERT_NDEBUG(expr, ...) \ + RUBY_ASSERT_MESG((expr), #expr RBIMPL_VA_OPT_ARGS(__VA_ARGS__)) +# else +# define RUBY_ASSERT_NDEBUG(expr) RUBY_ASSERT_MESG((expr), #expr) +# endif #endif /** @@ -197,10 +251,20 @@ RBIMPL_SYMBOL_EXPORT_END() * @param mesg The message to display on failure. */ #if RUBY_DEBUG -# define RUBY_ASSERT_MESG_WHEN(cond, expr, mesg) RUBY_ASSERT_MESG((expr), (mesg)) +# if defined(RBIMPL_VA_OPT_ARGS) +# define RUBY_ASSERT_MESG_WHEN(cond, /* expr, */...) \ + RUBY_ASSERT_MESG(__VA_ARGS__) +# else +# define RUBY_ASSERT_MESG_WHEN(cond, expr, mesg) RUBY_ASSERT_MESG((expr), (mesg)) +# endif #else -# define RUBY_ASSERT_MESG_WHEN(cond, expr, mesg) \ +# if defined(RBIMPL_VA_OPT_ARGS) +# define RUBY_ASSERT_MESG_WHEN(cond, expr, ...) \ + ((cond) ? RUBY_ASSERT_MESG((expr), __VA_ARGS__) : RBIMPL_ASSERT_NOTHING) +# else +# define RUBY_ASSERT_MESG_WHEN(cond, expr, mesg) \ ((cond) ? RUBY_ASSERT_MESG((expr), (mesg)) : RBIMPL_ASSERT_NOTHING) +# endif #endif /** @@ -210,7 +274,23 @@ RBIMPL_SYMBOL_EXPORT_END() * @param cond Extra condition that shall hold for assertion to take effect. * @param expr What supposedly evaluates to true. */ -#define RUBY_ASSERT_WHEN(cond, expr) RUBY_ASSERT_MESG_WHEN((cond), (expr), #expr) +#if defined(RBIMPL_VA_OPT_ARGS) +# define RUBY_ASSERT_WHEN(cond, expr, ...) \ + RUBY_ASSERT_MESG_WHEN(cond, expr, #expr RBIMPL_VA_OPT_ARGS(__VA_ARGS__)) +#else +# define RUBY_ASSERT_WHEN(cond, expr) RUBY_ASSERT_MESG_WHEN((cond), (expr), #expr) +#endif + +/** + * A variant of #RUBY_ASSERT that asserts when either #RUBY_DEBUG or built-in + * type of `obj` is `type`. + * + * @param obj Object to check its built-in typue. + * @param type Built-in type constant, T_ARRAY, T_STRING, etc. + */ +#define RUBY_ASSERT_BUILTIN_TYPE(obj, type) \ + RUBY_ASSERT(RB_TYPE_P(obj, type), \ + "Actual type is %s", rb_builtin_type_name(BUILTIN_TYPE(obj))) /** * This is either #RUBY_ASSERT or #RBIMPL_ASSUME, depending on #RUBY_DEBUG. diff --git a/include/ruby/atomic.h b/include/ruby/atomic.h index 3eb80fbf7d..043a6a9945 100644 --- a/include/ruby/atomic.h +++ b/include/ruby/atomic.h @@ -139,6 +139,15 @@ typedef unsigned int rb_atomic_t; rbimpl_atomic_cas(&(var), (oldval), (newval)) /** + * Atomic load. This loads `var` with an atomic intrinsic and returns + * its value. + * + * @param var A variable of ::rb_atomic_t + * @return What was stored in `var`j + */ +#define RUBY_ATOMIC_LOAD(var) rbimpl_atomic_load(&(var)) + +/** * Identical to #RUBY_ATOMIC_EXCHANGE, except for the return type. * * @param var A variable of ::rb_atomic_t. @@ -280,6 +289,17 @@ typedef unsigned int rb_atomic_t; RBIMPL_CAST(rbimpl_atomic_ptr_exchange((void **)&(var), (void *)val)) /** + * Identical to #RUBY_ATOMIC_LOAD, except it expects its arguments are `void*`. + * There are cases where ::rb_atomic_t is 32bit while `void*` is 64bit. This + * should be used for size related operations to support such platforms. + * + * @param var A variable of `void*` + * @return The value of `var` (without tearing) + */ +#define RUBY_ATOMIC_PTR_LOAD(var) \ + RBIMPL_CAST(rbimpl_atomic_ptr_load((void **)&var)) + +/** * Identical to #RUBY_ATOMIC_CAS, except it expects its arguments are `void*`. * There are cases where ::rb_atomic_t is 32bit while `void*` is 64bit. This * should be used for size related operations to support such platforms. @@ -404,7 +424,7 @@ rbimpl_atomic_size_add(volatile size_t *ptr, size_t val) #elif defined(HAVE_GCC_SYNC_BUILTINS) __sync_add_and_fetch(ptr, val); -#elif defined(_WIN32) && defined(_M_AMD64) +#elif defined(_WIN64) /* Ditto for `InterlockeExchangedAdd`. */ InterlockedExchangeAdd64(ptr, val); @@ -456,13 +476,15 @@ rbimpl_atomic_size_inc(volatile size_t *ptr) #elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS) rbimpl_atomic_size_add(ptr, 1); -#elif defined(_WIN32) && defined(_M_AMD64) +#elif defined(_WIN64) InterlockedIncrement64(ptr); #elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx)) atomic_inc_ulong(ptr); #else + RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t)); + rbimpl_atomic_size_add(ptr, 1); #endif @@ -538,7 +560,7 @@ rbimpl_atomic_size_sub(volatile size_t *ptr, size_t val) #elif defined(HAVE_GCC_SYNC_BUILTINS) __sync_sub_and_fetch(ptr, val); -#elif defined(_WIN32) && defined(_M_AMD64) +#elif defined(_WIN64) const ssize_t neg = -1; InterlockedExchangeAdd64(ptr, neg * val); @@ -590,13 +612,15 @@ rbimpl_atomic_size_dec(volatile size_t *ptr) #elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS) rbimpl_atomic_size_sub(ptr, 1); -#elif defined(_WIN32) && defined(_M_AMD64) +#elif defined(_WIN64) InterlockedDecrement64(ptr); #elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx)) atomic_dec_ulong(ptr); #else + RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t)); + rbimpl_atomic_size_sub(ptr, 1); #endif @@ -688,7 +712,7 @@ rbimpl_atomic_size_exchange(volatile size_t *ptr, size_t val) #elif defined(HAVE_GCC_SYNC_BUILTINS) return __sync_lock_test_and_set(ptr, val); -#elif defined(_WIN32) && defined(_M_AMD64) +#elif defined(_WIN64) return InterlockedExchange64(ptr, val); #elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx)) @@ -749,6 +773,21 @@ rbimpl_atomic_value_exchange(volatile VALUE *ptr, VALUE val) RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) +static inline rb_atomic_t +rbimpl_atomic_load(volatile rb_atomic_t *ptr) +{ +#if 0 + +#elif defined(HAVE_GCC_ATOMIC_BUILTINS) + return __atomic_load_n(ptr, __ATOMIC_SEQ_CST); +#else + return rbimpl_atomic_fetch_add(ptr, 0); +#endif +} + +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NOALIAS() +RBIMPL_ATTR_NONNULL((1)) static inline void rbimpl_atomic_set(volatile rb_atomic_t *ptr, rb_atomic_t val) { @@ -823,7 +862,7 @@ rbimpl_atomic_size_cas(volatile size_t *ptr, size_t oldval, size_t newval) #elif defined(HAVE_GCC_SYNC_BUILTINS) return __sync_val_compare_and_swap(ptr, oldval, newval); -#elif defined(_WIN32) && defined(_M_AMD64) +#elif defined(_WIN64) return InterlockedCompareExchange64(ptr, newval, oldval); #elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx)) @@ -875,6 +914,22 @@ rbimpl_atomic_ptr_cas(void **ptr, const void *oldval, const void *newval) RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) +static inline void * +rbimpl_atomic_ptr_load(void **ptr) +{ +#if 0 + +#elif defined(HAVE_GCC_ATOMIC_BUILTINS) + return __atomic_load_n(ptr, __ATOMIC_SEQ_CST); +#else + void *val = *ptr; + return rbimpl_atomic_ptr_cas(ptr, val, val); +#endif +} + +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NOALIAS() +RBIMPL_ATTR_NONNULL((1)) static inline VALUE rbimpl_atomic_value_cas(volatile VALUE *ptr, VALUE oldval, VALUE newval) { diff --git a/include/ruby/debug.h b/include/ruby/debug.h index f95acdb17e..f7c8e6ca8d 100644 --- a/include/ruby/debug.h +++ b/include/ruby/debug.h @@ -10,6 +10,7 @@ * modify this file, provided that the conditions mentioned in the * file COPYING are met. Consult the file for details. */ +#include "ruby/internal/attr/deprecated.h" #include "ruby/internal/attr/nonnull.h" #include "ruby/internal/attr/returns_nonnull.h" #include "ruby/internal/dllexport.h" @@ -51,6 +52,25 @@ RBIMPL_ATTR_NONNULL((3)) int rb_profile_frames(int start, int limit, VALUE *buff, int *lines); /** + * Queries mysterious "frame"s of the given range. + * + * A per-thread version of rb_profile_frames(). + * Arguments and return values are the same with rb_profile_frames() with the + * exception of the first argument _thread_, which accepts the Thread to be + * profiled/queried. + * + * @param[in] thread The Ruby Thread to be profiled. + * @param[in] start Start position (0 means the topmost). + * @param[in] limit Number objects of `buff`. + * @param[out] buff Return buffer. + * @param[out] lines Return buffer. + * @return Number of objects filled into `buff`. + * @post `buff` is filled with backtrace pointers. + * @post `lines` is filled with `__LINE__` of each backtraces. + */ +int rb_profile_thread_frames(VALUE thread, int start, int limit, VALUE *buff, int *lines); + +/** * Queries the path of the passed backtrace. * * @param[in] frame What rb_profile_frames() returned. @@ -596,48 +616,157 @@ VALUE rb_tracearg_object(rb_trace_arg_t *trace_arg); /* * Postponed Job API - * rb_postponed_job_register and rb_postponed_job_register_one are - * async-signal-safe and used via SIGPROF by the "stackprof" RubyGem + * + * This API is designed to be called from contexts where it is not safe to run Ruby + * code (e.g. because they do not hold the GVL or because GC is in progress), and + * defer a callback to run in a context where it _is_ safe. The primary intended + * users of this API is for sampling profilers like the "stackprof" gem; these work + * by scheduling the periodic delivery of a SIGPROF signal, and inside the C-level + * signal handler, deferring a job to collect a Ruby backtrace when it is next safe + * to do so. + * + * Ruby maintains a small, fixed-size postponed job table. An extension using this + * API should first call `rb_postponed_job_preregister` to register a callback + * function in this table and obtain a handle of type `rb_postponed_job_handle_t` + * to it. Subsequently, the callback can be triggered by calling + * `rb_postponed_job_trigger` with that handle, or the `data` associated with the + * callback function can be changed by calling `rb_postponed_job_preregister` again. + * + * Because the postponed job table is quite small (it only has 32 entries on most + * common systems), extensions should generally only preregister one or two `func` + * values. + * + * Historically, this API provided two functions `rb_postponed_job_register` and + * `rb_postponed_job_register_one`, which claimed to be fully async-signal-safe and + * would call back the provided `func` and `data` at an appropriate time. However, + * these functions were subject to race conditions which could cause crashes when + * racing with Ruby's internal use of them. These two functions are still present, + * but are marked as deprecated and have slightly changed semantics: + * + * * rb_postponed_job_register now works like rb_postponed_job_register_one i.e. + * `func` will only be executed at most one time each time Ruby checks for + * interrupts, no matter how many times it is registered + * * They are also called with the last `data` to be registered, not the first + * (which is how rb_postponed_job_register_one previously worked) */ + /** * Type of postponed jobs. * - * @param[in,out] arg What was passed to rb_postponed_job_register(). + * @param[in,out] arg What was passed to `rb_postponed_job_preregister` */ typedef void (*rb_postponed_job_func_t)(void *arg); /** - * Registers a postponed job. + * The type of a handle returned from `rb_postponed_job_preregister` and + * passed to `rb_postponed_job_trigger` + */ +typedef unsigned int rb_postponed_job_handle_t; +#define POSTPONED_JOB_HANDLE_INVALID ((rb_postponed_job_handle_t)UINT_MAX) + +/** + * Pre-registers a func in Ruby's postponed job preregistration table, + * returning an opaque handle which can be used to trigger the job later. Generally, + * this function will be called during the initialization routine of an extension. * - * There are situations when running a ruby program is not possible. For - * instance when a program is in a signal handler; for another instance when - * the GC is busy. On such situations however, there might be needs to do - * something. We cannot but defer such operations until we are 100% sure it is - * safe to execute them. This mechanism is called postponed jobs. This - * function registers a new one. The registered job would eventually gets - * executed. + * The returned handle can be used later to call `rb_postponed_job_trigger`. This will + * cause Ruby to call back into the registered `func` with `data` at a later time, in + * a context where the GVL is held and it is safe to perform Ruby allocations. * - * @param[in] flags (Unused) reserved for future extensions. + * If the given `func` was already pre-registered, this function will overwrite the + * stored data with the newly passed data, and return the same handle instance as + * was previously returned. + * + * If this function is called concurrently with the same `func`, then the stored data + * could be the value from either call (but will definitely be one of them). + * + * If this function is called to update the data concurrently with a call to + * `rb_postponed_job_trigger` on the same handle, it's undefined whether `func` will + * be called with the old data or the new data. + * + * Although the current implementation of this function is in fact async-signal-safe and + * has defined semantics when called concurrently on the same `func`, a future Ruby + * version might require that this method be called under the GVL; thus, programs which + * aim to be forward-compatible should call this method whilst holding the GVL. + * + * @param[in] flags Unused and ignored + * @param[in] func The function to be pre-registered + * @param[in] data The data to be pre-registered + * @retval POSTPONED_JOB_HANDLE_INVALID The job table is full; this registration + * did not succeed and no further registration will do so for + * the lifetime of the program. + * @retval otherwise A handle which can be passed to `rb_postponed_job_trigger` + */ +rb_postponed_job_handle_t rb_postponed_job_preregister(unsigned int flags, rb_postponed_job_func_t func, void *data); + +/** + * Triggers a pre-registered job registered with rb_postponed_job_preregister, + * scheduling it for execution the next time the Ruby VM checks for interrupts. + * The context in which the job is called in holds the GVL and is safe to perform + * Ruby allocations within (i.e. it is not during GC). + * + * This method is async-signal-safe and can be called from any thread, at any + * time, including in signal handlers. + * + * If this method is called multiple times, Ruby will coalesce this into only + * one call to the job the next time it checks for interrupts. + * + * @params[in] h A handle returned from rb_postponed_job_preregister + */ +void rb_postponed_job_trigger(rb_postponed_job_handle_t h); + +/** + * Schedules the given `func` to be called with `data` when Ruby next checks for + * interrupts. If this function is called multiple times in between Ruby checking + * for interrupts, then `func` will be called only once with the `data` value from + * the first call to this function. + * + * Like `rb_postponed_job_trigger`, the context in which the job is called + * holds the GVL and can allocate Ruby objects. + * + * This method essentially has the same semantics as: + * + * ``` + * rb_postponed_job_trigger(rb_postponed_job_preregister(func, data)); + * ``` + * + * @note Previous versions of Ruby promised that the (`func`, `data`) pairs would + * be executed as many times as they were registered with this function; in + * reality this was always subject to race conditions and this function no + * longer provides this guarantee. Instead, multiple calls to this function + * can be coalesced into a single execution of the passed `func`, with the + * most recent `data` registered at that time passed in. + * + * @deprecated This interface implies that arbitrarily many `func`'s can be enqueued + * over the lifetime of the program, whilst in reality the registration + * slots for postponed jobs are a finite resource. This is made clearer + * by the `rb_postponed_job_preregister` and `rb_postponed_job_trigger` + * functions, and a future version of Ruby might delete this function. + * + * @param[in] flags Unused and ignored. * @param[in] func Job body. * @param[in,out] data Passed as-is to `func`. - * @retval 0 Postponed job buffer is full. Failed. - * @retval otherwise Opaque return value. - * @post The passed job is postponed. + * @retval 0 Postponed job registration table is full. Failed. + * @retval 1 Registration succeeded. + * @post The passed job will run on the next interrupt check. */ + RBIMPL_ATTR_DEPRECATED(("use rb_postponed_job_preregister and rb_postponed_job_trigger")) int rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void *data); /** - * Identical to rb_postponed_job_register_one(), except it additionally checks - * for duplicated registration. In case the passed job is already in the - * postponed job buffer this function does nothing. + * Identical to `rb_postponed_job_register` + * + * @deprecated This is deprecated for the same reason as `rb_postponed_job_register` * - * @param[in] flags (Unused) reserved for future extensions. + * @param[in] flags Unused and ignored. * @param[in] func Job body. * @param[in,out] data Passed as-is to `func`. - * @retval 0 Postponed job buffer is full. Failed. - * @retval otherwise Opaque return value. + * @retval 0 Postponed job registration table is full. Failed. + * @retval 1 Registration succeeded. + * @post The passed job will run on the next interrupt check. */ + RBIMPL_ATTR_DEPRECATED(("use rb_postponed_job_preregister and rb_postponed_job_trigger")) int rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data); /** @} */ diff --git a/include/ruby/fiber/scheduler.h b/include/ruby/fiber/scheduler.h index ad3d2d7483..8f3d383330 100644 --- a/include/ruby/fiber/scheduler.h +++ b/include/ruby/fiber/scheduler.h @@ -97,7 +97,7 @@ VALUE rb_fiber_scheduler_get(void); * current thread will call scheduler's `#close` method on finalisation * (allowing the scheduler to properly manage all non-finished fibers). * `scheduler` can be an object of any class corresponding to - * `Fiber::SchedulerInterface`. Its implementation is up to the user. + * `Fiber::Scheduler` interface. Its implementation is up to the user. * * @param[in] scheduler The scheduler to set. * @exception rb_eArgError `scheduler` does not conform the interface. diff --git a/include/ruby/internal/abi.h b/include/ruby/internal/abi.h index 8e1bbf3951..e735a67564 100644 --- a/include/ruby/internal/abi.h +++ b/include/ruby/internal/abi.h @@ -33,7 +33,7 @@ #endif #endif /* RUBY_ABI_VERSION */ -#ifdef RUBY_DLN_CHECK_ABI +#if defined(RUBY_DLN_CHECK_ABI) && !defined(RUBY_EXPORT) # ifdef __cplusplus extern "C" { diff --git a/include/ruby/internal/arithmetic/long.h b/include/ruby/internal/arithmetic/long.h index 6b8fd8ffc3..6c00dbceb7 100644 --- a/include/ruby/internal/arithmetic/long.h +++ b/include/ruby/internal/arithmetic/long.h @@ -114,11 +114,11 @@ RB_INT2FIX(long i) /* :NOTE: VALUE can be wider than long. As j being unsigned, 2j+1 is fully * defined. Also it can be compiled into a single LEA instruction. */ - const unsigned long j = i; + const unsigned long j = RBIMPL_CAST((unsigned long)i); const unsigned long k = (j << 1) + RUBY_FIXNUM_FLAG; - const long l = k; + const long l = RBIMPL_CAST((long)k); const SIGNED_VALUE m = l; /* Sign extend */ - const VALUE n = m; + const VALUE n = RBIMPL_CAST((VALUE)m); RBIMPL_ASSERT_OR_ASSUME(RB_FIXNUM_P(n)); return n; @@ -166,7 +166,7 @@ rbimpl_fix2long_by_idiv(VALUE x) /* :NOTE: VALUE can be wider than long. (x-1)/2 never overflows because * RB_FIXNUM_P(x) holds. Also it has no portability issue like y>>1 * below. */ - const SIGNED_VALUE y = x - RUBY_FIXNUM_FLAG; + const SIGNED_VALUE y = RBIMPL_CAST((SIGNED_VALUE)(x - RUBY_FIXNUM_FLAG)); const SIGNED_VALUE z = y / 2; const long w = RBIMPL_CAST((long)z); @@ -193,7 +193,7 @@ rbimpl_fix2long_by_shift(VALUE x) /* :NOTE: VALUE can be wider than long. If right shift is arithmetic, this * is noticeably faster than above. */ - const SIGNED_VALUE y = x; + const SIGNED_VALUE y = RBIMPL_CAST((SIGNED_VALUE)x); const SIGNED_VALUE z = y >> 1; const long w = RBIMPL_CAST((long)z); @@ -252,7 +252,7 @@ static inline unsigned long rb_fix2ulong(VALUE x) { RBIMPL_ASSERT_OR_ASSUME(RB_FIXNUM_P(x)); - return rb_fix2long(x); + return RBIMPL_CAST((unsigned long)rb_fix2long(x)); } /** @@ -323,7 +323,7 @@ static inline VALUE rb_ulong2num_inline(unsigned long v) { if (RB_POSFIXABLE(v)) - return RB_LONG2FIX(v); + return RB_LONG2FIX(RBIMPL_CAST((long)v)); else return rb_uint2big(v); } diff --git a/include/ruby/internal/arithmetic/long_long.h b/include/ruby/internal/arithmetic/long_long.h index 65dec8729d..aab455c830 100644 --- a/include/ruby/internal/arithmetic/long_long.h +++ b/include/ruby/internal/arithmetic/long_long.h @@ -127,7 +127,7 @@ static inline unsigned LONG_LONG rb_num2ull_inline(VALUE x) { if (RB_FIXNUM_P(x)) - return RB_FIX2LONG(x); + return RBIMPL_CAST((unsigned LONG_LONG)RB_FIX2LONG(x)); else return rb_num2ull(x); } diff --git a/include/ruby/internal/arithmetic/st_data_t.h b/include/ruby/internal/arithmetic/st_data_t.h index 3bff4ffc0b..91776b3fce 100644 --- a/include/ruby/internal/arithmetic/st_data_t.h +++ b/include/ruby/internal/arithmetic/st_data_t.h @@ -58,7 +58,7 @@ RBIMPL_ATTR_ARTIFICIAL() static inline VALUE RB_ST2FIX(st_data_t i) { - SIGNED_VALUE x = i; + SIGNED_VALUE x = RBIMPL_CAST((SIGNED_VALUE)i); if (x >= 0) { x &= RUBY_FIXNUM_MAX; @@ -69,7 +69,7 @@ RB_ST2FIX(st_data_t i) RBIMPL_ASSERT_OR_ASSUME(RB_FIXABLE(x)); unsigned long y = RBIMPL_CAST((unsigned long)x); - return RB_LONG2FIX(y); + return RB_LONG2FIX(RBIMPL_CAST((long)y)); } #endif /* RBIMPL_ARITHMETIC_ST_DATA_T_H */ diff --git a/include/ruby/internal/attr/noexcept.h b/include/ruby/internal/attr/noexcept.h index ea3001df2a..7c3f92f1e7 100644 --- a/include/ruby/internal/attr/noexcept.h +++ b/include/ruby/internal/attr/noexcept.h @@ -54,7 +54,7 @@ * get smarter and smarter. Today they can infer if it actually throws * or not without any annotations by humans (correct me if I'm wrong). * - * - When an inline function attributed `noexcepr` actually _does_ throw an + * - When an inline function attributed `noexcept` actually _does_ throw an * exception: they have to call `std::terminate` then (C++ standard * mandates so). This means exception handling routines are actually * enforced, not omitted. This doesn't impact runtime performance (The diff --git a/include/ruby/internal/core/rarray.h b/include/ruby/internal/core/rarray.h index 9670f93d0c..90690fe794 100644 --- a/include/ruby/internal/core/rarray.h +++ b/include/ruby/internal/core/rarray.h @@ -36,18 +36,6 @@ #include "ruby/assert.h" /** - * @private - * @warning Do not touch this macro. - * @warning It is an implementation detail. - * @warning The value of this macro must match for ruby itself and all - * extension libraries, otherwise serious memory corruption shall - * occur. - */ -#ifndef USE_TRANSIENT_HEAP -# define USE_TRANSIENT_HEAP 1 -#endif - -/** * Convenient casting macro. * * @param obj An object, which is in fact an ::RArray. @@ -59,15 +47,9 @@ #define RARRAY_EMBED_LEN_MASK RARRAY_EMBED_LEN_MASK #define RARRAY_EMBED_LEN_MAX RARRAY_EMBED_LEN_MAX #define RARRAY_EMBED_LEN_SHIFT RARRAY_EMBED_LEN_SHIFT -#if USE_TRANSIENT_HEAP -# define RARRAY_TRANSIENT_FLAG RARRAY_TRANSIENT_FLAG -#else -# define RARRAY_TRANSIENT_FLAG 0 -#endif /** @endcond */ #define RARRAY_LEN rb_array_len /**< @alias{rb_array_len} */ #define RARRAY_CONST_PTR rb_array_const_ptr /**< @alias{rb_array_const_ptr} */ -#define RARRAY_CONST_PTR_TRANSIENT rb_array_const_ptr_transient /**< @alias{rb_array_const_ptr_transient} */ /** @cond INTERNAL_MACRO */ #if defined(__fcc__) || defined(__fcc_version) || \ @@ -80,7 +62,6 @@ #define RARRAY_EMBED_LEN RARRAY_EMBED_LEN #define RARRAY_LENINT RARRAY_LENINT -#define RARRAY_TRANSIENT_P RARRAY_TRANSIENT_P #define RARRAY_ASET RARRAY_ASET #define RARRAY_PTR RARRAY_PTR /** @endcond */ @@ -132,24 +113,6 @@ enum ruby_rarray_flags { */ RARRAY_EMBED_LEN_MASK = RUBY_FL_USER9 | RUBY_FL_USER8 | RUBY_FL_USER7 | RUBY_FL_USER6 | RUBY_FL_USER5 | RUBY_FL_USER4 | RUBY_FL_USER3 - -#if USE_TRANSIENT_HEAP - , - - /** - * This flag has something to do with an array's "transiency". A transient - * array is an array of young generation (of generational GC), who stores - * its elements inside of dedicated memory pages called a transient heap. - * Not every young generation share that storage scheme, but elder - * generations must no join. - * - * @internal - * - * 3rd parties must not be aware that there even is more than one way to - * store array elements. It was a bad idea to expose this to them. - */ - RARRAY_TRANSIENT_FLAG = RUBY_FL_USER13 -#endif }; /** @@ -249,16 +212,6 @@ VALUE *rb_ary_ptr_use_start(VALUE ary); */ void rb_ary_ptr_use_end(VALUE a); -#if USE_TRANSIENT_HEAP -/** - * Destructively converts an array of transient backend into ordinal one. - * - * @param[out] a An object of ::RArray. - * @pre `a` must be a transient array. - * @post `a` gets out of transient heap, destructively. - */ -void rb_ary_detransient(VALUE a); -#endif RBIMPL_SYMBOL_EXPORT_END() RBIMPL_ATTR_PURE_UNLESS_DEBUG() @@ -331,33 +284,6 @@ RARRAY_LENINT(VALUE ary) } RBIMPL_ATTR_PURE_UNLESS_DEBUG() -RBIMPL_ATTR_ARTIFICIAL() -/** - * Queries if the array is a transient array. - * - * @param[in] ary Array in question. - * @retval true Yes it is. - * @retval false No it isn't. - * @pre `ary` must be an instance of ::RArray. - * - * @internal - * - * @shyouhei doesn't understand the benefit of this function called from - * extension libraries. - */ -static inline bool -RARRAY_TRANSIENT_P(VALUE ary) -{ - RBIMPL_ASSERT_TYPE(ary, RUBY_T_ARRAY); - -#if USE_TRANSIENT_HEAP - return RB_FL_ANY_RAW(ary, RARRAY_TRANSIENT_FLAG); -#else - return false; -#endif -} - -RBIMPL_ATTR_PURE_UNLESS_DEBUG() /** * @private * @@ -368,7 +294,7 @@ RBIMPL_ATTR_PURE_UNLESS_DEBUG() * @return Its backend storage. */ static inline const VALUE * -rb_array_const_ptr_transient(VALUE a) +rb_array_const_ptr(VALUE a) { RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY); @@ -380,102 +306,21 @@ rb_array_const_ptr_transient(VALUE a) } } -#if ! USE_TRANSIENT_HEAP -RBIMPL_ATTR_PURE_UNLESS_DEBUG() -#endif -/** - * @private - * - * This is an implementation detail of RARRAY_PTR(). People do not use it - * directly. - * - * @param[in] a An object of ::RArray. - * @return Its backend storage. - * @post `a` is not a transient array. - */ -static inline const VALUE * -rb_array_const_ptr(VALUE a) -{ - RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY); - -#if USE_TRANSIENT_HEAP - if (RARRAY_TRANSIENT_P(a)) { - rb_ary_detransient(a); - } -#endif - return rb_array_const_ptr_transient(a); -} - -/** - * @private - * - * This is an implementation detail of #RARRAY_PTR_USE. People do not use it - * directly. - * - * @param[in] a An object of ::RArray. - * @param[in] allow_transient Whether `a` can be transient or not. - * @return Its backend storage. - * @post `a` is not a transient array unless `allow_transient`. - */ -static inline VALUE * -rb_array_ptr_use_start(VALUE a, - RBIMPL_ATTR_MAYBE_UNUSED() - int allow_transient) -{ - RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY); - -#if USE_TRANSIENT_HEAP - if (!allow_transient) { - if (RARRAY_TRANSIENT_P(a)) { - rb_ary_detransient(a); - } - } -#endif - - return rb_ary_ptr_use_start(a); -} - -/** - * @private - * - * This is an implementation detail of #RARRAY_PTR_USE. People do not use it - * directly. - * - * @param[in] a An object of ::RArray. - * @param[in] allow_transient Whether `a` can be transient or not. - */ -static inline void -rb_array_ptr_use_end(VALUE a, - RBIMPL_ATTR_MAYBE_UNUSED() - int allow_transient) -{ - RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY); - rb_ary_ptr_use_end(a); -} - /** * @private * * This is an implementation detail of #RARRAY_PTR_USE. People do not use it * directly. */ -#define RBIMPL_RARRAY_STMT(flag, ary, var, expr) do { \ +#define RBIMPL_RARRAY_STMT(ary, var, expr) do { \ RBIMPL_ASSERT_TYPE((ary), RUBY_T_ARRAY); \ const VALUE rbimpl_ary = (ary); \ - VALUE *var = rb_array_ptr_use_start(rbimpl_ary, (flag)); \ + VALUE *var = rb_ary_ptr_use_start(rbimpl_ary); \ expr; \ - rb_array_ptr_use_end(rbimpl_ary, (flag)); \ + rb_ary_ptr_use_end(rbimpl_ary); \ } while (0) /** - * @private - * - * This is an implementation detail of #RARRAY_PTR_USE. People do not use it - * directly. - */ -#define RARRAY_PTR_USE_END(a) rb_array_ptr_use_end(a, 0) - -/** * Declares a section of code where raw pointers are used. In case you need to * touch the raw C array instead of polite CAPIs, then that operation shall be * wrapped using this macro. @@ -501,21 +346,11 @@ rb_array_ptr_use_end(VALUE a, * them use it... Maybe some transition path can be implemented later. */ #define RARRAY_PTR_USE(ary, ptr_name, expr) \ - RBIMPL_RARRAY_STMT(0, ary, ptr_name, expr) - -/** - * Identical to #RARRAY_PTR_USE, except the pointer can be a transient one. - * - * @param ary An object of ::RArray. - * @param ptr_name A variable name which points the C array in `expr`. - * @param expr The expression that touches `ptr_name`. - */ -#define RARRAY_PTR_USE_TRANSIENT(ary, ptr_name, expr) \ - RBIMPL_RARRAY_STMT(1, ary, ptr_name, expr) + RBIMPL_RARRAY_STMT(ary, ptr_name, expr) /** * Wild use of a C pointer. This function accesses the backend storage - * directly. This is slower than #RARRAY_PTR_USE_TRANSIENT. It exercises + * directly. This is slower than #RARRAY_PTR_USE. It exercises * extra manoeuvres to protect our generational GC. Use of this function is * considered archaic. Use a modern way instead. * @@ -550,7 +385,7 @@ RARRAY_PTR(VALUE ary) static inline void RARRAY_ASET(VALUE ary, long i, VALUE v) { - RARRAY_PTR_USE_TRANSIENT(ary, ptr, + RARRAY_PTR_USE(ary, ptr, RB_OBJ_WRITE(ary, &ptr[i], v)); } @@ -565,6 +400,6 @@ RARRAY_ASET(VALUE ary, long i, VALUE v) * remains as it is due to that. If we could warn such usages we can set a * transition path, but currently no way is found to do so. */ -#define RARRAY_AREF(a, i) RARRAY_CONST_PTR_TRANSIENT(a)[i] +#define RARRAY_AREF(a, i) RARRAY_CONST_PTR(a)[i] #endif /* RBIMPL_RARRAY_H */ diff --git a/include/ruby/internal/core/rbasic.h b/include/ruby/internal/core/rbasic.h index 4617f743a7..a1477e2600 100644 --- a/include/ruby/internal/core/rbasic.h +++ b/include/ruby/internal/core/rbasic.h @@ -56,22 +56,20 @@ enum ruby_rvalue_flags { }; /** - * Ruby's object's, base components. Every single ruby objects have them in - * common. + * Ruby object's base components. All Ruby objects have them in common. */ struct RUBY_ALIGNAS(SIZEOF_VALUE) RBasic { /** - * Per-object flags. Each ruby objects have their own characteristics - * apart from their classes. For instance whether an object is frozen or - * not is not controlled by its class. This is where such properties are - * stored. + * Per-object flags. Each Ruby object has its own characteristics apart + * from its class. For instance, whether an object is frozen or not is not + * controlled by its class. This is where such properties are stored. * * @see enum ::ruby_fl_type * - * @note This is ::VALUE rather than an enum for alignment purpose. Back + * @note This is ::VALUE rather than an enum for alignment purposes. Back * in the 1990s there were no such thing like `_Alignas` in C. */ VALUE flags; @@ -79,10 +77,10 @@ RBasic { /** * Class of an object. Every object has its class. Also, everything is an * object in Ruby. This means classes are also objects. Classes have - * their own classes, classes of classes have their classes, too ... and - * it recursively continues forever. + * their own classes, classes of classes have their classes too, and it + * recursively continues forever. * - * Also note the `const` qualifier. In ruby an object cannot "change" its + * Also note the `const` qualifier. In Ruby, an object cannot "change" its * class. */ const VALUE klass; diff --git a/include/ruby/internal/core/rmatch.h b/include/ruby/internal/core/rmatch.h index 2d2fd897f5..a528c2999e 100644 --- a/include/ruby/internal/core/rmatch.h +++ b/include/ruby/internal/core/rmatch.h @@ -68,7 +68,7 @@ struct rmatch_offset { }; /** Represents a match. */ -struct rmatch { +struct rb_matchext_struct { /** * "Registers" of a match. This is a quasi-opaque struct that holds * execution result of a match. Roughly resembles `&~`. @@ -82,6 +82,8 @@ struct rmatch { int char_offset_num_allocated; }; +typedef struct rb_matchext_struct rb_matchext_t; + /** * Regular expression execution context. When a regular expression "matches" * to a string, it generates capture groups etc. This struct holds that info. @@ -102,16 +104,13 @@ struct RMatch { VALUE str; /** - * The result of this match. - */ - struct rmatch *rmatch; - - /** * The expression of this match. */ VALUE regexp; /* RRegexp */ }; +#define RMATCH_EXT(m) ((rb_matchext_t *)((char *)(m) + sizeof(struct RMatch))) + RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** @@ -139,8 +138,7 @@ static inline struct re_registers * RMATCH_REGS(VALUE match) { RBIMPL_ASSERT_TYPE(match, RUBY_T_MATCH); - RBIMPL_ASSERT_OR_ASSUME(RMATCH(match)->rmatch != NULL); - return &RMATCH(match)->rmatch->regs; + return &RMATCH_EXT(match)->regs; } #endif /* RBIMPL_RMATCH_H */ diff --git a/include/ruby/internal/core/robject.h b/include/ruby/internal/core/robject.h index dc1faad89f..c2bcae6306 100644 --- a/include/ruby/internal/core/robject.h +++ b/include/ruby/internal/core/robject.h @@ -119,13 +119,6 @@ struct RObject { } as; }; -/* Offsets for YJIT */ -#ifndef __cplusplus -static const int32_t ROBJECT_OFFSET_AS_HEAP_IVPTR = offsetof(struct RObject, as.heap.ivptr); -static const int32_t ROBJECT_OFFSET_AS_HEAP_IV_INDEX_TBL = offsetof(struct RObject, as.heap.iv_index_tbl); -static const int32_t ROBJECT_OFFSET_AS_ARY = offsetof(struct RObject, as.ary); -#endif - RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** diff --git a/include/ruby/internal/core/rstring.h b/include/ruby/internal/core/rstring.h index 777fe6dab1..0bca74e688 100644 --- a/include/ruby/internal/core/rstring.h +++ b/include/ruby/internal/core/rstring.h @@ -43,7 +43,6 @@ /** @cond INTERNAL_MACRO */ #define RSTRING_NOEMBED RSTRING_NOEMBED #define RSTRING_FSTR RSTRING_FSTR -#define RSTRING_EMBED_LEN RSTRING_EMBED_LEN #define RSTRING_LEN RSTRING_LEN #define RSTRING_LENINT RSTRING_LENINT #define RSTRING_PTR RSTRING_PTR @@ -199,6 +198,13 @@ struct RString { /** Basic part, including flags and class. */ struct RBasic basic; + /** + * Length of the string, not including terminating NUL character. + * + * @note This is in bytes. + */ + long len; + /** String's specific fields. */ union { @@ -207,14 +213,6 @@ struct RString { * pattern. */ struct { - - /** - * Length of the string, not including terminating NUL character. - * - * @note This is in bytes. - */ - long len; - /** * Pointer to the contents of the string. In the old days each * string had dedicated memory regions. That is no longer true @@ -245,7 +243,6 @@ struct RString { /** Embedded contents. */ struct { - long len; /* This is a length 1 array because: * 1. GCC has a bug that does not optimize C flexible array members * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452) @@ -364,24 +361,12 @@ RBIMPL_ATTR_ARTIFICIAL() * * @param[in] str String in question. * @return Its length, in bytes. - * @pre `str` must be an instance of ::RString, and must has its - * ::RSTRING_NOEMBED flag off. - * - * @internal - * - * This was a macro before. It was inevitable to be public, since macros are - * global constructs. But should it be forever? Now that it is a function, - * @shyouhei thinks it could just be eliminated, hidden into implementation - * details. + * @pre `str` must be an instance of ::RString. */ static inline long -RSTRING_EMBED_LEN(VALUE str) +RSTRING_LEN(VALUE str) { - RBIMPL_ASSERT_TYPE(str, RUBY_T_STRING); - RBIMPL_ASSERT_OR_ASSUME(! RB_FL_ANY_RAW(str, RSTRING_NOEMBED)); - - long f = RSTRING(str)->as.embed.len; - return f; + return RSTRING(str)->len; } RBIMPL_WARNING_PUSH() @@ -411,7 +396,7 @@ rbimpl_rstring_getmem(VALUE str) else { /* Expecting compilers to optimize this on-stack struct away. */ struct RString retval; - retval.as.heap.len = RSTRING_EMBED_LEN(str); + retval.len = RSTRING_LEN(str); retval.as.heap.ptr = RSTRING(str)->as.embed.ary; return retval; } @@ -419,21 +404,6 @@ rbimpl_rstring_getmem(VALUE str) RBIMPL_WARNING_POP() -RBIMPL_ATTR_PURE_UNLESS_DEBUG() -RBIMPL_ATTR_ARTIFICIAL() -/** - * Queries the length of the string. - * - * @param[in] str String in question. - * @return Its length, in bytes. - * @pre `str` must be an instance of ::RString. - */ -static inline long -RSTRING_LEN(VALUE str) -{ - return rbimpl_rstring_getmem(str).as.heap.len; -} - RBIMPL_ATTR_ARTIFICIAL() /** * Queries the contents pointer of the string. @@ -447,13 +417,9 @@ RSTRING_PTR(VALUE str) { char *ptr = rbimpl_rstring_getmem(str).as.heap.ptr; - if (RB_UNLIKELY(! ptr)) { + if (RUBY_DEBUG && RB_UNLIKELY(! ptr)) { /* :BEWARE: @shyouhei thinks that currently, there are rooms for this - * function to return NULL. In the 20th century that was a pointless - * concern. However struct RString can hold fake strings nowadays. It - * seems no check against NULL are exercised around handling of them - * (one of such usages is located in marshal.c, which scares - * @shyouhei). Better check here for maximum safety. + * function to return NULL. Better check here for maximum safety. * * Also, this is not rb_warn() because RSTRING_PTR() can be called * during GC (see what obj_info() does). rb_warn() needs to allocate @@ -477,12 +443,12 @@ RSTRING_END(VALUE str) { struct RString buf = rbimpl_rstring_getmem(str); - if (RB_UNLIKELY(! buf.as.heap.ptr)) { + if (RUBY_DEBUG && RB_UNLIKELY(! buf.as.heap.ptr)) { /* Ditto. */ rb_debug_rstring_null_ptr("RSTRING_END"); } - return &buf.as.heap.ptr[buf.as.heap.len]; + return &buf.as.heap.ptr[buf.len]; } RBIMPL_ATTR_ARTIFICIAL() @@ -516,7 +482,7 @@ RSTRING_LENINT(VALUE str) __extension__ ({ \ struct RString rbimpl_str = rbimpl_rstring_getmem(str); \ (ptrvar) = rbimpl_str.as.heap.ptr; \ - (lenvar) = rbimpl_str.as.heap.len; \ + (lenvar) = rbimpl_str.len; \ }) #else # define RSTRING_GETMEM(str, ptrvar, lenvar) \ diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h index 1abf965305..6c19576c20 100644 --- a/include/ruby/internal/core/rtypeddata.h +++ b/include/ruby/internal/core/rtypeddata.h @@ -114,6 +114,8 @@ #define RUBY_TYPED_PROMOTED1 RUBY_TYPED_PROMOTED1 /** @endcond */ +#define TYPED_DATA_EMBEDDED 2 + /** * @private * @@ -137,6 +139,8 @@ rbimpl_typeddata_flags { */ RUBY_TYPED_FREE_IMMEDIATELY = 1, + RUBY_TYPED_EMBEDDABLE = 2, + /** * This flag has something to do with Ractor. Multiple Ractors run without * protecting each other. Sharing an object among Ractors is basically @@ -173,10 +177,9 @@ rbimpl_typeddata_flags { RUBY_TYPED_WB_PROTECTED = RUBY_FL_WB_PROTECTED, /* THIS FLAG DEPENDS ON Ruby version */ /** - * This flag is mysterious. It seems nobody is currently using it. The - * intention of this flag is also unclear. We need further investigations. + * This flag no longer in use */ - RUBY_TYPED_PROMOTED1 = RUBY_FL_PROMOTED1, /* THIS FLAG DEPENDS ON Ruby version */ + RUBY_TYPED_UNUSED = RUBY_FL_UNUSED6, /** * This flag determines whether marking and compaction should be carried out @@ -461,7 +464,7 @@ RBIMPL_SYMBOL_EXPORT_END() */ #define TypedData_Make_Struct0(result, klass, type, size, data_type, sval) \ VALUE result = rb_data_typed_object_zalloc(klass, size, data_type); \ - (sval) = RBIMPL_CAST((type *)RTYPEDDATA_DATA(result)); \ + (sval) = (type *)RTYPEDDATA_GET_DATA(result); \ RBIMPL_CAST(/*suppress unused variable warnings*/(void)(sval)) /** @@ -512,6 +515,36 @@ RBIMPL_SYMBOL_EXPORT_END() #define TypedData_Get_Struct(obj,type,data_type,sval) \ ((sval) = RBIMPL_CAST((type *)rb_check_typeddata((obj), (data_type)))) +static inline bool +RTYPEDDATA_EMBEDDED_P(VALUE obj) +{ +#if RUBY_DEBUG + if (RB_UNLIKELY(!RB_TYPE_P(obj, RUBY_T_DATA))) { + Check_Type(obj, RUBY_T_DATA); + RBIMPL_UNREACHABLE_RETURN(false); + } +#endif + + return RTYPEDDATA(obj)->typed_flag & TYPED_DATA_EMBEDDED; +} + +static inline void * +RTYPEDDATA_GET_DATA(VALUE obj) +{ +#if RUBY_DEBUG + if (RB_UNLIKELY(!RB_TYPE_P(obj, RUBY_T_DATA))) { + Check_Type(obj, RUBY_T_DATA); + RBIMPL_UNREACHABLE_RETURN(false); + } +#endif + + /* We reuse the data pointer in embedded TypedData. We can't use offsetof + * since RTypedData a non-POD type in C++. */ + const size_t embedded_typed_data_size = sizeof(struct RTypedData) - sizeof(void *); + + return RTYPEDDATA_EMBEDDED_P(obj) ? (char *)obj + embedded_typed_data_size : RTYPEDDATA(obj)->data; +} + RBIMPL_ATTR_PURE() RBIMPL_ATTR_ARTIFICIAL() /** @@ -528,7 +561,8 @@ RBIMPL_ATTR_ARTIFICIAL() static inline bool rbimpl_rtypeddata_p(VALUE obj) { - return RTYPEDDATA(obj)->typed_flag == 1; + VALUE typed_flag = RTYPEDDATA(obj)->typed_flag; + return typed_flag != 0 && typed_flag <= 3; } RBIMPL_ATTR_PURE_UNLESS_DEBUG() diff --git a/include/ruby/internal/encoding/encoding.h b/include/ruby/internal/encoding/encoding.h index dc3e0151f0..a58f9f2b15 100644 --- a/include/ruby/internal/encoding/encoding.h +++ b/include/ruby/internal/encoding/encoding.h @@ -28,6 +28,7 @@ #include "ruby/internal/attr/pure.h" #include "ruby/internal/attr/returns_nonnull.h" #include "ruby/internal/dllexport.h" +#include "ruby/internal/encoding/coderange.h" #include "ruby/internal/value.h" #include "ruby/internal/core/rbasic.h" #include "ruby/internal/fl_type.h" @@ -79,7 +80,7 @@ enum ruby_encoding_consts { static inline void RB_ENCODING_SET_INLINED(VALUE obj, int encindex) { - VALUE f = /* upcast */ encindex; + VALUE f = /* upcast */ RBIMPL_CAST((VALUE)encindex); f <<= RUBY_ENCODING_SHIFT; RB_FL_UNSET_RAW(obj, RUBY_ENCODING_MASK); diff --git a/include/ruby/internal/encoding/string.h b/include/ruby/internal/encoding/string.h index 6ed7ca1c90..2b9dfe4f31 100644 --- a/include/ruby/internal/encoding/string.h +++ b/include/ruby/internal/encoding/string.h @@ -30,7 +30,7 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() /** - * Identical to rb_enc_str_new(), except it additionally takes an encoding. + * Identical to rb_str_new(), except it additionally takes an encoding. * * @param[in] ptr A memory region of `len` bytes length. * @param[in] len Length of `ptr`, in bytes, not including the diff --git a/include/ruby/internal/error.h b/include/ruby/internal/error.h index 1ce9a30e6f..cd37f4461a 100644 --- a/include/ruby/internal/error.h +++ b/include/ruby/internal/error.h @@ -481,7 +481,7 @@ VALUE *rb_ruby_debug_ptr(void); */ #define ruby_debug (*rb_ruby_debug_ptr()) -/* reports if `-W' specified */ +/* reports if $VERBOSE is true */ RBIMPL_ATTR_NONNULL((1)) RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2) /** @@ -496,7 +496,8 @@ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2) * default, the method just emits its passed contents to ::rb_stderr using * rb_io_write(). * - * @note This function is affected by the `-W` flag. + * @note This function is affected by the value of $VERBOSE, it does + * nothing unless $VERBOSE is true. * @param[in] fmt Format specifier string compatible with rb_sprintf(). * * @internal @@ -521,7 +522,7 @@ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 4) * Issues a compile-time warning that happens at `__file__:__line__`. Purpose * of this function being exposed to CAPI is unclear. * - * @note This function is affected by the `-W` flag. + * @note This function is affected by the value of $VERBOSE. * @param[in] file The path corresponding to Ruby level `__FILE__`. * @param[in] line The number corresponding to Ruby level `__LINE__`. * @param[in] fmt Format specifier string compatible with rb_sprintf(). @@ -534,19 +535,20 @@ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2) * Identical to rb_sys_fail(), except it does not raise an exception to render * a warning instead. * - * @note This function is affected by the `-W` flag. + * @note This function is affected by the value of $VERBOSE. * @param[in] fmt Format specifier string compatible with rb_sprintf(). */ void rb_sys_warning(const char *fmt, ...); -/* reports always */ +/* reports if $VERBOSE is not nil (so if it is true or false) */ RBIMPL_ATTR_COLD() RBIMPL_ATTR_NONNULL((1)) RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2) /** - * Identical to rb_warning(), except it reports always regardless of runtime - * `-W` flag. + * Identical to rb_warning(), except it reports unless $VERBOSE is nil. * + * @note This function is affected by the value of $VERBOSE, it does + * nothing if $VERBOSE is nil. * @param[in] fmt Format specifier string compatible with rb_sprintf(). */ void rb_warn(const char *fmt, ...); @@ -555,8 +557,7 @@ RBIMPL_ATTR_COLD() RBIMPL_ATTR_NONNULL((2)) RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3) /** - * Identical to rb_category_warning(), except it reports always regardless of - * runtime `-W` flag. + * Identical to rb_category_warning(), except it reports unless $VERBOSE is nil. * * @param[in] cat Category e.g. deprecated. * @param[in] fmt Format specifier string compatible with rb_sprintf(). @@ -566,8 +567,7 @@ void rb_category_warn(rb_warning_category_t cat, const char *fmt, ...); RBIMPL_ATTR_NONNULL((1, 3)) RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 4) /** - * Identical to rb_compile_warning(), except it reports always regardless of - * runtime `-W` flag. + * Identical to rb_compile_warning(), except it reports unless $VERBOSE is nil. * * @param[in] file The path corresponding to Ruby level `__FILE__`. * @param[in] line The number corresponding to Ruby level `__LINE__`. diff --git a/include/ruby/internal/event.h b/include/ruby/internal/event.h index 04b137a193..1d194ed618 100644 --- a/include/ruby/internal/event.h +++ b/include/ruby/internal/event.h @@ -23,6 +23,10 @@ #include "ruby/internal/dllexport.h" #include "ruby/internal/value.h" +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + /* These macros are not enums because they are wider than int.*/ /** @@ -54,6 +58,7 @@ #define RUBY_EVENT_THREAD_END 0x0800 /**< Encountered an end of a thread. */ #define RUBY_EVENT_FIBER_SWITCH 0x1000 /**< Encountered a `Fiber#yield`. */ #define RUBY_EVENT_SCRIPT_COMPILED 0x2000 /**< Encountered an `eval`. */ +#define RUBY_EVENT_RESCUE 0x4000 /**< Encountered a `rescue` statement. */ #define RUBY_EVENT_TRACEPOINT_ALL 0xffff /**< Bitmask of extended events. */ /** @} */ diff --git a/include/ruby/internal/fl_type.h b/include/ruby/internal/fl_type.h index 555f23a5a5..0a05166784 100644 --- a/include/ruby/internal/fl_type.h +++ b/include/ruby/internal/fl_type.h @@ -57,8 +57,7 @@ #define FL_SINGLETON RBIMPL_CAST((VALUE)RUBY_FL_SINGLETON) /**< @old{RUBY_FL_SINGLETON} */ #define FL_WB_PROTECTED RBIMPL_CAST((VALUE)RUBY_FL_WB_PROTECTED) /**< @old{RUBY_FL_WB_PROTECTED} */ -#define FL_PROMOTED0 RBIMPL_CAST((VALUE)RUBY_FL_PROMOTED0) /**< @old{RUBY_FL_PROMOTED0} */ -#define FL_PROMOTED1 RBIMPL_CAST((VALUE)RUBY_FL_PROMOTED1) /**< @old{RUBY_FL_PROMOTED1} */ +#define FL_PROMOTED RBIMPL_CAST((VALUE)RUBY_FL_PROMOTED) /**< @old{RUBY_FL_PROMOTED} */ #define FL_FINALIZE RBIMPL_CAST((VALUE)RUBY_FL_FINALIZE) /**< @old{RUBY_FL_FINALIZE} */ #define FL_TAINT RBIMPL_CAST((VALUE)RUBY_FL_TAINT) /**< @old{RUBY_FL_TAINT} */ #define FL_SHAREABLE RBIMPL_CAST((VALUE)RUBY_FL_SHAREABLE) /**< @old{RUBY_FL_SHAREABLE} */ @@ -111,13 +110,6 @@ #define RB_OBJ_FREEZE_RAW RB_OBJ_FREEZE_RAW #define RB_OBJ_FROZEN RB_OBJ_FROZEN #define RB_OBJ_FROZEN_RAW RB_OBJ_FROZEN_RAW -#define RB_OBJ_INFECT RB_OBJ_INFECT -#define RB_OBJ_INFECT_RAW RB_OBJ_INFECT_RAW -#define RB_OBJ_TAINT RB_OBJ_TAINT -#define RB_OBJ_TAINTABLE RB_OBJ_TAINTABLE -#define RB_OBJ_TAINTED RB_OBJ_TAINTED -#define RB_OBJ_TAINTED_RAW RB_OBJ_TAINTED_RAW -#define RB_OBJ_TAINT_RAW RB_OBJ_TAINT_RAW #define RB_OBJ_UNTRUST RB_OBJ_TAINT #define RB_OBJ_UNTRUSTED RB_OBJ_TAINTED /** @endcond */ @@ -207,12 +199,15 @@ ruby_fl_type { RUBY_FL_WB_PROTECTED = (1<<5), /** - * This flag has something to do with our garbage collector. These days - * ruby objects are "generational". There are those who are young and - * those who are old. Young objects are prone to die; monitored relatively - * extensively by the garbage collector. OTOH old objects tend to live - * longer. They are relatively rarely considered. This flag is set when a - * object experienced promotion i.e. survived a garbage collection. + * Ruby objects are "generational". There are young objects & old objects. + * Young objects are prone to die & monitored relatively extensively by the + * garbage collector. Old objects tend to live longer & are monitored less + * frequently. When an object survives a GC, its age is incremented. When + * age is equal to RVALUE_OLD_AGE, the object becomes Old. This flag is set + * when an object becomes old, and is used by the write barrier to check if + * an old object should be considered for marking more frequently - as old + * objects that have references added between major GCs need to be remarked + * to prevent the referred object being mistakenly swept. * * @internal * @@ -220,41 +215,14 @@ ruby_fl_type { * 3rd parties. It must be an implementation detail that they should never * know. Might better be hidden. */ - RUBY_FL_PROMOTED0 = (1<<5), + RUBY_FL_PROMOTED = (1<<5), /** - * This flag has something to do with our garbage collector. These days - * ruby objects are "generational". There are those who are young and - * those who are old. Young objects are prone to die; monitored relatively - * extensively by the garbage collector. OTOH old objects tend to live - * longer. They are relatively rarely considered. This flag is set when a - * object experienced two promotions i.e. survived garbage collections - * twice. + * This flag is no longer in use * * @internal - * - * But honestly, @shyouhei doesn't think this flag should be visible from - * 3rd parties. It must be an implementation detail that they should never - * know. Might better be hidden. */ - RUBY_FL_PROMOTED1 = (1<<6), - - /** - * This flag has something to do with our garbage collector. These days - * ruby objects are "generational". There are those who are young and - * those who are old. Young objects are prone to die; monitored relatively - * extensively by the garbage collector. OTOH old objects tend to live - * longer. They are relatively rarely considered. This flag is set when a - * object experienced promotions i.e. survived more than one garbage - * collections. - * - * @internal - * - * But honestly, @shyouhei doesn't think this flag should be visible from - * 3rd parties. It must be an implementation detail that they should never - * know. Might better be hidden. - */ - RUBY_FL_PROMOTED = RUBY_FL_PROMOTED0 | RUBY_FL_PROMOTED1, + RUBY_FL_UNUSED6 = (1<<6), /** * This flag has something to do with finalisers. A ruby object can have @@ -283,7 +251,7 @@ ruby_fl_type { # pragma deprecated(RUBY_FL_TAINT) #endif - = (1<<8), + = 0, /** * This flag has something to do with Ractor. Multiple Ractors run without @@ -310,7 +278,7 @@ ruby_fl_type { # pragma deprecated(RUBY_FL_UNTRUSTED) #endif - = (1<<8), + = 0, /** * This flag has something to do with object IDs. Unlike in the old days, @@ -427,7 +395,7 @@ ruby_fl_type { * 3rd parties. It must be an implementation detail that they should never * know. Might better be hidden. */ - RUBY_FL_SINGLETON = RUBY_FL_USER0, + RUBY_FL_SINGLETON = RUBY_FL_USER1, }; enum { @@ -489,7 +457,7 @@ RB_FL_ABLE(VALUE obj) RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** - * This is an implenentation detail of RB_FL_TEST(). 3rd parties need not use + * This is an implementation detail of RB_FL_TEST(). 3rd parties need not use * this. Just always use RB_FL_TEST(). * * @param[in] obj Object in question. @@ -537,7 +505,7 @@ RB_FL_TEST(VALUE obj, VALUE flags) RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** - * This is an implenentation detail of RB_FL_ANY(). 3rd parties need not use + * This is an implementation detail of RB_FL_ANY(). 3rd parties need not use * this. Just always use RB_FL_ANY(). * * @param[in] obj Object in question. @@ -571,7 +539,7 @@ RB_FL_ANY(VALUE obj, VALUE flags) RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** - * This is an implenentation detail of RB_FL_ALL(). 3rd parties need not use + * This is an implementation detail of RB_FL_ALL(). 3rd parties need not use * this. Just always use RB_FL_ALL(). * * @param[in] obj Object in question. @@ -607,7 +575,7 @@ RBIMPL_ATTR_ARTIFICIAL() /** * @private * - * This is an implenentation detail of RB_FL_SET(). 3rd parties need not use + * This is an implementation detail of RB_FL_SET(). 3rd parties need not use * this. Just always use RB_FL_SET(). * * @param[out] obj Object in question. @@ -627,7 +595,7 @@ rbimpl_fl_set_raw_raw(struct RBasic *obj, VALUE flags) RBIMPL_ATTR_ARTIFICIAL() /** - * This is an implenentation detail of RB_FL_SET(). 3rd parties need not use + * This is an implementation detail of RB_FL_SET(). 3rd parties need not use * this. Just always use RB_FL_SET(). * * @param[out] obj Object in question. @@ -667,7 +635,7 @@ RBIMPL_ATTR_ARTIFICIAL() /** * @private * - * This is an implenentation detail of RB_FL_UNSET(). 3rd parties need not use + * This is an implementation detail of RB_FL_UNSET(). 3rd parties need not use * this. Just always use RB_FL_UNSET(). * * @param[out] obj Object in question. @@ -687,7 +655,7 @@ rbimpl_fl_unset_raw_raw(struct RBasic *obj, VALUE flags) RBIMPL_ATTR_ARTIFICIAL() /** - * This is an implenentation detail of RB_FL_UNSET(). 3rd parties need not use + * This is an implementation detail of RB_FL_UNSET(). 3rd parties need not use * this. Just always use RB_FL_UNSET(). * * @param[out] obj Object in question. @@ -722,7 +690,7 @@ RBIMPL_ATTR_ARTIFICIAL() /** * @private * - * This is an implenentation detail of RB_FL_REVERSE(). 3rd parties need not + * This is an implementation detail of RB_FL_REVERSE(). 3rd parties need not * use this. Just always use RB_FL_REVERSE(). * * @param[out] obj Object in question. @@ -742,7 +710,7 @@ rbimpl_fl_reverse_raw_raw(struct RBasic *obj, VALUE flags) RBIMPL_ATTR_ARTIFICIAL() /** - * This is an implenentation detail of RB_FL_REVERSE(). 3rd parties need not + * This is an implementation detail of RB_FL_REVERSE(). 3rd parties need not * use this. Just always use RB_FL_REVERSE(). * * @param[out] obj Object in question. @@ -898,7 +866,7 @@ RB_OBJ_INFECT(VALUE dst, VALUE src) RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** - * This is an implenentation detail of RB_OBJ_FROZEN(). 3rd parties need not + * This is an implementation detail of RB_OBJ_FROZEN(). 3rd parties need not * use this. Just always use RB_OBJ_FROZEN(). * * @param[in] obj Object in question. @@ -937,9 +905,13 @@ RB_OBJ_FROZEN(VALUE obj) } } +RUBY_SYMBOL_EXPORT_BEGIN +void rb_obj_freeze_inline(VALUE obj); +RUBY_SYMBOL_EXPORT_END + RBIMPL_ATTR_ARTIFICIAL() /** - * This is an implenentation detail of RB_OBJ_FREEZE(). 3rd parties need not + * This is an implementation detail of RB_OBJ_FREEZE(). 3rd parties need not * use this. Just always use RB_OBJ_FREEZE(). * * @param[out] obj Object in question. @@ -947,11 +919,7 @@ RBIMPL_ATTR_ARTIFICIAL() static inline void RB_OBJ_FREEZE_RAW(VALUE obj) { - RB_FL_SET_RAW(obj, RUBY_FL_FREEZE); + rb_obj_freeze_inline(obj); } -RUBY_SYMBOL_EXPORT_BEGIN -void rb_obj_freeze_inline(VALUE obj); -RUBY_SYMBOL_EXPORT_END - #endif /* RBIMPL_FL_TYPE_H */ diff --git a/include/ruby/internal/gc.h b/include/ruby/internal/gc.h index 205830954e..462f416af2 100644 --- a/include/ruby/internal/gc.h +++ b/include/ruby/internal/gc.h @@ -44,10 +44,11 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() -#define REF_EDGE(s, p) (offsetof(struct s, p)) -#define REFS_LIST_PTR(l) ((RUBY_DATA_FUNC)l) +#define RUBY_REF_EDGE(s, p) offsetof(s, p) +#define RUBY_REFS_LIST_PTR(l) (RUBY_DATA_FUNC)(l) #define RUBY_REF_END SIZE_MAX -#define RUBY_REFERENCES_START(t) static size_t t[] = { +#define RUBY_REFERENCES(t) static const size_t t[] +#define RUBY_REFERENCES_START(t) RUBY_REFERENCES(t) = { #define RUBY_REFERENCES_END RUBY_REF_END, }; /* gc.c */ @@ -209,22 +210,6 @@ void rb_gc_mark_movable(VALUE obj); VALUE rb_gc_location(VALUE obj); /** - * Asserts that the passed object is no longer needed. Such objects are - * reclaimed sooner or later so this function is not mandatory. But sometimes - * you can know from your application knowledge that an object is surely dead - * at some point. Calling this as a hint can be a polite way. - * - * @param[out] obj Object, dead. - * @pre `obj` have never been passed to this function before. - * @post `obj` could be invalidated. - * @warning It is a failure to pass an object multiple times to this - * function. - * @deprecated This is now a no-op function. - */ -RBIMPL_ATTR_DEPRECATED(("this is now a no-op function")) -void rb_gc_force_recycle(VALUE obj); - -/** * Triggers a GC process. This was the only GC entry point that we had at the * beginning. Over time our GC evolved. Now what this function does is just a * very simplified variation of the entire GC algorithms. A series of @@ -443,18 +428,6 @@ RBIMPL_SYMBOL_EXPORT_END() #define USE_RGENGC 1 /** - * @private - * - * This is a compile-time flag to enable/disable incremental GC feature. It - * has to be set at the time ruby itself compiles. Makes no sense for 3rd - * parties. It is safe for them to set this though; that just doesn't change - * anything. - */ -#ifndef USE_RINCGC -# define USE_RINCGC 1 -#endif - -/** * @deprecated This macro seems broken. Setting this to anything other than * zero just doesn't compile. We need to KonMari. */ @@ -850,4 +823,7 @@ rb_obj_write( return a; } +RBIMPL_ATTR_DEPRECATED(("Will be removed soon")) +static inline void rb_gc_force_recycle(VALUE obj){} + #endif /* RBIMPL_GC_H */ diff --git a/include/ruby/internal/globals.h b/include/ruby/internal/globals.h index 5a414fc472..60d8e5309a 100644 --- a/include/ruby/internal/globals.h +++ b/include/ruby/internal/globals.h @@ -94,7 +94,7 @@ RUBY_EXTERN VALUE rb_cRegexp; /**< `Regexp` class. */ RUBY_EXTERN VALUE rb_cStat; /**< `File::Stat` class. */ RUBY_EXTERN VALUE rb_cString; /**< `String` class. */ RUBY_EXTERN VALUE rb_cStruct; /**< `Struct` class. */ -RUBY_EXTERN VALUE rb_cSymbol; /**< `Sumbol` class. */ +RUBY_EXTERN VALUE rb_cSymbol; /**< `Symbol` class. */ RUBY_EXTERN VALUE rb_cThread; /**< `Thread` class. */ RUBY_EXTERN VALUE rb_cTime; /**< `Time` class. */ RUBY_EXTERN VALUE rb_cTrueClass; /**< `TrueClass` class. */ diff --git a/include/ruby/internal/has/c_attribute.h b/include/ruby/internal/has/c_attribute.h index c5c48867bf..69b0f402cd 100644 --- a/include/ruby/internal/has/c_attribute.h +++ b/include/ruby/internal/has/c_attribute.h @@ -21,11 +21,23 @@ * @brief Defines #RBIMPL_HAS_C_ATTRIBUTE. */ +#include "ruby/internal/has/extension.h" +#include "ruby/internal/has/warning.h" + /** Wraps (or simulates) `__has_c_attribute`. */ #if defined(__cplusplus) # /* Makes no sense. */ # define RBIMPL_HAS_C_ATTRIBUTE(_) 0 +#elif RBIMPL_HAS_EXTENSION(c_attributes) +# /* Hmm. It seems Clang 17 has this macro defined even when -std=c99 mode, +# * _and_ fails to compile complaining that attributes are C2X feature. We +# * need to work around this nonsense. */ +# define RBIMPL_HAS_C_ATTRIBUTE(_) __has_c_attribute(_) + +#elif RBIMPL_HAS_WARNING("-Wc2x-extensions") +# define RBIMPL_HAS_C_ATTRIBUTE(_) 0 + #elif defined(__has_c_attribute) # define RBIMPL_HAS_C_ATTRIBUTE(_) __has_c_attribute(_) diff --git a/include/ruby/internal/intern/array.h b/include/ruby/internal/intern/array.h index 2262c6f0c6..1909fdf17b 100644 --- a/include/ruby/internal/intern/array.h +++ b/include/ruby/internal/intern/array.h @@ -187,7 +187,7 @@ VALUE rb_ary_shared_with_p(VALUE lhs, VALUE rhs); * : (int i) -> T? * | (int beg, int len) -> ::Array[T]? * | (Range[int] r) -> ::Array[T]? - * | (ArithmeticSequence as) -> ::Array[T]? # This also raises RagneError. + * | (ArithmeticSequence as) -> ::Array[T]? # This also raises RangeError. * end * ``` */ diff --git a/include/ruby/internal/intern/bignum.h b/include/ruby/internal/intern/bignum.h index 43d68018de..c27f77a1fb 100644 --- a/include/ruby/internal/intern/bignum.h +++ b/include/ruby/internal/intern/bignum.h @@ -51,7 +51,7 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() VALUE rb_big_new(size_t len, int sign); /** - * Queries if the passed bignum instance is a "bigzro". What is a bigzero? + * Queries if the passed bignum instance is a "bigzero". What is a bigzero? * Well, bignums are for very big integers, but can also represent tiny ones * like -1, 0, 1. Bigzero are instances of bignums whose values are zero. * Knowing if a bignum is bigzero can be handy on occasions, like for instance @@ -793,7 +793,7 @@ size_t rb_absint_size(VALUE val, int *nlz_bits_ret); * @exception rb_eTypeError `val` doesn't respond to `#to_int`. * @retval (size_t)-1 Overflowed. * @retval otherwise - `((val_numbits * CHAR_BIT + word_numbits - 1) / word_numbits)`, + * `((val_numbits * CHAR_BIT + word_numbits - 1) / word_numbits)`, * where val_numbits is the number of bits of `abs(val)`. * @post If `nlz_bits_ret` is not `NULL` and there is no overflow, * `(return_value * word_numbits - val_numbits)` is stored in diff --git a/include/ruby/internal/intern/class.h b/include/ruby/internal/intern/class.h index 0fb2d001bc..357af5d176 100644 --- a/include/ruby/internal/intern/class.h +++ b/include/ruby/internal/intern/class.h @@ -88,8 +88,8 @@ VALUE rb_define_class_id(ID id, VALUE super); * @post `outer::id` refers the returned class. * @note If a class named `id` is already defined and its superclass is * `super`, the function just returns the defined class. - * @note The compaction GC does not move classes returned by this - * function. + * @note The GC does not collect nor move classes returned by this + * function. They are immortal. */ VALUE rb_define_class_id_under(VALUE outer, ID id, VALUE super); @@ -127,8 +127,8 @@ VALUE rb_define_module_id(ID id); * constant is not a module. * @return The created module. * @post `outer::id` refers the returned module. - * @note The compaction GC does not move classes returned by this - * function. + * @note The GC does not collect nor move classes returned by this + * function. They are immortal. */ VALUE rb_define_module_id_under(VALUE outer, ID id); diff --git a/include/ruby/internal/intern/error.h b/include/ruby/internal/intern/error.h index bf8daadd3e..c56db91b4f 100644 --- a/include/ruby/internal/intern/error.h +++ b/include/ruby/internal/intern/error.h @@ -237,6 +237,9 @@ RBIMPL_ATTR_NORETURN() */ void rb_error_arity(int argc, int min, int max); +bool rb_str_chilled_p(VALUE str); +void rb_str_modify(VALUE str); + RBIMPL_SYMBOL_EXPORT_END() /** @@ -249,12 +252,18 @@ RBIMPL_SYMBOL_EXPORT_END() if (RB_UNLIKELY(RB_OBJ_FROZEN(frozen_obj))) { \ rb_error_frozen_object(frozen_obj); \ } \ + if (RB_UNLIKELY(rb_str_chilled_p(frozen_obj))) { \ + rb_str_modify(frozen_obj); \ + } \ } while (0) /** @alias{rb_check_frozen} */ static inline void rb_check_frozen_inline(VALUE obj) { + if (rb_str_chilled_p(obj)) { + rb_str_modify(obj); + } if (RB_UNLIKELY(RB_OBJ_FROZEN(obj))) { rb_error_frozen_object(obj); } diff --git a/include/ruby/internal/intern/load.h b/include/ruby/internal/intern/load.h index 288a16c2ec..9ceb98c2e4 100644 --- a/include/ruby/internal/intern/load.h +++ b/include/ruby/internal/intern/load.h @@ -177,6 +177,43 @@ VALUE rb_f_require(VALUE self, VALUE feature); VALUE rb_require_string(VALUE feature); /** + * Resolves and returns a symbol of a function in the native extension + * specified by the feature and symbol names. Extensions will use this function + * to access the symbols provided by other native extensions. + * + * @param[in] feature Name of a feature, e.g. `"json"`. + * @param[in] symbol Name of a symbol defined by the feature. + * @return The resolved symbol of a function, defined and externed by the + * specified feature. It may be NULL if the feature is not loaded, + * the feature is not extension, or the symbol is not found. + */ +void *rb_ext_resolve_symbol(const char *feature, const char *symbol); + +/** + * This macro is to provide backwards compatibility. It provides a way to + * define function prototypes and resolving function symbols in a safe way. + * + * ```CXX + * // prototypes + * #ifdef HAVE_RB_EXT_RESOLVE_SYMBOL + * VALUE *(*other_extension_func)(VALUE,VALUE); + * #else + * VALUE other_extension_func(VALUE); + * #endif + * + * // in Init_xxx() + * #ifdef HAVE_RB_EXT_RESOLVE_SYMBOL + * other_extension_func = \ + * (VALUE(*)(VALUE,VALUE))rb_ext_resolve_symbol(fname, sym_name); + * if (other_extension_func == NULL) { + * // raise your own error + * } + * #endif + * ``` + */ +#define HAVE_RB_EXT_RESOLVE_SYMBOL 1 + +/** * @name extension configuration * @{ */ diff --git a/include/ruby/internal/intern/object.h b/include/ruby/internal/intern/object.h index b9ffa57c06..9daad7d046 100644 --- a/include/ruby/internal/intern/object.h +++ b/include/ruby/internal/intern/object.h @@ -151,13 +151,12 @@ VALUE rb_obj_is_kind_of(VALUE obj, VALUE klass); * @return An allocated, not yet initialised instance of `klass`. * @note It calls the allocator defined by rb_define_alloc_func(). You * cannot use this function to define an allocator. Use - * rb_newobj_of(), #TypedData_Make_Struct or others, instead. + * TypedData_Make_Struct or others, instead. * @note Usually prefer rb_class_new_instance() to rb_obj_alloc() and * rb_obj_call_init(). * @see rb_class_new_instance() * @see rb_obj_call_init() * @see rb_define_alloc_func() - * @see rb_newobj_of() * @see #TypedData_Make_Struct */ VALUE rb_obj_alloc(VALUE klass); diff --git a/include/ruby/internal/intern/process.h b/include/ruby/internal/intern/process.h index 7a7b24ed4b..cfa5e13162 100644 --- a/include/ruby/internal/intern/process.h +++ b/include/ruby/internal/intern/process.h @@ -31,6 +31,15 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() /* process.c */ /** + * Wait for the specified process to terminate, reap it, and return its status. + * + * @param[in] pid The process ID to wait for. + * @param[in] flags The flags to pass to waitpid(2). + * @return VALUE An instance of Process::Status. + */ +VALUE rb_process_status_wait(rb_pid_t pid, int flags); + +/** * Sets the "last status", or the `$?`. * * @param[in] status The termination status, as defined in `waitpid(3posix)`. @@ -247,7 +256,7 @@ rb_pid_t rb_spawn_err(int argc, const VALUE *argv, char *errbuf, size_t buflen); * * @internal * - * This function might or might not exist depending on `./confiugre` result. + * This function might or might not exist depending on `./configure` result. * It must be a portability hell. Better not use. */ VALUE rb_proc_times(VALUE _); diff --git a/include/ruby/internal/intern/re.h b/include/ruby/internal/intern/re.h index 31f5593275..4dd58b469b 100644 --- a/include/ruby/internal/intern/re.h +++ b/include/ruby/internal/intern/re.h @@ -87,11 +87,6 @@ void rb_match_busy(VALUE md); * @retval RUBY_Qfalse There is a `n`-th capture and is empty. * @retval RUBY_Qtrue There is a `n`-th capture that has something. * - * @internal - * - * @shyouhei wonders: why there are both rb_reg_match_defined() and - * rb_match_nth_defined, which are largely the same things, but do not share - * their implementations at all? */ VALUE rb_reg_nth_defined(int n, VALUE md); diff --git a/include/ruby/internal/intern/select.h b/include/ruby/internal/intern/select.h index fabc287cd1..6ba84c6e63 100644 --- a/include/ruby/internal/intern/select.h +++ b/include/ruby/internal/intern/select.h @@ -76,7 +76,7 @@ struct timeval; * * Although any file descriptors are possible here, it makes completely no * sense to pass a descriptor that is not `O_NONBLOCK`. If you want to know - * the reason for this limitatuon in detail, you might find this thread super + * the reason for this limitation in detail, you might find this thread super * interesting: https://lkml.org/lkml/2004/10/6/117 */ int rb_thread_fd_select(int nfds, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout); diff --git a/include/ruby/internal/intern/signal.h b/include/ruby/internal/intern/signal.h index 84f7558404..4773788651 100644 --- a/include/ruby/internal/intern/signal.h +++ b/include/ruby/internal/intern/signal.h @@ -97,7 +97,7 @@ RBIMPL_ATTR_NONNULL(()) * - Case #11: When signo and PID are both negative, the behaviour of this * function depends on how `killpg(3)` works. On Linux, it seems such * attempt is strictly prohibited and `Errno::EINVAL` is raised. But on - * macOS, it seems it tries to to send the signal actually to the process + * macOS, it seems it tries to send the signal actually to the process * group. * * @note Above description is in fact different from how `kill(2)` works. @@ -113,12 +113,6 @@ RBIMPL_ATTR_NONNULL(()) */ VALUE rb_f_kill(int argc, const VALUE *argv); -/* This must be private, @shyouhei guesses. */ -#ifdef POSIX_SIGNAL -#define posix_signal ruby_posix_signal -void (*posix_signal(int, void (*)(int)))(int); -#endif - RBIMPL_ATTR_PURE() /** * Queries the name of the signal. It returns for instance `"KILL"` for diff --git a/include/ruby/internal/intern/string.h b/include/ruby/internal/intern/string.h index 3083125e56..6827563e8d 100644 --- a/include/ruby/internal/intern/string.h +++ b/include/ruby/internal/intern/string.h @@ -412,7 +412,7 @@ VALUE rb_utf8_str_new_static(const char *ptr, long len); /** * Identical to rb_interned_str(), except it takes a Ruby's string instead of - * C's. It can also be seen as a routine identical to to rb_str_new_shared(), + * C's. It can also be seen as a routine identical to rb_str_new_shared(), * except it returns an infamous "f"string. * * @param[in] str An object of ::RString. @@ -454,7 +454,7 @@ VALUE rb_interned_str(const char *ptr, long len); RBIMPL_ATTR_NONNULL(()) /** * Identical to rb_interned_str(), except it assumes the passed pointer is a - * pointer to a C's string. It can also be seen as a routine identical to to + * pointer to a C's string. It can also be seen as a routine identical to * rb_str_to_interned_str(), except it takes a C's string instead of Ruby's. * Or it can also be seen as a routine identical to rb_str_new_cstr(), except * it returns an infamous "f"string. @@ -602,6 +602,21 @@ VALUE rb_str_dup(VALUE str); VALUE rb_str_resurrect(VALUE str); /** + * Returns whether a string is chilled or not. + * + * This function is temporary and users must check for its presence using + * #ifdef HAVE_RB_STR_CHILLED_P. If HAVE_RB_STR_CHILLED_P is not defined, then + * strings can't be chilled. + * + * @param[in] str A string. + * @retval 1 The string is chilled. + * @retval 0 Otherwise. + */ +bool rb_str_chilled_p(VALUE str); + +#define HAVE_RB_STR_CHILLED_P 1 + +/** * Obtains a "temporary lock" of the string. This advisory locking mechanism * prevents other cooperating threads from tampering the receiver. The same * thing could be done via freeze mechanism, but this one can also be unlocked diff --git a/include/ruby/internal/intern/struct.h b/include/ruby/internal/intern/struct.h index 312cf444e2..16b3fad4e0 100644 --- a/include/ruby/internal/intern/struct.h +++ b/include/ruby/internal/intern/struct.h @@ -46,14 +46,16 @@ VALUE rb_struct_new(VALUE klass, ...); * * @param[in] name Name of the class. * @param[in] ... Arbitrary number of `const char*`, terminated by - * zero. Each of which are the name of fields. + * NULL. Each of which are the name of fields. * @exception rb_eNameError `name` is not a constant name. * @exception rb_eTypeError `name` is already taken. - * @exception rb_eArgError Duplicated field name. + * @exception rb_eArgError Duplicated field name. * @return The defined class. * @post Global toplevel constant `name` is defined. * @note `name` is allowed to be a null pointer. This function creates * an anonymous struct class then. + * @note The GC does not collect nor move classes returned by this + * function. They are immortal. * * @internal * @@ -70,14 +72,16 @@ RBIMPL_ATTR_NONNULL((2)) * @param[out] space Namespace that the defining class shall reside. * @param[in] name Name of the class. * @param[in] ... Arbitrary number of `const char*`, terminated by - * zero. Each of which are the name of fields. + * NULL. Each of which are the name of fields. * @exception rb_eNameError `name` is not a constant name. * @exception rb_eTypeError `name` is already taken. - * @exception rb_eArgError Duplicated field name. + * @exception rb_eArgError Duplicated field name. * @return The defined class. * @post `name` is a constant under `space`. * @note In contrast to rb_struct_define(), it doesn't make any sense to * pass a null pointer to this function. + * @note The GC does not collect nor move classes returned by this + * function. They are immortal. */ VALUE rb_struct_define_under(VALUE space, const char *name, ...); @@ -164,10 +168,10 @@ VALUE rb_struct_alloc_noinit(VALUE klass); * @param[in] super Superclass of the defining class. * @param[in] func Must be 0 for extension libraries. * @param[in] ... Arbitrary number of `const char*`, terminated by - * zero. Each of which are the name of fields. + * NULL. Each of which are the name of fields. * @exception rb_eNameError `name` is not a constant name. * @exception rb_eTypeError `name` is already taken. - * @exception rb_eArgError Duplicated field name. + * @exception rb_eArgError Duplicated field name. * @return The defined class. * @post Global toplevel constant `name` is defined. * @note `name` is allowed to be a null pointer. This function creates @@ -187,17 +191,35 @@ RBIMPL_ATTR_NONNULL((2)) * @param[in] super Superclass of the defining class. * @param[in] alloc Must be 0 for extension libraries. * @param[in] ... Arbitrary number of `const char*`, terminated by - * zero. Each of which are the name of fields. + * NULL. Each of which are the name of fields. * @exception rb_eNameError `class_name` is not a constant name. * @exception rb_eTypeError `class_name` is already taken. - * @exception rb_eArgError Duplicated field name. + * @exception rb_eArgError Duplicated field name. * @return The defined class. * @post `class_name` is a constant under `outer`. * @note In contrast to rb_struct_define_without_accessor(), it doesn't * make any sense to pass a null name. + * @note The GC does not collect nor move classes returned by this + * function. They are immortal. */ VALUE rb_struct_define_without_accessor_under(VALUE outer, const char *class_name, VALUE super, rb_alloc_func_t alloc, ...); +/** + * Defines an anonymous data class. + * + * @endinternal + * + * @param[in] super Superclass of the defining class. Must be a + * descendant of ::rb_cData, or 0 as ::rb_cData. + * @param[in] ... Arbitrary number of `const char*`, terminated by + * NULL. Each of which are the name of fields. + * @exception rb_eArgError Duplicated field name. + * @return The defined class. + * @note The GC does not collect nor move classes returned by this + * function. They are immortal. + */ +VALUE rb_data_define(VALUE super, ...); + RBIMPL_SYMBOL_EXPORT_END() #endif /* RBIMPL_INTERN_STRUCT_H */ diff --git a/include/ruby/internal/intern/vm.h b/include/ruby/internal/intern/vm.h index 76af796b54..29e0c7f534 100644 --- a/include/ruby/internal/intern/vm.h +++ b/include/ruby/internal/intern/vm.h @@ -229,8 +229,7 @@ void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func); * restrict creation of an instance of a class. For example it rarely makes * sense for a DB adaptor class to allow programmers creating DB row objects * without querying the DB itself. You can kill sporadic creation of such - * objects then, by nullifying the allocator function using this API. Your - * object shall be allocated using #RB_NEWOBJ_OF() directly. + * objects then, by nullifying the allocator function using this API. * * @param[out] klass The class to modify. * @pre `klass` must be an instance of Class. diff --git a/include/ruby/internal/interpreter.h b/include/ruby/internal/interpreter.h index 662d39c0ec..a10e7ad2d8 100644 --- a/include/ruby/internal/interpreter.h +++ b/include/ruby/internal/interpreter.h @@ -141,7 +141,7 @@ void ruby_show_copyright(void); * * @param[in] addr A pointer somewhere on the stack, near its bottom. */ -void ruby_init_stack(volatile VALUE *addr); +void ruby_init_stack(void *addr); /** * Initializes the VM and builtin libraries. diff --git a/include/ruby/internal/memory.h b/include/ruby/internal/memory.h index 6884db195d..64e850c65e 100644 --- a/include/ruby/internal/memory.h +++ b/include/ruby/internal/memory.h @@ -38,7 +38,7 @@ # include <alloca.h> #endif -#if defined(_MSC_VER) && defined(_WIN64) +#if defined(_MSC_VER) && defined(_M_AMD64) # include <intrin.h> # pragma intrinsic(_umul128) #endif @@ -56,13 +56,14 @@ #include "ruby/internal/has/builtin.h" #include "ruby/internal/stdalign.h" #include "ruby/internal/stdbool.h" +#include "ruby/internal/stdckdint.h" #include "ruby/internal/xmalloc.h" #include "ruby/backward/2/limits.h" #include "ruby/backward/2/long_long.h" #include "ruby/backward/2/assume.h" #include "ruby/defines.h" -/** @cond INTENAL_MACRO */ +/** @cond INTERNAL_MACRO */ /* Make alloca work the best possible way. */ #if defined(alloca) @@ -567,7 +568,10 @@ rbimpl_size_mul_overflow(size_t x, size_t y) { struct rbimpl_size_mul_overflow_tag ret = { false, 0, }; -#if RBIMPL_HAS_BUILTIN(__builtin_mul_overflow) +#if defined(ckd_mul) + ret.left = ckd_mul(&ret.right, x, y); + +#elif RBIMPL_HAS_BUILTIN(__builtin_mul_overflow) ret.left = __builtin_mul_overflow(x, y, &ret.right); #elif defined(DSIZE_T) @@ -639,7 +643,7 @@ rbimpl_size_mul_or_raise(size_t x, size_t y) static inline void * rb_alloc_tmp_buffer2(volatile VALUE *store, long count, size_t elsize) { - const size_t total_size = rbimpl_size_mul_or_raise(count, elsize); + const size_t total_size = rbimpl_size_mul_or_raise(RBIMPL_CAST((size_t)count), elsize); const size_t cnt = (total_size + sizeof(VALUE) - 1) / sizeof(VALUE); return rb_alloc_tmp_buffer_with_count(store, total_size, cnt); } diff --git a/include/ruby/internal/module.h b/include/ruby/internal/module.h index d678dd2102..97b0b2b8b0 100644 --- a/include/ruby/internal/module.h +++ b/include/ruby/internal/module.h @@ -56,8 +56,8 @@ RBIMPL_ATTR_NONNULL(()) * @post Top-level constant named `name` refers the returned class. * @note If a class named `name` is already defined and its superclass is * `super`, the function just returns the defined class. - * @note The compaction GC does not move classes returned by this - * function. + * @note The GC does not collect nor move classes returned by this + * function. They are immortal. * * @internal * @@ -75,8 +75,8 @@ RBIMPL_ATTR_NONNULL(()) * constant is not a module. * @return The created module. * @post Top-level constant named `name` refers the returned module. - * @note The compaction GC does not move classes returned by this - * function. + * @note The GC does not collect nor move modules returned by this + * function. They are immortal. * * @internal * @@ -103,8 +103,8 @@ RBIMPL_ATTR_NONNULL(()) * @post `outer::name` refers the returned class. * @note If a class named `name` is already defined and its superclass * is `super`, the function just returns the defined class. - * @note The compaction GC does not move classes returned by this - * function. + * @note The GC does not collect nor move classes returned by this + * function. They are immortal. */ VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super); @@ -118,8 +118,8 @@ RBIMPL_ATTR_NONNULL(()) * the constant is not a class. * @return The created module. * @post `outer::name` refers the returned module. - * @note The compaction GC does not move classes returned by this - * function. + * @note The GC does not collect nor move modules returned by this + * function. They are immortal. */ VALUE rb_define_module_under(VALUE outer, const char *name); diff --git a/include/ruby/internal/newobj.h b/include/ruby/internal/newobj.h index ba1d7cbe59..6eee2fa5fa 100644 --- a/include/ruby/internal/newobj.h +++ b/include/ruby/internal/newobj.h @@ -29,63 +29,14 @@ #include "ruby/internal/value.h" #include "ruby/assert.h" -/** - * Declares, allocates, then assigns a new object to the given variable. - * - * @param obj Variable name. - * @param type Variable type. - * @exception rb_eNoMemError No space left. - * @return An allocated object, not initialised. - * @note Modern programs tend to use #NEWOBJ_OF instead. - * - * @internal - * - * :FIXME: Should we deprecate it? - */ -#define RB_NEWOBJ(obj,type) type *(obj) = RBIMPL_CAST((type *)rb_newobj()) - -/** - * Identical to #RB_NEWOBJ, except it also accepts the allocating object's - * class and flags. - * - * @param obj Variable name. - * @param type Variable type. - * @param klass Object's class. - * @param flags Object's flags. - * @exception rb_eNoMemError No space left. - * @return An allocated object, filled with the arguments. - */ -#define RB_NEWOBJ_OF(obj,type,klass,flags) type *(obj) = RBIMPL_CAST((type *)rb_newobj_of(klass, flags)) - -#define NEWOBJ RB_NEWOBJ /**< @old{RB_NEWOBJ} */ -#define NEWOBJ_OF RB_NEWOBJ_OF /**< @old{RB_NEWOBJ_OF} */ #define OBJSETUP rb_obj_setup /**< @old{rb_obj_setup} */ #define CLONESETUP rb_clone_setup /**< @old{rb_clone_setup} */ #define DUPSETUP rb_dup_setup /**< @old{rb_dup_setup} */ RBIMPL_SYMBOL_EXPORT_BEGIN() /** - * This is the implementation detail of #RB_NEWOBJ. - * - * @exception rb_eNoMemError No space left. - * @return An allocated object, not initialised. - */ -VALUE rb_newobj(void); - -/** - * This is the implementation detail of #RB_NEWOBJ_OF. - * - * @param klass Object's class. - * @param flags Object's flags. - * @exception rb_eNoMemError No space left. - * @return An allocated object, filled with the arguments. - */ -VALUE rb_newobj_of(VALUE klass, VALUE flags); - -/** * Fills common fields in the object. * - * @note Prefer rb_newobj_of() to this function. * @param[in,out] obj A Ruby object to be set up. * @param[in] klass `obj` will belong to this class. * @param[in] type One of ::ruby_value_type. diff --git a/include/ruby/internal/special_consts.h b/include/ruby/internal/special_consts.h index dc0a6b41d6..85579e33f0 100644 --- a/include/ruby/internal/special_consts.h +++ b/include/ruby/internal/special_consts.h @@ -156,7 +156,7 @@ RB_TEST(VALUE obj) * * RTEST(v) can be 0 if and only if (v == Qfalse || v == Qnil). */ - return obj & ~RUBY_Qnil; + return obj & RBIMPL_CAST((VALUE)~RUBY_Qnil); } RBIMPL_ATTR_CONST() @@ -226,7 +226,7 @@ RB_NIL_OR_UNDEF_P(VALUE obj) * * NIL_OR_UNDEF_P(v) can be true only when v is Qundef or Qnil. */ - const VALUE mask = ~(RUBY_Qundef ^ RUBY_Qnil); + const VALUE mask = RBIMPL_CAST((VALUE)~(RUBY_Qundef ^ RUBY_Qnil)); const VALUE common_bits = RUBY_Qundef & RUBY_Qnil; return (obj & mask) == common_bits; } diff --git a/include/ruby/internal/static_assert.h b/include/ruby/internal/static_assert.h index 594c2b2917..b9ff6646e7 100644 --- a/include/ruby/internal/static_assert.h +++ b/include/ruby/internal/static_assert.h @@ -71,7 +71,7 @@ #else # define RBIMPL_STATIC_ASSERT(name, expr) \ - typedef int static_assert_ ## name ## _check[1 - 2 * !(expr)] + MAYBE_UNUSED(typedef int static_assert_ ## name ## _check[1 - 2 * !(expr)]) #endif #endif /* RBIMPL_STATIC_ASSERT_H */ diff --git a/include/ruby/internal/stdckdint.h b/include/ruby/internal/stdckdint.h new file mode 100644 index 0000000000..e5b5b8b751 --- /dev/null +++ b/include/ruby/internal/stdckdint.h @@ -0,0 +1,68 @@ +#ifndef RBIMPL_STDCKDINT_H /*-*-C++-*-vi:se ft=cpp:*/ +#define RBIMPL_STDCKDINT_H +/** + * @author Ruby developers <ruby-core@ruby-lang.org> + * @copyright This file is a part of the programming language Ruby. + * Permission is hereby granted, to either redistribute and/or + * modify this file, provided that the conditions mentioned in the + * file COPYING are met. Consult the file for details. + * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are + * implementation details. Don't take them as canon. They could + * rapidly appear then vanish. The name (path) of this header file + * is also an implementation detail. Do not expect it to persist + * at the place it is now. Developers are free to move it anywhere + * anytime at will. + * @note To ruby-core: remember that this header can be possibly + * recursively included from extension libraries written in C++. + * Do not expect for instance `__VA_ARGS__` is always available. + * We assume C99 for ruby itself but we don't assume languages of + * extension libraries. They could be written in C++98. + * @brief C23 shim for <stdckdint.h> + */ +#include "ruby/internal/config.h" +#include "ruby/internal/cast.h" +#include "ruby/internal/has/builtin.h" +#include "ruby/internal/stdbool.h" + +#ifdef __has_include +# if __has_include(<stdckdint.h>) +# /* Conforming C23 situation; e.g. recent clang */ +# define RBIMPL_HAVE_STDCKDINT_H +# endif +#endif + +#ifdef HAVE_STDCKDINT_H +# /* Some OSes (most notably FreeBSD) have this file. */ +# define RBIMPL_HAVE_STDCKDINT_H +#endif + +#ifdef __cplusplus +# /* It seems OS/Compiler provided stdckdint.h tend not support C++ yet. +# * Situations could improve someday but in a meantime let us work around. +# */ +# undef RBIMPL_HAVE_STDCKDINT_H +#endif + +#ifdef RBIMPL_HAVE_STDCKDINT_H +# /* Take that. */ +# include <stdckdint.h> + +#elif RBIMPL_HAS_BUILTIN(__builtin_add_overflow) +# define ckd_add(x, y, z) RBIMPL_CAST((bool)__builtin_add_overflow((y), (z), (x))) +# define ckd_sub(x, y, z) RBIMPL_CAST((bool)__builtin_sub_overflow((y), (z), (x))) +# define ckd_mul(x, y, z) RBIMPL_CAST((bool)__builtin_mul_overflow((y), (z), (x))) +# define __STDC_VERSION_STDCKDINT_H__ 202311L + +#/* elif defined(__cplusplus) */ +#/* :TODO: if we assume C++11 we can use `<type_traits>` to implement them. */ + +#else +# /* intentionally leave them undefined */ +# /* to make `#ifdef ckd_add` etc. work as intended. */ +# undef ckd_add +# undef ckd_sub +# undef ckd_mul +# undef __STDC_VERSION_STDCKDINT_H__ +#endif + +#endif /* RBIMPL_STDCKDINT_H */ diff --git a/include/ruby/internal/value_type.h b/include/ruby/internal/value_type.h index 977f60a009..557f18813b 100644 --- a/include/ruby/internal/value_type.h +++ b/include/ruby/internal/value_type.h @@ -443,7 +443,7 @@ Check_Type(VALUE v, enum ruby_value_type t) } unexpected_type: - rb_unexpected_type(v, t); + rb_unexpected_type(v, RBIMPL_CAST((int)t)); } #endif /* RBIMPL_VALUE_TYPE_H */ diff --git a/include/ruby/io.h b/include/ruby/io.h index 9b381fc9bb..e9dfeda5b1 100644 --- a/include/ruby/io.h +++ b/include/ruby/io.h @@ -137,40 +137,51 @@ struct rb_io_encoding { VALUE ecopts; }; +#ifndef HAVE_RB_IO_T +#define HAVE_RB_IO_T 1 /** Ruby's IO, metadata and buffers. */ -typedef struct rb_io { - +struct rb_io { /** The IO's Ruby level counterpart. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) VALUE self; /** stdio ptr for read/write, if available. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) FILE *stdio_file; /** file descriptor. */ + RBIMPL_ATTR_DEPRECATED(("rb_io_descriptor")) int fd; /** mode flags: FMODE_XXXs */ + RBIMPL_ATTR_DEPRECATED(("rb_io_mode")) int mode; /** child's pid (for pipes) */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) rb_pid_t pid; /** number of lines read */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) int lineno; /** pathname for file */ + RBIMPL_ATTR_DEPRECATED(("rb_io_path")) VALUE pathv; /** finalize proc */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) void (*finalize)(struct rb_io*,int); /** Write buffer. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) rb_io_buffer_t wbuf; /** * (Byte) read buffer. Note also that there is a field called * ::rb_io_t::cbuf, which also concerns read IO. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) rb_io_buffer_t rbuf; /** @@ -178,20 +189,25 @@ typedef struct rb_io { * * @see rb_io_set_write_io() */ + RBIMPL_ATTR_DEPRECATED(("rb_io_get_write_io")) VALUE tied_io_for_writing; + RBIMPL_ATTR_DEPRECATED(("with no replacement")) struct rb_io_encoding encs; /**< Decomposed encoding flags. */ /** Encoding converter used when reading from this IO. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) rb_econv_t *readconv; /** * rb_io_ungetc() destination. This buffer is read before checking * ::rb_io_t::rbuf */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) rb_io_buffer_t cbuf; /** Encoding converter used when writing to this IO. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) rb_econv_t *writeconv; /** @@ -200,21 +216,25 @@ typedef struct rb_io { * conversion from encoding X to encoding Y does not exist, Ruby finds an * encoding Z that bridges the two, so that X to Z to Y conversion happens. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) VALUE writeconv_asciicompat; /** Whether ::rb_io_t::writeconv is already set up. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) int writeconv_initialized; /** * Value of ::rb_io_t::rb_io_enc_t::ecflags stored right before * initialising ::rb_io_t::writeconv. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) int writeconv_pre_ecflags; /** * Value of ::rb_io_t::rb_io_enc_t::ecopts stored right before initialising * ::rb_io_t::writeconv. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) VALUE writeconv_pre_ecopts; /** @@ -224,27 +244,23 @@ typedef struct rb_io { * * This of course doesn't help inter-process IO interleaves, though. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) VALUE write_lock; /** * The timeout associated with this IO when performing blocking operations. */ + RBIMPL_ATTR_DEPRECATED(("rb_io_timeout/rb_io_set_timeout")) VALUE timeout; -} rb_io_t; +}; +#endif + +typedef struct rb_io rb_io_t; /** @alias{rb_io_enc_t} */ typedef struct rb_io_encoding rb_io_enc_t; /** - * @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 HAVE_RB_IO_T 1 - -/** * @name Possible flags for ::rb_io_t::mode * * @{ diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h index 737fafe518..e4d98bf051 100644 --- a/include/ruby/io/buffer.h +++ b/include/ruby/io/buffer.h @@ -23,10 +23,18 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() #define RUBY_IO_BUFFER_VERSION 2 +// The `IO::Buffer` class. RUBY_EXTERN VALUE rb_cIOBuffer; + +// The operating system page size. RUBY_EXTERN size_t RUBY_IO_BUFFER_PAGE_SIZE; + +// The default buffer size, usually a (small) multiple of the page size. +// Can be overridden by the RUBY_IO_BUFFER_DEFAULT_SIZE environment variable. RUBY_EXTERN size_t RUBY_IO_BUFFER_DEFAULT_SIZE; +// Represents the internal state of the buffer. +// More than one flag can be set at a time. enum rb_io_buffer_flags { // The memory in the buffer is owned by someone else. // More specifically, it means that someone else owns the buffer and we shouldn't try to resize it. @@ -49,21 +57,22 @@ enum rb_io_buffer_flags { RB_IO_BUFFER_PRIVATE = 64, // The buffer is read-only and cannot be modified. - RB_IO_BUFFER_READONLY = 128 + RB_IO_BUFFER_READONLY = 128, + + // The buffer is backed by a file. + RB_IO_BUFFER_FILE = 256, }; +// Represents the endian of the data types. enum rb_io_buffer_endian { + // The least significant units are put first. RB_IO_BUFFER_LITTLE_ENDIAN = 4, RB_IO_BUFFER_BIG_ENDIAN = 8, -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN, -#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#if defined(WORDS_BIGENDIAN) RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN, -#elif REG_DWORD == REG_DWORD_LITTLE_ENDIAN +#else RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN, -#elif REG_DWORD == REG_DWORD_BIG_ENDIAN - RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN, #endif RB_IO_BUFFER_NETWORK_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN @@ -79,7 +88,10 @@ int rb_io_buffer_try_unlock(VALUE self); VALUE rb_io_buffer_free(VALUE self); VALUE rb_io_buffer_free_locked(VALUE self); -int rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size); +// Access the internal buffer and flags. Validates the pointers. +// The points may not remain valid if the source buffer is manipulated. +// Consider using rb_io_buffer_lock if needed. +enum rb_io_buffer_flags rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size); void rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size); void rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size); diff --git a/include/ruby/memory_view.h b/include/ruby/memory_view.h index 1ddca2d46f..42309d5afc 100644 --- a/include/ruby/memory_view.h +++ b/include/ruby/memory_view.h @@ -47,10 +47,10 @@ typedef struct { char format; /** :FIXME: what is a "native" size is unclear. */ - unsigned native_size_p: 1; + bool native_size_p; /** Endian of the component */ - unsigned little_endian_p: 1; + bool little_endian_p; /** The component's offset. */ size_t offset; diff --git a/include/ruby/onigmo.h b/include/ruby/onigmo.h index 0a5400c3a5..d233336316 100644 --- a/include/ruby/onigmo.h +++ b/include/ruby/onigmo.h @@ -844,6 +844,8 @@ void onig_free(OnigRegex); ONIG_EXTERN void onig_free_body(OnigRegex); ONIG_EXTERN +int onig_reg_copy(OnigRegex* reg, OnigRegex orig_reg); +ONIG_EXTERN OnigPosition onig_scan(OnigRegex reg, const OnigUChar* str, const OnigUChar* end, OnigRegion* region, OnigOptionType option, int (*scan_callback)(OnigPosition, OnigPosition, OnigRegion*, void*), void* callback_arg); ONIG_EXTERN OnigPosition onig_search(OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* start, const OnigUChar* range, OnigRegion* region, OnigOptionType option); diff --git a/include/ruby/random.h b/include/ruby/random.h index 39bdb6f3e3..f3df0d96fb 100644 --- a/include/ruby/random.h +++ b/include/ruby/random.h @@ -11,7 +11,7 @@ * * This is a set of APIs to roll your own subclass of ::rb_cRandom. An * illustrative example of such PRNG can be found at - * `ext/-test-/ramdom/loop.c`. + * `ext/-test-/random/loop.c`. */ #include "ruby/ruby.h" diff --git a/include/ruby/re.h b/include/ruby/re.h index 3892d6e7f2..f86d6f26cf 100644 --- a/include/ruby/re.h +++ b/include/ruby/re.h @@ -18,6 +18,7 @@ #include <stdio.h> +#include "ruby/onigmo.h" #include "ruby/regex.h" #include "ruby/internal/core/rmatch.h" #include "ruby/internal/dllexport.h" @@ -126,6 +127,30 @@ VALUE rb_reg_quote(VALUE str); regex_t *rb_reg_prepare_re(VALUE re, VALUE str); /** + * Runs a regular expression match using function `match`. Performs preparation, + * error handling, and memory cleanup. + * + * @param[in] re Target regular expression. + * @param[in] str What `re` is about to run on. + * @param[in] match The function to run to match `str` against `re`. + * @param[in] args Pointer to arguments to pass into `match`. + * @param[out] regs Registers on a successful match. + * @exception rb_eArgError `re` does not fit for `str`. + * @exception rb_eEncCompatError `re` and `str` are incompatible. + * @exception rb_eRegexpError `re` is malformed. + * @return Match position on a successful match, `ONIG_MISMATCH` otherwise. + * + * @internal + * + * The type `regex_t *` is defined in `<ruby/onigmo.h>`, _and_ + * _conflicts_ with POSIX's `<regex.h>`. We can no longer save the situation + * at this point. Just don't mix the two. + */ +OnigPosition rb_reg_onig_match(VALUE re, VALUE str, + OnigPosition (*match)(regex_t *reg, VALUE str, struct re_registers *regs, void *args), + void *args, struct re_registers *regs); + +/** * Duplicates a match data. This is roughly the same as `onig_region_copy()`, * except it tries to GC when there is not enough memory. * diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index eb9a7e4d0f..035f02c70b 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -97,8 +97,10 @@ VALUE rb_get_path(VALUE obj); VALUE rb_get_path_no_checksafe(VALUE); /** - * @deprecated This macro is an alias of #FilePathValue now. The part that did - * "String" was deleted. It remains here because of no harm. + * This macro actually does the same thing as #FilePathValue now. The "String" + * part indicates that this is for when a string is treated like a pathname, + * rather than the actual pathname on the file systems. For examples: + * `Dir.fnmatch?`, `File.join`, `File.basename`, etc. */ #define FilePathStringValue(v) ((v) = rb_get_path(v)) @@ -270,6 +272,124 @@ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 0) */ int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap); +#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 inevitable. + * + * 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` invocations 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); + +/** + * 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 /**< System-provided original `errno`. */ +#undef errno +#define errno (*rb_errno_ptr()) /**< Ractor-aware version of `errno`. */ + +/** @} */ + + /** @cond INTERNAL_MACRO */ #if RBIMPL_HAS_WARNING("-Wgnu-zero-variadic-macro-arguments") # /* Skip it; clang -pedantic doesn't like the following */ diff --git a/include/ruby/st.h b/include/ruby/st.h index 0d42283364..f35ab43603 100644 --- a/include/ruby/st.h +++ b/include/ruby/st.h @@ -104,8 +104,6 @@ st_table *rb_st_init_table(const struct st_hash_type *); #define st_init_table rb_st_init_table st_table *rb_st_init_table_with_size(const struct st_hash_type *, st_index_t); #define st_init_table_with_size rb_st_init_table_with_size -st_table *rb_st_init_existing_table_with_size(st_table *tab, const struct st_hash_type *type, st_index_t size); -#define st_init_existing_table_with_size rb_st_init_existing_table_with_size st_table *rb_st_init_numtable(void); #define st_init_numtable rb_st_init_numtable st_table *rb_st_init_numtable_with_size(st_index_t); diff --git a/include/ruby/thread.h b/include/ruby/thread.h index 0b5b1ca0f3..337f477fd0 100644 --- a/include/ruby/thread.h +++ b/include/ruby/thread.h @@ -190,14 +190,59 @@ void *rb_nogvl(void *(*func)(void *), void *data1, */ #define RUBY_CALL_WO_GVL_FLAG_SKIP_CHECK_INTS_ -#define RUBY_INTERNAL_THREAD_EVENT_STARTED 1 << 0 /** thread started */ +/** + * 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 void rb_internal_thread_event_data_t; // for future extension. +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, @@ -211,7 +256,12 @@ typedef struct rb_internal_thread_event_hook rb_internal_thread_event_hook_t; * @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. + * @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( @@ -223,13 +273,53 @@ rb_internal_thread_event_hook_t *rb_internal_thread_add_event_hook( * Unregister the passed hook. * * @param[in] hook. The hook to unregister. - * @return Wether the hook was found and unregistered. - * @note This functionality is a noop on Windows. + * @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 */ diff --git a/include/ruby/util.h b/include/ruby/util.h index ee11bc940a..12e69c4b80 100644 --- a/include/ruby/util.h +++ b/include/ruby/util.h @@ -33,7 +33,9 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() -/** an approximation of ceil(n * log10(2)), up to 65536 at least */ +/** an approximation of ceil(n * log10(2)), up to 1,048,576 (1<<20) + * without overflow within 32-bit calculation + */ #define DECIMAL_SIZE_OF_BITS(n) (((n) * 3010 + 9998) / 9999) /** an approximation of decimal representation size for n-bytes */ diff --git a/include/ruby/version.h b/include/ruby/version.h index 08c0aadb07..e9113177de 100644 --- a/include/ruby/version.h +++ b/include/ruby/version.h @@ -67,7 +67,7 @@ * Minor version. As of writing this version changes annually. Greater * version doesn't mean "better"; they just mean years passed. */ -#define RUBY_API_VERSION_MINOR 3 +#define RUBY_API_VERSION_MINOR 4 /** * Teeny version. This digit is kind of reserved these days. Kept 0 for the diff --git a/include/ruby/vm.h b/include/ruby/vm.h index 3458c28be7..8779780952 100644 --- a/include/ruby/vm.h +++ b/include/ruby/vm.h @@ -49,6 +49,13 @@ int ruby_vm_destruct(ruby_vm_t *vm); */ void ruby_vm_at_exit(void(*func)(ruby_vm_t *)); +/** + * Returns whether the Ruby VM will free all memory at shutdown. + * + * @return true if free-at-exit is enabled, false otherwise. + */ +bool ruby_free_at_exit_p(void); + RBIMPL_SYMBOL_EXPORT_END() #endif /* RUBY_VM_H */ diff --git a/include/ruby/win32.h b/include/ruby/win32.h index dfb56f4182..27a3467606 100644 --- a/include/ruby/win32.h +++ b/include/ruby/win32.h @@ -35,6 +35,7 @@ extern "C++" { /* template without extern "C++" */ #endif #include <winsock2.h> #include <ws2tcpip.h> +#include <mswsock.h> #if !defined(_MSC_VER) || _MSC_VER >= 1400 #include <iphlpapi.h> #endif |