diff options
Diffstat (limited to 'include/ruby')
107 files changed, 3870 insertions, 2818 deletions
diff --git a/include/ruby/assert.h b/include/ruby/assert.h index c9f2c3fbef..acc5e5bbfc 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" @@ -103,7 +104,7 @@ # /* keep NDEBUG undefined */ #elif (RBIMPL_NDEBUG == 0) && (RBIMPL_RUBY_DEBUG == 0) -# /* The (*1) situation in avobe diagram. */ +# /* The (*1) situation in above diagram. */ # define RUBY_DEBUG 0 # define RUBY_NDEBUG 1 # define NDEBUG @@ -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. @@ -218,17 +298,19 @@ RBIMPL_SYMBOL_EXPORT_END() * @copydetails #RUBY_ASSERT */ #if RUBY_DEBUG -# define RBIMPL_ASSERT_OR_ASSUME(expr) RUBY_ASSERT_ALWAYS(expr) +# define RBIMPL_ASSERT_OR_ASSUME RUBY_ASSERT_ALWAYS +#elif ! defined(RBIMPL_VA_OPT_ARGS) +# define RBIMPL_ASSERT_OR_ASSUME(expr) RBIMPL_ASSUME(expr) #elif RBIMPL_COMPILER_BEFORE(Clang, 7, 0, 0) # /* See commit 67d259c5dccd31fe49d417fec169977712ffdf10 */ -# define RBIMPL_ASSERT_OR_ASSUME(expr) RBIMPL_ASSERT_NOTHING +# define RBIMPL_ASSERT_OR_ASSUME(...) RBIMPL_ASSERT_NOTHING #elif defined(RUBY_ASSERT_NOASSUME) # /* See commit d300a734414ef6de7e8eb563b7cc4389c455ed08 */ -# define RBIMPL_ASSERT_OR_ASSUME(expr) RBIMPL_ASSERT_NOTHING +# define RBIMPL_ASSERT_OR_ASSUME(...) RBIMPL_ASSERT_NOTHING #elif ! defined(RBIMPL_HAVE___ASSUME) -# define RBIMPL_ASSERT_OR_ASSUME(expr) RBIMPL_ASSERT_NOTHING +# define RBIMPL_ASSERT_OR_ASSUME(...) RBIMPL_ASSERT_NOTHING #else -# define RBIMPL_ASSERT_OR_ASSUME(expr) RBIMPL_ASSUME(expr) +# define RBIMPL_ASSERT_OR_ASSUME(expr, ...) RBIMPL_ASSUME(expr) #endif #endif /* RUBY_ASSERT_H */ diff --git a/include/ruby/atomic.h b/include/ruby/atomic.h index 3eb80fbf7d..fcc48f532c 100644 --- a/include/ruby/atomic.h +++ b/include/ruby/atomic.h @@ -34,7 +34,7 @@ # include <sys/types.h> /* ssize_t */ #endif -#if RBIMPL_COMPILER_SINCE(MSVC, 13, 0, 0) +#if RBIMPL_COMPILER_IS(MSVC) # pragma intrinsic(_InterlockedOr) #elif defined(__sun) && defined(HAVE_ATOMIC_H) # include <atomic.h> @@ -72,13 +72,40 @@ typedef unsigned int rb_atomic_t; #elif defined(HAVE_GCC_SYNC_BUILTINS) typedef unsigned int rb_atomic_t; #elif defined(_WIN32) +# include <winsock2.h> // to prevent macro redefinitions +# include <windows.h> // for `LONG` and `Interlocked` functions typedef LONG rb_atomic_t; #elif defined(__sun) && defined(HAVE_ATOMIC_H) typedef unsigned int rb_atomic_t; +#elif defined(HAVE_STDATOMIC_H) +# include <stdatomic.h> +typedef unsigned int rb_atomic_t; #else # error No atomic operation found #endif +/* Memory ordering constants */ +#if defined(HAVE_GCC_ATOMIC_BUILTINS) +# define RBIMPL_ATOMIC_RELAXED __ATOMIC_RELAXED +# define RBIMPL_ATOMIC_ACQUIRE __ATOMIC_ACQUIRE +# define RBIMPL_ATOMIC_RELEASE __ATOMIC_RELEASE +# define RBIMPL_ATOMIC_ACQ_REL __ATOMIC_ACQ_REL +# define RBIMPL_ATOMIC_SEQ_CST __ATOMIC_SEQ_CST +#elif defined(HAVE_STDATOMIC_H) +# define RBIMPL_ATOMIC_RELAXED memory_order_relaxed +# define RBIMPL_ATOMIC_ACQUIRE memory_order_acquire +# define RBIMPL_ATOMIC_RELEASE memory_order_release +# define RBIMPL_ATOMIC_ACQ_REL memory_order_acq_rel +# define RBIMPL_ATOMIC_SEQ_CST memory_order_seq_cst +#else +/* Dummy values for unsupported platforms */ +# define RBIMPL_ATOMIC_RELAXED 0 +# define RBIMPL_ATOMIC_ACQUIRE 1 +# define RBIMPL_ATOMIC_RELEASE 2 +# define RBIMPL_ATOMIC_ACQ_REL 3 +# define RBIMPL_ATOMIC_SEQ_CST 4 +#endif + /** * Atomically replaces the value pointed by `var` with the result of addition * of `val` to the old value of `var`. @@ -88,7 +115,7 @@ typedef unsigned int rb_atomic_t; * @return What was stored in `var` before the addition. * @post `var` holds `var + val`. */ -#define RUBY_ATOMIC_FETCH_ADD(var, val) rbimpl_atomic_fetch_add(&(var), (val)) +#define RUBY_ATOMIC_FETCH_ADD(var, val) rbimpl_atomic_fetch_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Atomically replaces the value pointed by `var` with the result of @@ -99,7 +126,7 @@ typedef unsigned int rb_atomic_t; * @return What was stored in `var` before the subtraction. * @post `var` holds `var - val`. */ -#define RUBY_ATOMIC_FETCH_SUB(var, val) rbimpl_atomic_fetch_sub(&(var), (val)) +#define RUBY_ATOMIC_FETCH_SUB(var, val) rbimpl_atomic_fetch_sub(&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Atomically replaces the value pointed by `var` with the result of @@ -111,7 +138,7 @@ typedef unsigned int rb_atomic_t; * @post `var` holds `var | val`. * @note For portability, this macro can return void. */ -#define RUBY_ATOMIC_OR(var, val) rbimpl_atomic_or(&(var), (val)) +#define RUBY_ATOMIC_OR(var, val) rbimpl_atomic_or(&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Atomically replaces the value pointed by `var` with `val`. This is just an @@ -122,7 +149,7 @@ typedef unsigned int rb_atomic_t; * @return What was stored in `var` before the assignment. * @post `var` holds `val`. */ -#define RUBY_ATOMIC_EXCHANGE(var, val) rbimpl_atomic_exchange(&(var), (val)) +#define RUBY_ATOMIC_EXCHANGE(var, val) rbimpl_atomic_exchange(&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Atomic compare-and-swap. This stores `val` to `var` if and only if the @@ -136,7 +163,16 @@ typedef unsigned int rb_atomic_t; * @retval otherwise Something else is at `var`; not updated. */ #define RUBY_ATOMIC_CAS(var, oldval, newval) \ - rbimpl_atomic_cas(&(var), (oldval), (newval)) + rbimpl_atomic_cas(&(var), (oldval), (newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST) + +/** + * 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), RBIMPL_ATOMIC_SEQ_CST) /** * Identical to #RUBY_ATOMIC_EXCHANGE, except for the return type. @@ -146,7 +182,7 @@ typedef unsigned int rb_atomic_t; * @return void * @post `var` holds `val`. */ -#define RUBY_ATOMIC_SET(var, val) rbimpl_atomic_set(&(var), (val)) +#define RUBY_ATOMIC_SET(var, val) rbimpl_atomic_store(&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Identical to #RUBY_ATOMIC_FETCH_ADD, except for the return type. @@ -156,7 +192,7 @@ typedef unsigned int rb_atomic_t; * @return void * @post `var` holds `var + val`. */ -#define RUBY_ATOMIC_ADD(var, val) rbimpl_atomic_add(&(var), (val)) +#define RUBY_ATOMIC_ADD(var, val) rbimpl_atomic_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Identical to #RUBY_ATOMIC_FETCH_SUB, except for the return type. @@ -166,7 +202,7 @@ typedef unsigned int rb_atomic_t; * @return void * @post `var` holds `var - val`. */ -#define RUBY_ATOMIC_SUB(var, val) rbimpl_atomic_sub(&(var), (val)) +#define RUBY_ATOMIC_SUB(var, val) rbimpl_atomic_sub(&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Atomically increments the value pointed by `var`. @@ -175,7 +211,7 @@ typedef unsigned int rb_atomic_t; * @return void * @post `var` holds `var + 1`. */ -#define RUBY_ATOMIC_INC(var) rbimpl_atomic_inc(&(var)) +#define RUBY_ATOMIC_INC(var) rbimpl_atomic_inc(&(var), RBIMPL_ATOMIC_SEQ_CST) /** * Atomically decrements the value pointed by `var`. @@ -184,7 +220,19 @@ typedef unsigned int rb_atomic_t; * @return void * @post `var` holds `var - 1`. */ -#define RUBY_ATOMIC_DEC(var) rbimpl_atomic_dec(&(var)) +#define RUBY_ATOMIC_DEC(var) rbimpl_atomic_dec(&(var), RBIMPL_ATOMIC_SEQ_CST) + +/** + * Identical to #RUBY_ATOMIC_FETCH_ADD, except it expects its arguments to be `size_t`. + * There are cases where ::rb_atomic_t is 32bit while `size_t` is 64bit. This + * should be used for size related operations to support such platforms. + * + * @param var A variable of `size_t`. + * @param val Value to add. + * @return What was stored in `var` before the addition. + * @post `var` holds `var + val`. + */ +#define RUBY_ATOMIC_SIZE_FETCH_ADD(var, val) rbimpl_atomic_size_fetch_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Identical to #RUBY_ATOMIC_INC, except it expects its argument is `size_t`. @@ -195,7 +243,7 @@ typedef unsigned int rb_atomic_t; * @return void * @post `var` holds `var + 1`. */ -#define RUBY_ATOMIC_SIZE_INC(var) rbimpl_atomic_size_inc(&(var)) +#define RUBY_ATOMIC_SIZE_INC(var) rbimpl_atomic_size_inc(&(var), RBIMPL_ATOMIC_SEQ_CST) /** * Identical to #RUBY_ATOMIC_DEC, except it expects its argument is `size_t`. @@ -206,7 +254,7 @@ typedef unsigned int rb_atomic_t; * @return void * @post `var` holds `var - 1`. */ -#define RUBY_ATOMIC_SIZE_DEC(var) rbimpl_atomic_size_dec(&(var)) +#define RUBY_ATOMIC_SIZE_DEC(var) rbimpl_atomic_size_dec(&(var), RBIMPL_ATOMIC_SEQ_CST) /** * Identical to #RUBY_ATOMIC_EXCHANGE, except it expects its arguments are @@ -220,7 +268,7 @@ typedef unsigned int rb_atomic_t; * @post `var` holds `val`. */ #define RUBY_ATOMIC_SIZE_EXCHANGE(var, val) \ - rbimpl_atomic_size_exchange(&(var), (val)) + rbimpl_atomic_size_exchange(&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Identical to #RUBY_ATOMIC_CAS, except it expects its arguments are `size_t`. @@ -234,7 +282,7 @@ typedef unsigned int rb_atomic_t; * @retval otherwise Something else is at `var`; not updated. */ #define RUBY_ATOMIC_SIZE_CAS(var, oldval, newval) \ - rbimpl_atomic_size_cas(&(var), (oldval), (newval)) + rbimpl_atomic_size_cas(&(var), (oldval), (newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST) /** * Identical to #RUBY_ATOMIC_ADD, except it expects its arguments are `size_t`. @@ -246,7 +294,7 @@ typedef unsigned int rb_atomic_t; * @return void * @post `var` holds `var + val`. */ -#define RUBY_ATOMIC_SIZE_ADD(var, val) rbimpl_atomic_size_add(&(var), (val)) +#define RUBY_ATOMIC_SIZE_ADD(var, val) rbimpl_atomic_size_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Identical to #RUBY_ATOMIC_SUB, except it expects its arguments are `size_t`. @@ -258,7 +306,7 @@ typedef unsigned int rb_atomic_t; * @return void * @post `var` holds `var - val`. */ -#define RUBY_ATOMIC_SIZE_SUB(var, val) rbimpl_atomic_size_sub(&(var), (val)) +#define RUBY_ATOMIC_SIZE_SUB(var, val) rbimpl_atomic_size_sub(&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Identical to #RUBY_ATOMIC_EXCHANGE, except it expects its arguments are @@ -277,7 +325,31 @@ typedef unsigned int rb_atomic_t; * some pointers, most notably function pointers. */ #define RUBY_ATOMIC_PTR_EXCHANGE(var, val) \ - RBIMPL_CAST(rbimpl_atomic_ptr_exchange((void **)&(var), (void *)val)) + RBIMPL_CAST(rbimpl_atomic_ptr_exchange((void **)&(var), (void *)val, RBIMPL_ATOMIC_SEQ_CST)) + +/** + * 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, RBIMPL_ATOMIC_SEQ_CST)) + +/** +* Identical to #RUBY_ATOMIC_SET, except it expects its arguments are +* `void*`. There are cases where ::rb_atomic_t is 32bit while ::VALUE is +* 64bit. This should be used for pointer related operations to support such +* platforms. +* +* @param var A variable of `void*`. +* @param val Value to set. +* @post `var` holds `val`. +*/ +#define RUBY_ATOMIC_PTR_SET(var, val) \ + rbimpl_atomic_ptr_store((volatile void **)&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Identical to #RUBY_ATOMIC_CAS, except it expects its arguments are `void*`. @@ -291,7 +363,20 @@ typedef unsigned int rb_atomic_t; * @retval otherwise Something else is at `var`; not updated. */ #define RUBY_ATOMIC_PTR_CAS(var, oldval, newval) \ - RBIMPL_CAST(rbimpl_atomic_ptr_cas((void **)&(var), (oldval), (newval))) + RBIMPL_CAST(rbimpl_atomic_ptr_cas((void **)&(var), (void *)(oldval), (void *)(newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST)) + +/** + * Identical to #RUBY_ATOMIC_SET, except it expects its arguments are + * ::VALUE. There are cases where ::rb_atomic_t is 32bit while ::VALUE is + * 64bit. This should be used for pointer related operations to support such + * platforms. + * + * @param var A variable of ::VALUE. + * @param val Value to set. + * @post `var` holds `val`. + */ +#define RUBY_ATOMIC_VALUE_SET(var, val) \ + rbimpl_atomic_value_store(&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Identical to #RUBY_ATOMIC_EXCHANGE, except it expects its arguments are @@ -305,7 +390,7 @@ typedef unsigned int rb_atomic_t; * @post `var` holds `val`. */ #define RUBY_ATOMIC_VALUE_EXCHANGE(var, val) \ - rbimpl_atomic_value_exchange(&(var), (val)) + rbimpl_atomic_value_exchange(&(var), (val), RBIMPL_ATOMIC_SEQ_CST) /** * Identical to #RUBY_ATOMIC_CAS, except it expects its arguments are ::VALUE. @@ -319,19 +404,20 @@ typedef unsigned int rb_atomic_t; * @retval otherwise Something else is at `var`; not updated. */ #define RUBY_ATOMIC_VALUE_CAS(var, oldval, newval) \ - rbimpl_atomic_value_cas(&(var), (oldval), (newval)) + rbimpl_atomic_value_cas(&(var), (oldval), (newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST) /** @cond INTERNAL_MACRO */ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline rb_atomic_t -rbimpl_atomic_fetch_add(volatile rb_atomic_t *ptr, rb_atomic_t val) +rbimpl_atomic_fetch_add(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) - return __atomic_fetch_add(ptr, val, __ATOMIC_SEQ_CST); + return __atomic_fetch_add(ptr, val, memory_order); #elif defined(HAVE_GCC_SYNC_BUILTINS) return __sync_fetch_and_add(ptr, val); @@ -348,6 +434,47 @@ rbimpl_atomic_fetch_add(volatile rb_atomic_t *ptr, rb_atomic_t val) RBIMPL_ASSERT_OR_ASSUME(val <= INT_MAX); return atomic_add_int_nv(ptr, val) - val; +#elif defined(HAVE_STDATOMIC_H) + return atomic_fetch_add_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order); + +#else +# error Unsupported platform. +#endif +} + +/** @cond INTERNAL_MACRO */ +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NOALIAS() +RBIMPL_ATTR_NONNULL((1)) +static inline size_t +rbimpl_atomic_size_fetch_add(volatile size_t *ptr, size_t val, int memory_order) +{ + (void)memory_order; +#if 0 + +#elif defined(HAVE_GCC_ATOMIC_BUILTINS) + return __atomic_fetch_add(ptr, val, memory_order); + +#elif defined(HAVE_GCC_SYNC_BUILTINS) + return __sync_fetch_and_add(ptr, val); + +#elif defined(_WIN32) + return InterlockedExchangeAdd64(ptr, val); + +#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx)) + /* Ditto for `atomic_add_int_nv`. */ + RBIMPL_ASSERT_OR_ASSUME(val <= LONG_MAX); + atomic_add_long(ptr, val); + +#elif defined(__sun) && defined(HAVE_ATOMIC_H) + RBIMPL_STATIC_ASSERT(size_of_rb_atomic_t, sizeof *ptr == sizeof(rb_atomic_t)); + + volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr); + rbimpl_atomic_fetch_add(tmp, val, memory_order); + +#elif defined(HAVE_STDATOMIC_H) + return atomic_fetch_add_explicit((_Atomic volatile size_t *)ptr, val, memory_order); + #else # error Unsupported platform. #endif @@ -357,8 +484,9 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline void -rbimpl_atomic_add(volatile rb_atomic_t *ptr, rb_atomic_t val) +rbimpl_atomic_add(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) @@ -367,7 +495,7 @@ rbimpl_atomic_add(volatile rb_atomic_t *ptr, rb_atomic_t val) * return value is not used, then compiles it into single `LOCK ADD` * instruction. */ - __atomic_add_fetch(ptr, val, __ATOMIC_SEQ_CST); + __atomic_add_fetch(ptr, val, memory_order); #elif defined(HAVE_GCC_SYNC_BUILTINS) __sync_add_and_fetch(ptr, val); @@ -385,6 +513,9 @@ rbimpl_atomic_add(volatile rb_atomic_t *ptr, rb_atomic_t val) RBIMPL_ASSERT_OR_ASSUME(val <= INT_MAX); atomic_add_int(ptr, val); +#elif defined(HAVE_STDATOMIC_H) + atomic_fetch_add_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order); + #else # error Unsupported platform. #endif @@ -394,17 +525,18 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline void -rbimpl_atomic_size_add(volatile size_t *ptr, size_t val) +rbimpl_atomic_size_add(volatile size_t *ptr, size_t val, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) - __atomic_add_fetch(ptr, val, __ATOMIC_SEQ_CST); + __atomic_add_fetch(ptr, val, memory_order); #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); @@ -413,12 +545,17 @@ rbimpl_atomic_size_add(volatile size_t *ptr, size_t val) RBIMPL_ASSERT_OR_ASSUME(val <= LONG_MAX); atomic_add_long(ptr, val); -#else +#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H)) RBIMPL_STATIC_ASSERT(size_of_rb_atomic_t, sizeof *ptr == sizeof(rb_atomic_t)); volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr); - rbimpl_atomic_add(tmp, val); + rbimpl_atomic_add(tmp, val, memory_order); +#elif defined(HAVE_STDATOMIC_H) + atomic_fetch_add_explicit((_Atomic volatile size_t *)ptr, val, memory_order); + +#else +# error Unsupported platform. #endif } @@ -426,12 +563,13 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline void -rbimpl_atomic_inc(volatile rb_atomic_t *ptr) +rbimpl_atomic_inc(volatile rb_atomic_t *ptr, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS) - rbimpl_atomic_add(ptr, 1); + rbimpl_atomic_add(ptr, 1, memory_order); #elif defined(_WIN32) InterlockedIncrement(ptr); @@ -439,9 +577,11 @@ rbimpl_atomic_inc(volatile rb_atomic_t *ptr) #elif defined(__sun) && defined(HAVE_ATOMIC_H) atomic_inc_uint(ptr); -#else - rbimpl_atomic_add(ptr, 1); +#elif defined(HAVE_STDATOMIC_H) + rbimpl_atomic_add(ptr, 1, memory_order); +#else +# error Unsupported platform. #endif } @@ -449,22 +589,30 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline void -rbimpl_atomic_size_inc(volatile size_t *ptr) +rbimpl_atomic_size_inc(volatile size_t *ptr, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS) - rbimpl_atomic_size_add(ptr, 1); + rbimpl_atomic_size_add(ptr, 1, memory_order); -#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_atomic_size_add(ptr, 1); +#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H)) + RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t)); + + rbimpl_atomic_size_add(ptr, 1, memory_order); +#elif defined(HAVE_STDATOMIC_H) + rbimpl_atomic_size_add(ptr, 1, memory_order); + +#else +# error Unsupported platform. #endif } @@ -472,12 +620,13 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline rb_atomic_t -rbimpl_atomic_fetch_sub(volatile rb_atomic_t *ptr, rb_atomic_t val) +rbimpl_atomic_fetch_sub(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) - return __atomic_fetch_sub(ptr, val, __ATOMIC_SEQ_CST); + return __atomic_fetch_sub(ptr, val, memory_order); #elif defined(HAVE_GCC_SYNC_BUILTINS) return __sync_fetch_and_sub(ptr, val); @@ -492,6 +641,9 @@ rbimpl_atomic_fetch_sub(volatile rb_atomic_t *ptr, rb_atomic_t val) RBIMPL_ASSERT_OR_ASSUME(val <= INT_MAX); return atomic_add_int_nv(ptr, neg * val) + val; +#elif defined(HAVE_STDATOMIC_H) + return atomic_fetch_sub_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order); + #else # error Unsupported platform. #endif @@ -501,12 +653,13 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline void -rbimpl_atomic_sub(volatile rb_atomic_t *ptr, rb_atomic_t val) +rbimpl_atomic_sub(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) - __atomic_sub_fetch(ptr, val, __ATOMIC_SEQ_CST); + __atomic_sub_fetch(ptr, val, memory_order); #elif defined(HAVE_GCC_SYNC_BUILTINS) __sync_sub_and_fetch(ptr, val); @@ -519,6 +672,9 @@ rbimpl_atomic_sub(volatile rb_atomic_t *ptr, rb_atomic_t val) RBIMPL_ASSERT_OR_ASSUME(val <= INT_MAX); atomic_add_int(ptr, neg * val); +#elif defined(HAVE_STDATOMIC_H) + atomic_fetch_sub_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order); + #else # error Unsupported platform. #endif @@ -528,17 +684,18 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline void -rbimpl_atomic_size_sub(volatile size_t *ptr, size_t val) +rbimpl_atomic_size_sub(volatile size_t *ptr, size_t val, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) - __atomic_sub_fetch(ptr, val, __ATOMIC_SEQ_CST); + __atomic_sub_fetch(ptr, val, memory_order); #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); @@ -547,12 +704,17 @@ rbimpl_atomic_size_sub(volatile size_t *ptr, size_t val) RBIMPL_ASSERT_OR_ASSUME(val <= LONG_MAX); atomic_add_long(ptr, neg * val); -#else +#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H)) RBIMPL_STATIC_ASSERT(size_of_rb_atomic_t, sizeof *ptr == sizeof(rb_atomic_t)); volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr); - rbimpl_atomic_sub(tmp, val); + rbimpl_atomic_sub(tmp, val, memory_order); +#elif defined(HAVE_STDATOMIC_H) + atomic_fetch_sub_explicit((_Atomic volatile size_t *)ptr, val, memory_order); + +#else +# error Unsupported platform. #endif } @@ -560,12 +722,13 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline void -rbimpl_atomic_dec(volatile rb_atomic_t *ptr) +rbimpl_atomic_dec(volatile rb_atomic_t *ptr, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS) - rbimpl_atomic_sub(ptr, 1); + rbimpl_atomic_sub(ptr, 1, memory_order); #elif defined(_WIN32) InterlockedDecrement(ptr); @@ -573,9 +736,11 @@ rbimpl_atomic_dec(volatile rb_atomic_t *ptr) #elif defined(__sun) && defined(HAVE_ATOMIC_H) atomic_dec_uint(ptr); -#else - rbimpl_atomic_sub(ptr, 1); +#elif defined(HAVE_STDATOMIC_H) + rbimpl_atomic_sub(ptr, 1, memory_order); +#else +# error Unsupported platform. #endif } @@ -583,22 +748,30 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline void -rbimpl_atomic_size_dec(volatile size_t *ptr) +rbimpl_atomic_size_dec(volatile size_t *ptr, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS) - rbimpl_atomic_size_sub(ptr, 1); + rbimpl_atomic_size_sub(ptr, 1, memory_order); -#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_atomic_size_sub(ptr, 1); +#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H)) + RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t)); + + rbimpl_atomic_size_sub(ptr, 1, memory_order); +#elif defined(HAVE_STDATOMIC_H) + rbimpl_atomic_size_sub(ptr, 1, memory_order); + +#else +# error Unsupported platform. #endif } @@ -606,59 +779,42 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline void -rbimpl_atomic_or(volatile rb_atomic_t *ptr, rb_atomic_t val) +rbimpl_atomic_or(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) - __atomic_or_fetch(ptr, val, __ATOMIC_SEQ_CST); + __atomic_or_fetch(ptr, val, memory_order); #elif defined(HAVE_GCC_SYNC_BUILTINS) __sync_or_and_fetch(ptr, val); -#elif RBIMPL_COMPILER_SINCE(MSVC, 13, 0, 0) +#elif RBIMPL_COMPILER_IS(MSVC) _InterlockedOr(ptr, val); -#elif defined(_WIN32) && defined(__GNUC__) - /* This was for old MinGW. Maybe not needed any longer? */ - __asm__( - "lock\n\t" - "orl\t%1, %0" - : "=m"(ptr) - : "Ir"(val)); - -#elif defined(_WIN32) && defined(_M_IX86) - __asm mov eax, ptr; - __asm mov ecx, val; - __asm lock or [eax], ecx; - #elif defined(__sun) && defined(HAVE_ATOMIC_H) atomic_or_uint(ptr, val); +#elif !defined(_WIN32) && defined(HAVE_STDATOMIC_H) + atomic_fetch_or_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order); + #else # error Unsupported platform. #endif } -/* Nobody uses this but for theoretical backwards compatibility... */ -#if RBIMPL_COMPILER_BEFORE(MSVC, 13, 0, 0) -static inline rb_atomic_t -rb_w32_atomic_or(volatile rb_atomic_t *var, rb_atomic_t val) -{ - return rbimpl_atomic_or(var, val); -} -#endif - RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline rb_atomic_t -rbimpl_atomic_exchange(volatile rb_atomic_t *ptr, rb_atomic_t val) +rbimpl_atomic_exchange(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) - return __atomic_exchange_n(ptr, val, __ATOMIC_SEQ_CST); + return __atomic_exchange_n(ptr, val, memory_order); #elif defined(HAVE_GCC_SYNC_BUILTINS) return __sync_lock_test_and_set(ptr, val); @@ -669,6 +825,9 @@ rbimpl_atomic_exchange(volatile rb_atomic_t *ptr, rb_atomic_t val) #elif defined(__sun) && defined(HAVE_ATOMIC_H) return atomic_swap_uint(ptr, val); +#elif defined(HAVE_STDATOMIC_H) + return atomic_exchange_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order); + #else # error Unsupported platform. #endif @@ -678,29 +837,53 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline size_t -rbimpl_atomic_size_exchange(volatile size_t *ptr, size_t val) +rbimpl_atomic_size_exchange(volatile size_t *ptr, size_t val, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) - return __atomic_exchange_n(ptr, val, __ATOMIC_SEQ_CST); + return __atomic_exchange_n(ptr, val, memory_order); #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)) return atomic_swap_ulong(ptr, val); -#else +#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H)) RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t)); volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr); - const rb_atomic_t ret = rbimpl_atomic_exchange(tmp, val); + const rb_atomic_t ret = rbimpl_atomic_exchange(tmp, val, memory_order); return RBIMPL_CAST((size_t)ret); +#elif defined(HAVE_STDATOMIC_H) + return atomic_exchange_explicit((_Atomic volatile size_t *)ptr, val, memory_order); + +#else +# error Unsupported platform. +#endif +} + +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NOALIAS() +RBIMPL_ATTR_NONNULL((1)) +static inline void +rbimpl_atomic_size_store(volatile size_t *ptr, size_t val, int memory_order) +{ + (void)memory_order; +#if 0 + +#elif defined(HAVE_GCC_ATOMIC_BUILTINS) + __atomic_store_n(ptr, val, memory_order); + +#else + rbimpl_atomic_size_exchange(ptr, val, memory_order); + #endif } @@ -708,8 +891,9 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline void * -rbimpl_atomic_ptr_exchange(void *volatile *ptr, const void *val) +rbimpl_atomic_ptr_exchange(void *volatile *ptr, const void *val, int memory_order) { + (void)memory_order; #if 0 #elif defined(InterlockedExchangePointer) @@ -726,7 +910,7 @@ rbimpl_atomic_ptr_exchange(void *volatile *ptr, const void *val) const size_t sval = RBIMPL_CAST((size_t)val); volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr); - const size_t sret = rbimpl_atomic_size_exchange(sptr, sval); + const size_t sret = rbimpl_atomic_size_exchange(sptr, sval, memory_order); return RBIMPL_CAST((void *)sret); #endif @@ -735,14 +919,27 @@ rbimpl_atomic_ptr_exchange(void *volatile *ptr, const void *val) RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) +static inline void +rbimpl_atomic_ptr_store(volatile void **ptr, void *val, int memory_order) +{ + RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t)); + + const size_t sval = RBIMPL_CAST((size_t)val); + volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr); + rbimpl_atomic_size_store(sptr, sval, memory_order); +} + +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NOALIAS() +RBIMPL_ATTR_NONNULL((1)) static inline VALUE -rbimpl_atomic_value_exchange(volatile VALUE *ptr, VALUE val) +rbimpl_atomic_value_exchange(volatile VALUE *ptr, VALUE val, int memory_order) { RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t)); const size_t sval = RBIMPL_CAST((size_t)val); volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr); - const size_t sret = rbimpl_atomic_size_exchange(sptr, sval); + const size_t sret = rbimpl_atomic_size_exchange(sptr, sval, memory_order); return RBIMPL_CAST((VALUE)sret); } @@ -750,16 +947,46 @@ 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) +rbimpl_atomic_value_store(volatile VALUE *ptr, VALUE val, int memory_order) +{ + RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t)); + + const size_t sval = RBIMPL_CAST((size_t)val); + volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr); + rbimpl_atomic_size_store(sptr, sval, memory_order); +} + +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NOALIAS() +RBIMPL_ATTR_NONNULL((1)) +static inline rb_atomic_t +rbimpl_atomic_load(volatile rb_atomic_t *ptr, int memory_order) { + (void)memory_order; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) - __atomic_store_n(ptr, val, __ATOMIC_SEQ_CST); + return __atomic_load_n(ptr, memory_order); +#else + return rbimpl_atomic_fetch_add(ptr, 0, memory_order); +#endif +} + +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NOALIAS() +RBIMPL_ATTR_NONNULL((1)) +static inline void +rbimpl_atomic_store(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order) +{ + (void)memory_order; +#if 0 + +#elif defined(HAVE_GCC_ATOMIC_BUILTINS) + __atomic_store_n(ptr, val, memory_order); #else /* Maybe std::atomic<rb_atomic_t>::store can be faster? */ - rbimpl_atomic_exchange(ptr, val); + rbimpl_atomic_exchange(ptr, val, memory_order); #endif } @@ -768,73 +995,73 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline rb_atomic_t -rbimpl_atomic_cas(volatile rb_atomic_t *ptr, rb_atomic_t oldval, rb_atomic_t newval) +rbimpl_atomic_cas(volatile rb_atomic_t *ptr, rb_atomic_t oldval, rb_atomic_t newval, int success_memorder, int failure_memorder) { + (void)success_memorder; + (void)failure_memorder; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) __atomic_compare_exchange_n( - ptr, &oldval, newval, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + ptr, &oldval, newval, 0, success_memorder, failure_memorder); return oldval; #elif defined(HAVE_GCC_SYNC_BUILTINS) return __sync_val_compare_and_swap(ptr, oldval, newval); -#elif RBIMPL_COMPILER_SINCE(MSVC, 13, 0, 0) +#elif RBIMPL_COMPILER_IS(MSVC) return InterlockedCompareExchange(ptr, newval, oldval); -#elif defined(_WIN32) - PVOID *pptr = RBIMPL_CAST((PVOID *)ptr); - PVOID pold = RBIMPL_CAST((PVOID)oldval); - PVOID pnew = RBIMPL_CAST((PVOID)newval); - PVOID pret = InterlockedCompareExchange(pptr, pnew, pold); - return RBIMPL_CAST((rb_atomic_t)pret); - #elif defined(__sun) && defined(HAVE_ATOMIC_H) return atomic_cas_uint(ptr, oldval, newval); +#elif defined(HAVE_STDATOMIC_H) + atomic_compare_exchange_strong_explicit( + (_Atomic volatile rb_atomic_t *)ptr, &oldval, newval, success_memorder, failure_memorder); + return oldval; + #else # error Unsupported platform. #endif } -/* Nobody uses this but for theoretical backwards compatibility... */ -#if RBIMPL_COMPILER_BEFORE(MSVC, 13, 0, 0) -static inline rb_atomic_t -rb_w32_atomic_cas(volatile rb_atomic_t *var, rb_atomic_t oldval, rb_atomic_t newval) -{ - return rbimpl_atomic_cas(var, oldval, newval); -} -#endif - RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline size_t -rbimpl_atomic_size_cas(volatile size_t *ptr, size_t oldval, size_t newval) +rbimpl_atomic_size_cas(volatile size_t *ptr, size_t oldval, size_t newval, int success_memorder, int failure_memorder) { + (void)success_memorder; + (void)failure_memorder; #if 0 #elif defined(HAVE_GCC_ATOMIC_BUILTINS) __atomic_compare_exchange_n( - ptr, &oldval, newval, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + ptr, &oldval, newval, 0, success_memorder, failure_memorder); return oldval; #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)) return atomic_cas_ulong(ptr, oldval, newval); -#else +#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H)) RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t)); volatile rb_atomic_t *tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr); - return rbimpl_atomic_cas(tmp, oldval, newval); + return rbimpl_atomic_cas(tmp, oldval, newval, success_memorder, failure_memorder); + +#elif defined(HAVE_STDATOMIC_H) + atomic_compare_exchange_strong_explicit( + (_Atomic volatile size_t *)ptr, &oldval, newval, success_memorder, failure_memorder); + return oldval; +#else +# error Unsupported platform. #endif } @@ -842,8 +1069,10 @@ RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) static inline void * -rbimpl_atomic_ptr_cas(void **ptr, const void *oldval, const void *newval) +rbimpl_atomic_ptr_cas(void **ptr, const void *oldval, const void *newval, int success_memorder, int failure_memorder) { + (void)success_memorder; + (void)failure_memorder; #if 0 #elif defined(InterlockedExchangePointer) @@ -866,7 +1095,7 @@ rbimpl_atomic_ptr_cas(void **ptr, const void *oldval, const void *newval) const size_t snew = RBIMPL_CAST((size_t)newval); const size_t sold = RBIMPL_CAST((size_t)oldval); volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr); - const size_t sret = rbimpl_atomic_size_cas(sptr, sold, snew); + const size_t sret = rbimpl_atomic_size_cas(sptr, sold, snew, success_memorder, failure_memorder); return RBIMPL_CAST((void *)sret); #endif @@ -875,15 +1104,41 @@ 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, int memory_order) +{ + (void)memory_order; +#if 0 + +#elif defined(HAVE_GCC_ATOMIC_BUILTINS) + return __atomic_load_n(ptr, memory_order); +#else + void *val = *ptr; + return rbimpl_atomic_ptr_cas(ptr, val, val, memory_order, memory_order); +#endif +} + +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NOALIAS() +RBIMPL_ATTR_NONNULL((1)) +static inline VALUE +rbimpl_atomic_value_load(volatile VALUE *ptr, int memory_order) +{ + return RBIMPL_CAST((VALUE)rbimpl_atomic_ptr_load((void **)ptr, memory_order)); +} + +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NOALIAS() +RBIMPL_ATTR_NONNULL((1)) static inline VALUE -rbimpl_atomic_value_cas(volatile VALUE *ptr, VALUE oldval, VALUE newval) +rbimpl_atomic_value_cas(volatile VALUE *ptr, VALUE oldval, VALUE newval, int success_memorder, int failure_memorder) { RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t)); const size_t snew = RBIMPL_CAST((size_t)newval); const size_t sold = RBIMPL_CAST((size_t)oldval); volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr); - const size_t sret = rbimpl_atomic_size_cas(sptr, sold, snew); + const size_t sret = rbimpl_atomic_size_cas(sptr, sold, snew, success_memorder, failure_memorder); return RBIMPL_CAST((VALUE)sret); } /** @endcond */ diff --git a/include/ruby/backward.h b/include/ruby/backward.h index f804c2c36e..6726102158 100644 --- a/include/ruby/backward.h +++ b/include/ruby/backward.h @@ -11,12 +11,6 @@ #include "ruby/internal/interpreter.h" #include "ruby/backward/2/attributes.h" -#define RBIMPL_ATTR_DEPRECATED_SINCE(ver) RBIMPL_ATTR_DEPRECATED(("since " #ver)) -#define RBIMPL_ATTR_DEPRECATED_INTERNAL(ver) RBIMPL_ATTR_DEPRECATED(("since "#ver", also internal")) -#define RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY() RBIMPL_ATTR_DEPRECATED(("only for internal use")) - -RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY() void rb_clear_constant_cache(void); - /* from version.c */ #if defined(RUBY_SHOW_COPYRIGHT_TO_DIE) && !!(RUBY_SHOW_COPYRIGHT_TO_DIE+0) # error RUBY_SHOW_COPYRIGHT_TO_DIE is deprecated diff --git a/include/ruby/backward/2/attributes.h b/include/ruby/backward/2/attributes.h index 73acfc9dc0..916d9e9d5b 100644 --- a/include/ruby/backward/2/attributes.h +++ b/include/ruby/backward/2/attributes.h @@ -39,6 +39,7 @@ #include "ruby/internal/attr/noinline.h" #include "ruby/internal/attr/nonnull.h" #include "ruby/internal/attr/noreturn.h" +#include "ruby/internal/attr/packed_struct.h" #include "ruby/internal/attr/pure.h" #include "ruby/internal/attr/restrict.h" #include "ruby/internal/attr/returns_nonnull.h" @@ -80,10 +81,8 @@ #undef NOINLINE #define NOINLINE(x) RBIMPL_ATTR_NOINLINE() x -#ifndef MJIT_HEADER -# undef ALWAYS_INLINE -# define ALWAYS_INLINE(x) RBIMPL_ATTR_FORCEINLINE() x -#endif +#undef ALWAYS_INLINE +#define ALWAYS_INLINE(x) RBIMPL_ATTR_FORCEINLINE() x #undef ERRORFUNC #define ERRORFUNC(mesg, x) RBIMPL_ATTR_ERROR(mesg) x @@ -147,17 +146,14 @@ #define NORETURN(x) RBIMPL_ATTR_NORETURN() x #define NORETURN_STYLE_NEW -#ifndef PACKED_STRUCT -# define PACKED_STRUCT(x) x -#endif +#undef PACKED_STRUCT +#define PACKED_STRUCT(x) \ + RBIMPL_ATTR_PACKED_STRUCT_BEGIN() x RBIMPL_ATTR_PACKED_STRUCT_END() -#ifndef PACKED_STRUCT_UNALIGNED -# if UNALIGNED_WORD_ACCESS -# define PACKED_STRUCT_UNALIGNED(x) PACKED_STRUCT(x) -# else -# define PACKED_STRUCT_UNALIGNED(x) x -# endif -#endif +#undef PACKED_STRUCT_UNALIGNED +#define PACKED_STRUCT_UNALIGNED(x) \ + RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN() x \ + RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END() #undef RB_UNUSED_VAR #define RB_UNUSED_VAR(x) x RBIMPL_ATTR_MAYBE_UNUSED() diff --git a/include/ruby/backward/2/rmodule.h b/include/ruby/backward/2/rmodule.h index 53b37831c0..76c0936462 100644 --- a/include/ruby/backward/2/rmodule.h +++ b/include/ruby/backward/2/rmodule.h @@ -23,7 +23,7 @@ * who is implementing the internals) could have used those macros for a while. * Kept public as-is here to keep some theoretical backwards compatibility. */ -#define RMODULE_IV_TBL(m) RCLASS_IV_TBL(m) +#define RMODULE_IV_TBL(m) RCLASS_FIELDS(m) #define RMODULE_CONST_TBL(m) RCLASS_CONST_TBL(m) #define RMODULE_M_TBL(m) RCLASS_M_TBL(m) #define RMODULE_SUPER(m) RCLASS_SUPER(m) diff --git a/include/ruby/backward/cxxanyargs.hpp b/include/ruby/backward/cxxanyargs.hpp index 2414b7ae6d..0ca2745c20 100644 --- a/include/ruby/backward/cxxanyargs.hpp +++ b/include/ruby/backward/cxxanyargs.hpp @@ -190,33 +190,6 @@ rb_define_hooked_variable(const char *q, VALUE *w, std::nullptr_t e, void_type * /// @name Exceptions and tag jumps /// @{ -// RUBY_CXX_DEPRECATED("by rb_block_call since 1.9") -RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated") -/// @brief Old way to implement iterators. -/// @param[in] q A function that can yield. -/// @param[in] w Passed to `q`. -/// @param[in] e What is to be yielded. -/// @param[in] r Passed to `e`. -/// @return The return value of `q`. -/// @note `e` can be nullptr. -/// @deprecated This function is obsoleted since long before 2.x era. Do not -/// use it any longer. rb_block_call() is provided instead. -inline VALUE -rb_iterate(onearg_type *q, VALUE w, type *e, VALUE r) -{ - rb_block_call_func_t t = reinterpret_cast<rb_block_call_func_t>(e); - return backward::rb_iterate_deprecated(q, w, t, r); -} - -#ifdef HAVE_NULLPTR -RUBY_CXX_DEPRECATED("by rb_block_call since 1.9") -inline VALUE -rb_iterate(onearg_type *q, VALUE w, std::nullptr_t e, VALUE r) -{ - return backward::rb_iterate_deprecated(q, w, e, r); -} -#endif - RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated") /// @brief Call a method with a block. /// @param[in] q The self. @@ -537,9 +510,7 @@ struct driver { * this writing the version is 2.8. Let's warn this later, some time * during 3.x. Hopefully codes in old (ANYARGS-ed) format should be * less than now. */ -#if (RUBY_API_VERSION_MAJOR * 100 + RUBY_API_VERSION_MINOR) >= 301 RUBY_CXX_DEPRECATED("use of ANYARGS is deprecated") -#endif /// @copydoc define(VALUE klass, T mid, U func) /// @deprecated Pass correctly typed function instead. static inline void diff --git a/include/ruby/debug.h b/include/ruby/debug.h index c88da9c43d..547d5d94c4 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. @@ -207,6 +227,17 @@ typedef VALUE (*rb_debug_inspector_func_t)(const rb_debug_inspector_t *dc, void VALUE rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data); /** + * Queries the backtrace object of the context. This is as if you call + * `caller_locations` at the point of debugger. + * + * @param[in] dc A debug context. + * @return An array of `Thread::Backtrace::Location` which represents the + * current point of execution at `dc`. + + */ +VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc); + +/** * Queries the current receiver of the passed context's upper frame. * * @param[in] dc A debug context. @@ -250,15 +281,27 @@ VALUE rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long VALUE rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index); /** - * Queries the backtrace object of the context. This is as if you call - * `caller_locations` at the point of debugger. + * Queries the depth of the passed context's upper frame. * - * @param[in] dc A debug context. - * @return An array of `Thread::Backtrace::Location` which represents the - * current point of execution at `dc`. + * Note that the depth is not same as the frame index because debug_inspector + * skips some special frames but the depth counts all frames. + * + * @param[in] dc A debug context. + * @param[in] index Index of the frame from top to bottom. + * @exception rb_eArgError `index` out of range. + * @retval The depth at `index`-th frame in Integer. + */ +VALUE rb_debug_inspector_frame_depth(const rb_debug_inspector_t *dc, long index); + +// A macro to recognize `rb_debug_inspector_frame_depth()` is available or not +#define RB_DEBUG_INSPECTOR_FRAME_DEPTH(dc, index) rb_debug_inspector_frame_depth(dc, index) +/** + * Return current frame depth. + * + * @retval The depth of the current frame in Integer. */ -VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc); +VALUE rb_debug_inspector_current_depth(void); /** @} */ @@ -436,7 +479,7 @@ RBIMPL_ATTR_RETURNS_NONNULL() */ rb_trace_arg_t *rb_tracearg_from_tracepoint(VALUE tpval); -RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NONNULL((1)) /** * Queries the event of the passed trace. * @@ -445,7 +488,7 @@ RBIMPL_ATTR_NONNULL(()) */ rb_event_flag_t rb_tracearg_event_flag(rb_trace_arg_t *trace_arg); -RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NONNULL((1)) /** * Identical to rb_tracearg_event_flag(), except it returns the name of the * event in Ruby's symbol. @@ -455,7 +498,7 @@ RBIMPL_ATTR_NONNULL(()) */ VALUE rb_tracearg_event(rb_trace_arg_t *trace_arg); -RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NONNULL((1)) /** * Queries the line of the point where the trace is at. * @@ -465,7 +508,7 @@ RBIMPL_ATTR_NONNULL(()) */ VALUE rb_tracearg_lineno(rb_trace_arg_t *trace_arg); -RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NONNULL((1)) /** * Queries the file name of the point where the trace is at. * @@ -475,7 +518,19 @@ RBIMPL_ATTR_NONNULL(()) */ VALUE rb_tracearg_path(rb_trace_arg_t *trace_arg); -RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NONNULL((1)) +/** + * + * Queries the parameters passed on a call or return event. + * + * @param[in] trace_arg A trace instance + * @exception rb_eRuntimeError The tracing event does not support querying parameters. + * @return Array of parameters in the format of `Method#parameters`. + * + */ +VALUE rb_tracearg_parameters(rb_trace_arg_t *trace_arg); + +RBIMPL_ATTR_NONNULL((1)) /** * Queries the method name of the point where the trace is at. * @@ -485,7 +540,7 @@ RBIMPL_ATTR_NONNULL(()) */ VALUE rb_tracearg_method_id(rb_trace_arg_t *trace_arg); -RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NONNULL((1)) /** * Identical to rb_tracearg_method_id(), except it returns callee id like * rb_frame_callee(). @@ -496,7 +551,7 @@ RBIMPL_ATTR_NONNULL(()) */ VALUE rb_tracearg_callee_id(rb_trace_arg_t *trace_arg); -RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NONNULL((1)) /** * Queries the class that defines the method that the passed trace is at. This * can be different from the class of rb_tracearg_self()'s return value because @@ -508,7 +563,7 @@ RBIMPL_ATTR_NONNULL(()) */ VALUE rb_tracearg_defined_class(rb_trace_arg_t *trace_arg); -RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NONNULL((1)) /** * Creates a binding object of the point where the trace is at. * @@ -523,7 +578,7 @@ RBIMPL_ATTR_NONNULL(()) */ VALUE rb_tracearg_binding(rb_trace_arg_t *trace_arg); -RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NONNULL((1)) /** * Queries the receiver of the point trace is at. * @@ -532,7 +587,7 @@ RBIMPL_ATTR_NONNULL(()) */ VALUE rb_tracearg_self(rb_trace_arg_t *trace_arg); -RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NONNULL((1)) /** * Queries the return value that the trace represents. * @@ -542,7 +597,7 @@ RBIMPL_ATTR_NONNULL(()) */ VALUE rb_tracearg_return_value(rb_trace_arg_t *trace_arg); -RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NONNULL((1)) /** * Queries the raised exception that the trace represents. * @@ -552,7 +607,33 @@ RBIMPL_ATTR_NONNULL(()) */ VALUE rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg); -RBIMPL_ATTR_NONNULL(()) +RBIMPL_ATTR_NONNULL((1)) +/** + * Queries the compiled source code of the 'script_compiled' event. + * If loaded from a file, it will return nil. + * + * @param[in] trace_arg A trace instance + * @exception rb_eRuntimeError The tracing event is not 'script_compiled'. + * @retval RUBY_Qnil The script was loaded from a file. + * @retval otherwise The compiled source code. + * + */ +VALUE rb_tracearg_eval_script(rb_trace_arg_t *trace_arg); + +RBIMPL_ATTR_NONNULL((1)) +/** + * + * Queries the compiled instruction sequence on a 'script_compiled' event. + * Note that this method is MRI specific. + * + * @param[in] trace_arg A trace instance + * @exception rb_eRuntimeError The tracing event is not 'script_compiled'. + * @return The `RubyVM::InstructionSequence` object representing the instruction sequence. + * + */ +VALUE rb_tracearg_instruction_sequence(rb_trace_arg_t *trace_arg); + +RBIMPL_ATTR_NONNULL((1)) /** * Queries the allocated/deallocated object that the trace represents. * @@ -573,48 +654,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. + * + * 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. * - * 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. + * 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. * - * @param[in] flags (Unused) reserved for future extensions. + * 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 ced092adbc..4d764f68ae 100644 --- a/include/ruby/fiber/scheduler.h +++ b/include/ruby/fiber/scheduler.h @@ -23,7 +23,11 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() +// Version 3: Adds support for `fiber_interrupt`. +#define RUBY_FIBER_SCHEDULER_VERSION 3 + struct timeval; +struct rb_thread_struct; /** * Wrap a `ssize_t` and `int errno` into a single `VALUE`. This interface should @@ -43,10 +47,12 @@ struct timeval; * @return A `VALUE` which contains the result and/or errno. */ static inline VALUE -rb_fiber_scheduler_io_result(ssize_t result, int error) { +rb_fiber_scheduler_io_result(ssize_t result, int error) +{ if (result == -1) { return RB_INT2NUM(-error); - } else { + } + else { return RB_SIZE2NUM(result); } } @@ -63,11 +69,13 @@ rb_fiber_scheduler_io_result(ssize_t result, int error) { * @return The original result of the system call. */ static inline ssize_t -rb_fiber_scheduler_io_result_apply(VALUE result) { +rb_fiber_scheduler_io_result_apply(VALUE result) +{ if (RB_FIXNUM_P(result) && RB_NUM2INT(result) < 0) { errno = -RB_NUM2INT(result); return -1; - } else { + } + else { return RB_NUM2SIZE(result); } } @@ -91,7 +99,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. @@ -111,7 +119,7 @@ VALUE rb_fiber_scheduler_current(void); /** * Identical to rb_fiber_scheduler_current(), except it queries for that of the - * passed thread instead of the implicit current one. + * passed thread value instead of the implicit current one. * * @param[in] thread Target thread. * @exception rb_eTypeError `thread` is not a thread. @@ -121,6 +129,17 @@ VALUE rb_fiber_scheduler_current(void); VALUE rb_fiber_scheduler_current_for_thread(VALUE thread); /** + * Identical to rb_fiber_scheduler_current_for_thread(), except it expects + * a threadptr instead of a thread value. + * + * @param[in] thread Target thread. + * @exception rb_eTypeError `thread` is not a thread. + * @retval RUBY_Qnil No scheduler is in effect in `thread`. + * @retval otherwise The scheduler that is in effect in `thread`. + */ +VALUE rb_fiber_scheduler_current_for_threadptr(struct rb_thread_struct *thread); + +/** * Converts the passed timeout to an expression that rb_fiber_scheduler_block() * etc. expects. * @@ -140,7 +159,7 @@ VALUE rb_fiber_scheduler_make_timeout(struct timeval *timeout); VALUE rb_fiber_scheduler_close(VALUE scheduler); /** - * Nonblocking `sleep`. Depending on scheduler implementation, this for + * Non-blocking `sleep`. Depending on scheduler implementation, this for * instance switches to another fiber etc. * * @param[in] scheduler Target scheduler. @@ -160,6 +179,14 @@ VALUE rb_fiber_scheduler_kernel_sleep(VALUE scheduler, VALUE duration); */ VALUE rb_fiber_scheduler_kernel_sleepv(VALUE scheduler, int argc, VALUE * argv); +/** + * Yield to the scheduler, to be resumed on the next scheduling cycle. + * + * @param[in] scheduler Target scheduler. + * @return What `scheduler.yield` returns. + */ +VALUE rb_fiber_scheduler_yield(VALUE scheduler); + /* Description TBW */ #if 0 VALUE rb_fiber_scheduler_timeout_after(VALUE scheduler, VALUE timeout, VALUE exception, VALUE message); @@ -168,7 +195,7 @@ int rb_fiber_scheduler_supports_process_wait(VALUE scheduler); #endif /** - * Nonblocking `waitpid`. Depending on scheduler implementation, this for + * Non-blocking `waitpid`. Depending on scheduler implementation, this for * instance switches to another fiber etc. * * @param[in] scheduler Target scheduler. @@ -179,7 +206,7 @@ int rb_fiber_scheduler_supports_process_wait(VALUE scheduler); VALUE rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags); /** - * Nonblocking wait for the passed "blocker", which is for instance + * Non-blocking wait for the passed "blocker", which is for instance * `Thread.join` or `Mutex.lock`. Depending on scheduler implementation, this * for instance switches to another fiber etc. * @@ -193,6 +220,8 @@ VALUE rb_fiber_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout); /** * Wakes up a fiber previously blocked using rb_fiber_scheduler_block(). * + * This function may be called from a different thread. + * * @param[in] scheduler Target scheduler. * @param[in] blocker What was awaited for. * @param[in] fiber What to unblock. @@ -201,8 +230,8 @@ VALUE rb_fiber_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout); VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber); /** - * Nonblocking version of rb_io_wait(). Depending on scheduler implementation, - * this for instance switches to another fiber etc. + * Non-blocking version of rb_io_wait(). Depending on scheduler + * implementation, this for instance switches to another fiber etc. * * The "events" here is a Ruby level integer, which is an OR-ed value of * `IO::READABLE`, `IO::WRITABLE`, and `IO::PRIORITY`. @@ -216,7 +245,7 @@ VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber); VALUE rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeout); /** - * Nonblocking wait until the passed IO is ready for reading. This is a + * Non-blocking wait until the passed IO is ready for reading. This is a * special case of rb_fiber_scheduler_io_wait(), where the interest is * `IO::READABLE` and timeout is never. * @@ -227,7 +256,7 @@ VALUE rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE VALUE rb_fiber_scheduler_io_wait_readable(VALUE scheduler, VALUE io); /** - * Nonblocking wait until the passed IO is ready for writing. This is a + * Non-blocking wait until the passed IO is ready for writing. This is a * special case of rb_fiber_scheduler_io_wait(), where the interest is * `IO::WRITABLE` and timeout is never. * @@ -238,83 +267,135 @@ VALUE rb_fiber_scheduler_io_wait_readable(VALUE scheduler, VALUE io); VALUE rb_fiber_scheduler_io_wait_writable(VALUE scheduler, VALUE io); /** - * Nonblocking read from the passed IO. + * Non-blocking version of `IO.select`. + * + * It's possible that this will be emulated using a thread, so you should not + * rely on it for high performance. + * + * @param[in] scheduler Target scheduler. + * @param[in] readables An array of readable objects. + * @param[in] writables An array of writable objects. + * @param[in] exceptables An array of objects that might encounter exceptional conditions. + * @param[in] timeout Numeric timeout or nil. + * @return What `scheduler.io_select` returns, normally a 3-tuple of arrays of ready objects. + */ +VALUE rb_fiber_scheduler_io_select(VALUE scheduler, VALUE readables, VALUE writables, VALUE exceptables, VALUE timeout); + +/** + * Non-blocking version of `IO.select`, `argv` variant. + */ +VALUE rb_fiber_scheduler_io_selectv(VALUE scheduler, int argc, VALUE *argv); + +/** + * Non-blocking read from the passed IO. * * @param[in] scheduler Target scheduler. - * @param[out] io An io object to read from. - * @param[out] buffer Return buffer. - * @param[in] length Requested number of bytes to read. + * @param[in] io An io object to read from. + * @param[in] buffer The buffer to read to. + * @param[in] length The minimum number of bytes to read. + * @param[in] offset The offset in the buffer to read from. * @retval RUBY_Qundef `scheduler` doesn't have `#io_read`. * @return otherwise What `scheduler.io_read` returns `[-errno, size]`. */ -VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length); +VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset); /** - * Nonblocking write to the passed IO. + * Non-blocking write to the passed IO. * * @param[in] scheduler Target scheduler. - * @param[out] io An io object to write to. - * @param[in] buffer What to write. - * @param[in] length Number of bytes to write. + * @param[in] io An io object to write to. + * @param[in] buffer The buffer to write from. + * @param[in] length The minimum number of bytes to write. + * @param[in] offset The offset in the buffer to write from. * @retval RUBY_Qundef `scheduler` doesn't have `#io_write`. * @return otherwise What `scheduler.io_write` returns `[-errno, size]`. */ -VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length); +VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset); + +/** + * Non-blocking read from the passed IO at the specified offset. + * + * @param[in] scheduler Target scheduler. + * @param[in] io An io object to read from. + * @param[in] from The offset to read from. + * @param[in] buffer The buffer to read to. + * @param[in] length The minimum number of bytes to read. + * @param[in] offset The offset in the buffer to read to. + * @retval RUBY_Qundef `scheduler` doesn't have `#io_read`. + * @return otherwise What `scheduler.io_read` returns. + */ +VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset); + +/** + * Non-blocking write to the passed IO at the specified offset. + * + * @param[in] scheduler Target scheduler. + * @param[in] io An io object to write to. + * @param[in] from The offset to write to. + * @param[in] buffer The buffer to write from. + * @param[in] length The minimum number of bytes to write. + * @param[in] offset The offset in the buffer to write from. + * @retval RUBY_Qundef `scheduler` doesn't have `#io_write`. + * @return otherwise What `scheduler.io_write` returns. + */ +VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset); /** - * Nonblocking read from the passed IO at the specified offset. + * Non-blocking read from the passed IO using a native buffer. * * @param[in] scheduler Target scheduler. - * @param[out] io An io object to read from. - * @param[out] buffer Return buffer. - * @param[in] length Requested number of bytes to read. - * @param[in] offset The offset in the given IO to read the data from. + * @param[in] io An io object to read from. + * @param[in] base The memory to read to. + * @param[in] size Size of the memory. + * @param[in] length The minimum number of bytes to read. * @retval RUBY_Qundef `scheduler` doesn't have `#io_read`. * @return otherwise What `scheduler.io_read` returns. */ -VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset); +VALUE rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *base, size_t size, size_t length); /** - * Nonblocking write to the passed IO at the specified offset. + * Non-blocking write to the passed IO using a native buffer. * * @param[in] scheduler Target scheduler. - * @param[out] io An io object to write to. - * @param[in] buffer What to write. - * @param[in] length Number of bytes to write. - * @param[in] offset The offset in the given IO to write the data to. + * @param[in] io An io object to write to. + * @param[in] base The memory to write from. + * @param[in] size Size of the memory. + * @param[in] length The minimum number of bytes to write. * @retval RUBY_Qundef `scheduler` doesn't have `#io_write`. * @return otherwise What `scheduler.io_write` returns. */ -VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset); +VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *base, size_t size, size_t length); /** - * Nonblocking read from the passed IO using a native buffer. + * Non-blocking pread from the passed IO using a native buffer. * * @param[in] scheduler Target scheduler. - * @param[out] io An io object to read from. - * @param[out] buffer Return buffer. - * @param[in] size Size of the return buffer. - * @param[in] length Requested number of bytes to read. + * @param[in] io An io object to read from. + * @param[in] from The offset to read from. + * @param[in] base The memory to read to. + * @param[in] size Size of the memory. + * @param[in] length The minimum number of bytes to read. * @retval RUBY_Qundef `scheduler` doesn't have `#io_read`. * @return otherwise What `scheduler.io_read` returns. */ -VALUE rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *buffer, size_t size, size_t length); +VALUE rb_fiber_scheduler_io_pread_memory(VALUE scheduler, VALUE io, rb_off_t from, void *base, size_t size, size_t length); /** - * Nonblocking write to the passed IO using a native buffer. + * Non-blocking pwrite to the passed IO using a native buffer. * * @param[in] scheduler Target scheduler. - * @param[out] io An io object to write to. - * @param[in] buffer What to write. - * @param[in] size Size of the buffer. - * @param[in] length Number of bytes to write. + * @param[in] io An io object to write to. + * @param[in] from The offset to write from. + * @param[in] base The memory to write from. + * @param[in] size Size of the memory. + * @param[in] length The minimum number of bytes to write. * @retval RUBY_Qundef `scheduler` doesn't have `#io_write`. * @return otherwise What `scheduler.io_write` returns. */ -VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *buffer, size_t size, size_t length); +VALUE rb_fiber_scheduler_io_pwrite_memory(VALUE scheduler, VALUE io, rb_off_t from, const void *base, size_t size, size_t length); /** - * Nonblocking close the given IO. + * Non-blocking close the given IO. * * @param[in] scheduler Target scheduler. * @param[in] io An io object to close. @@ -324,7 +405,7 @@ VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void * VALUE rb_fiber_scheduler_io_close(VALUE scheduler, VALUE io); /** - * Nonblocking DNS lookup. + * Non-blocking DNS lookup. * * @param[in] scheduler Target scheduler. * @param[in] hostname A host name to query. @@ -333,6 +414,92 @@ VALUE rb_fiber_scheduler_io_close(VALUE scheduler, VALUE io); */ VALUE rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname); +// The state of the blocking operation execution. +struct rb_fiber_scheduler_blocking_operation_state { + void *result; + int saved_errno; +}; + +// The opaque handle for the blocking operation. +typedef struct rb_fiber_scheduler_blocking_operation rb_fiber_scheduler_blocking_operation_t; + +/** + * Extract the blocking operation handle from a BlockingOperationRuby object. + * + * This function safely extracts the opaque handle from a BlockingOperation VALUE + * while holding the GVL. The returned pointer can be passed to worker threads + * and used with rb_fiber_scheduler_blocking_operation_execute. + * + * @param[in] self The BlockingOperation VALUE to extract from + * @return The opaque struct pointer on success, NULL on error + * @note Experimental. + */ +rb_fiber_scheduler_blocking_operation_t *rb_fiber_scheduler_blocking_operation_extract(VALUE self); + +/** + * Execute blocking operation from handle (GVL not required). + * + * This function executes a blocking operation using the opaque handle + * obtained from rb_fiber_scheduler_blocking_operation_extract. + * It can be called from native threads without holding the GVL. + * + * @param[in] blocking_operation The opaque handle. + * @return 0 on success, -1 on error. + * @note Experimental. Can be called from any thread without holding the GVL + */ +int rb_fiber_scheduler_blocking_operation_execute(rb_fiber_scheduler_blocking_operation_t *blocking_operation); + +/** + * Cancel a blocking operation. + * + * This function cancels a blocking operation. If the operation is queued, + * it just marks it as cancelled. If it's executing, it marks it as cancelled + * and calls the unblock function to interrupt the operation. + * + * @param blocking_operation The opaque struct pointer + * @return 1 if unblock function was called, 0 if just marked cancelled, -1 on error + * @note Experimental. + */ +int rb_fiber_scheduler_blocking_operation_cancel(rb_fiber_scheduler_blocking_operation_t *blocking_operation); + +/** + * Defer the execution of the passed function to the scheduler. + * + * @param[in] scheduler Target scheduler. + * @param[in] function The function to run. + * @param[in] data The data to pass to the function. + * @param[in] unblock_function The unblock function to use to interrupt the operation. + * @param[in] data2 The data to pass to the unblock function. + * @param[in] flags Flags passed to `rb_nogvl`. + * @param[out] state The result and errno of the operation. + * @retval RUBY_Qundef `scheduler` doesn't have `#blocking_operation_wait`. + * @return otherwise What `scheduler.blocking_operation_wait` returns. + */ +VALUE rb_fiber_scheduler_blocking_operation_wait(VALUE scheduler, void* (*function)(void *), void *data, rb_unblock_function_t *unblock_function, void *data2, int flags, struct rb_fiber_scheduler_blocking_operation_state *state); + +/** + * Interrupt a fiber by raising an exception. You can construct an exception using `rb_make_exception`. + * + * This hook may be invoked by a different thread. + * + * @param[in] scheduler Target scheduler. + * @param[in] fiber The fiber to interrupt. + * @param[in] exception The exception to raise in the fiber. + * @return What `scheduler.fiber_interrupt` returns. + */ +VALUE rb_fiber_scheduler_fiber_interrupt(VALUE scheduler, VALUE fiber, VALUE exception); + +/** + * Create and schedule a non-blocking fiber. + * + * @param[in] scheduler Target scheduler. + * @param[in] argc Number of arguments in argv. + * @param[in] argv Array of arguments to pass to the fiber. + * @param[in] kw_splat Whether to expand last argument as keywords. + * @return The created and scheduled fiber. + */ +VALUE rb_fiber_scheduler_fiber(VALUE scheduler, int argc, VALUE *argv, int kw_splat); + RBIMPL_SYMBOL_EXPORT_END() #endif /* RUBY_FIBER_SCHEDULER_H */ diff --git a/include/ruby/intern.h b/include/ruby/intern.h index 2480e2e703..8718169ce2 100644 --- a/include/ruby/intern.h +++ b/include/ruby/intern.h @@ -36,7 +36,6 @@ #include "ruby/internal/intern/error.h" #include "ruby/internal/intern/eval.h" #include "ruby/internal/intern/file.h" -#include "ruby/internal/intern/gc.h" #include "ruby/internal/intern/hash.h" #include "ruby/internal/intern/io.h" #include "ruby/internal/intern/load.h" @@ -52,6 +51,7 @@ #include "ruby/internal/intern/re.h" #include "ruby/internal/intern/ruby.h" #include "ruby/internal/intern/select.h" +#include "ruby/internal/intern/set.h" #include "ruby/internal/intern/signal.h" #include "ruby/internal/intern/sprintf.h" #include "ruby/internal/intern/string.h" diff --git a/include/ruby/internal/abi.h b/include/ruby/internal/abi.h index ed779f3558..e6d1fa7e8f 100644 --- a/include/ruby/internal/abi.h +++ b/include/ruby/internal/abi.h @@ -1,6 +1,8 @@ #ifndef RUBY_ABI_H #define RUBY_ABI_H +#ifdef RUBY_ABI_VERSION /* should match the definition in config.h */ + /* This number represents Ruby's ABI version. * * In development Ruby, it should be bumped every time an ABI incompatible @@ -19,7 +21,7 @@ * - Backwards compatible refactors. * - Editing comments. * - * In released versions of Ruby, this number should not be changed since teeny + * In released versions of Ruby, this number is not defined since teeny * versions of Ruby should guarantee ABI compatibility. */ #define RUBY_ABI_VERSION 1 @@ -29,8 +31,9 @@ #if defined(HAVE_FUNC_WEAK) && !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) # define RUBY_DLN_CHECK_ABI #endif +#endif /* RUBY_ABI_VERSION */ -#ifdef RUBY_DLN_CHECK_ABI +#if defined(RUBY_DLN_CHECK_ABI) && !defined(RUBY_EXPORT) # ifdef __cplusplus extern "C" { @@ -39,7 +42,11 @@ extern "C" { RUBY_FUNC_EXPORTED unsigned long long __attribute__((weak)) ruby_abi_version(void) { +# ifdef RUBY_ABI_VERSION return RUBY_ABI_VERSION; +# else + return 0; +# endif } # ifdef __cplusplus diff --git a/include/ruby/internal/anyargs.h b/include/ruby/internal/anyargs.h index 9d8d16fdab..e4c6d155cc 100644 --- a/include/ruby/internal/anyargs.h +++ b/include/ruby/internal/anyargs.h @@ -84,12 +84,15 @@ #elif defined(_WIN32) || defined(__CYGWIN__) # /* Skip due to [Bug #16134] */ +# define RBIMPL_CAST_FN_PTR 1 #elif ! RBIMPL_HAS_ATTRIBUTE(transparent_union) # /* :TODO: improve here, please find a way to support. */ +# define RBIMPL_CAST_FN_PTR 1 #elif ! defined(HAVE_VA_ARGS_MACRO) # /* :TODO: improve here, please find a way to support. */ +# define RBIMPL_CAST_FN_PTR 1 #else # /** @cond INTERNAL_MACRO */ @@ -239,15 +242,16 @@ # define RBIMPL_ANYARGS_DISPATCH_rb_define_method_13(n) RBIMPL_ANYARGS_DISPATCH((n) == 13, rb_define_method_13, RBIMPL_ANYARGS_DISPATCH_rb_define_method_12(n)) # define RBIMPL_ANYARGS_DISPATCH_rb_define_method_14(n) RBIMPL_ANYARGS_DISPATCH((n) == 14, rb_define_method_14, RBIMPL_ANYARGS_DISPATCH_rb_define_method_13(n)) # define RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n) RBIMPL_ANYARGS_DISPATCH((n) == 15, rb_define_method_15, RBIMPL_ANYARGS_DISPATCH_rb_define_method_14(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_singleton_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_protected_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_private_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_module_function_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_global_function_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_id_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_15(n)) -# define RBIMPL_ANYARGS_DISPATCH_rb_define_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_m3, RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_singleton_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_protected_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_private_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_module_function_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_global_function_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_id_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_15(n)) +# define RBIMPL_ANYARGS_DISPATCH_rb_define_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n)) # define RBIMPL_ANYARGS_ATTRSET(sym) RBIMPL_ATTR_MAYBE_UNUSED() RBIMPL_ATTR_NONNULL(()) RBIMPL_ATTR_WEAKREF(sym) # define RBIMPL_ANYARGS_DECL(sym, ...) \ +RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _notimpl(__VA_ARGS__, VALUE(*)(int, const VALUE *, VALUE, VALUE), int); \ RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m3(__VA_ARGS__, VALUE(*)(ANYARGS), int); \ RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m2(__VA_ARGS__, VALUE(*)(VALUE, VALUE), int); \ RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m1(__VA_ARGS__, VALUE(*)(int, union { VALUE *x; const VALUE *y; } __attribute__((__transparent_union__)), VALUE), int); \ @@ -347,6 +351,25 @@ RBIMPL_ANYARGS_DECL(rb_define_method, VALUE, const char *) #endif /* __cplusplus */ +#if defined(RBIMPL_CAST_FN_PTR) && !defined(__cplusplus) +/* In C23, K&R style prototypes are gone and so `void foo(ANYARGS)` became + * equivalent to `void foo(void)` unlike in earlier versions. This is a problem + * for rb_define_* functions since that makes all valid functions one can pass + * trip -Wincompatible-pointer-types, which we treat as errors. This is mostly + * not a problem for the __builtin_choose_expr path, but outside of that we + * need to add a cast for compatibility. + */ +#define rb_define_method(klass, mid, func, arity) rb_define_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity)) +#define rb_define_method_id(klass, mid, func, arity) rb_define_method_id((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity)) +#define rb_define_singleton_method(obj, mid, func, arity) rb_define_singleton_method((obj), (mid), (VALUE (*)(ANYARGS))(func), (arity)) +#define rb_define_protected_method(klass, mid, func, arity) rb_define_protected_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity)) +#define rb_define_private_method(klass, mid, func, arity) rb_define_private_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity)) +#define rb_define_module_function(mod, mid, func, arity) rb_define_module_function((mod), (mid), (VALUE (*)(ANYARGS))(func), (arity)) +#define rb_define_global_function(mid, func, arity) rb_define_global_function((mid), (VALUE (*)(ANYARGS))(func), (arity)) + +#undef RBIMPL_CAST_FN_PTR +#endif /* defined(RBIMPL_CAST_FN_PTR) && !defined(__cplusplus) */ + /** * This macro is to properly cast a function parameter of *_define_method * family. It has been around since 1.x era so you can maximise backwards diff --git a/include/ruby/internal/arithmetic.h b/include/ruby/internal/arithmetic.h index 3f7840c384..7ebb4a86f1 100644 --- a/include/ruby/internal/arithmetic.h +++ b/include/ruby/internal/arithmetic.h @@ -18,7 +18,8 @@ * 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 Conversion between C's arithmtic types and Ruby's numeric types. + * @brief Conversion between C's arithmetic types and Ruby's numeric + * types. */ #include "ruby/internal/arithmetic/char.h" #include "ruby/internal/arithmetic/double.h" diff --git a/include/ruby/internal/arithmetic/int.h b/include/ruby/internal/arithmetic/int.h index 6bd8ec2184..7b24d16887 100644 --- a/include/ruby/internal/arithmetic/int.h +++ b/include/ruby/internal/arithmetic/int.h @@ -254,7 +254,7 @@ static inline VALUE rb_uint2num_inline(unsigned int 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/intptr_t.h b/include/ruby/internal/arithmetic/intptr_t.h index a354f4469c..70090f88e6 100644 --- a/include/ruby/internal/arithmetic/intptr_t.h +++ b/include/ruby/internal/arithmetic/intptr_t.h @@ -32,6 +32,18 @@ #define rb_int_new rb_int2inum /**< @alias{rb_int2inum} */ #define rb_uint_new rb_uint2inum /**< @alias{rb_uint2inum} */ +// These definitions are same as fiddle/conversions.h +#if SIZEOF_VOIDP <= SIZEOF_LONG +# define PTR2NUM(x) (LONG2NUM((long)(x))) +# define NUM2PTR(x) ((void*)(NUM2ULONG(x))) +#elif SIZEOF_VOIDP <= SIZEOF_LONG_LONG +# define PTR2NUM(x) (LL2NUM((LONG_LONG)(x))) +# define NUM2PTR(x) ((void*)(NUM2ULL(x))) +#else +// should have been an error in ruby/internal/value.h +# error Need integer for VALUE +#endif + RBIMPL_SYMBOL_EXPORT_BEGIN() /** 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/deprecated.h b/include/ruby/internal/attr/deprecated.h index e1bbdbd15a..a374ace868 100644 --- a/include/ruby/internal/attr/deprecated.h +++ b/include/ruby/internal/attr/deprecated.h @@ -48,7 +48,7 @@ #elif RBIMPL_HAS_ATTRIBUTE(deprecated) /* but not with message. */ # define RBIMPL_ATTR_DEPRECATED(msg) __attribute__((__deprecated__)) -#elif RBIMPL_COMPILER_SINCE(MSVC, 14, 0, 0) +#elif RBIMPL_COMPILER_IS(MSVC) # define RBIMPL_ATTR_DEPRECATED(msg) __declspec(deprecated msg) #elif RBIMPL_HAS_DECLSPEC_ATTRIBUTE(deprecated) @@ -72,4 +72,11 @@ # define RBIMPL_ATTR_DEPRECATED_EXT(msg) RBIMPL_ATTR_DEPRECATED(msg) #endif +#define RBIMPL_ATTR_DEPRECATED_SINCE(ver) \ + RBIMPL_ATTR_DEPRECATED(("since " #ver)) +#define RBIMPL_ATTR_DEPRECATED_INTERNAL(ver) \ + RBIMPL_ATTR_DEPRECATED(("since "#ver", also internal")) +#define RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY() \ + RBIMPL_ATTR_DEPRECATED(("only for internal use")) + #endif /* RBIMPL_ATTR_DEPRECATED_H */ diff --git a/include/ruby/internal/attr/forceinline.h b/include/ruby/internal/attr/forceinline.h index b7daafede7..5b9ae794af 100644 --- a/include/ruby/internal/attr/forceinline.h +++ b/include/ruby/internal/attr/forceinline.h @@ -29,7 +29,7 @@ * `__forceinline` are mutually exclusive. We have to mimic that behaviour for * non-MSVC compilers. */ -#if RBIMPL_COMPILER_SINCE(MSVC, 12, 0, 0) +#if RBIMPL_COMPILER_IS(MSVC) # define RBIMPL_ATTR_FORCEINLINE() __forceinline #elif RBIMPL_HAS_ATTRIBUTE(always_inline) # define RBIMPL_ATTR_FORCEINLINE() __attribute__((__always_inline__)) inline diff --git a/include/ruby/internal/attr/nodiscard.h b/include/ruby/internal/attr/nodiscard.h index 087192a7a8..c3ae118942 100644 --- a/include/ruby/internal/attr/nodiscard.h +++ b/include/ruby/internal/attr/nodiscard.h @@ -26,7 +26,7 @@ /** * Wraps (or simulates) `[[nodiscard]]`. In C++ (at least since C++20) a - * nodiscard attribute can have a message why the result shall not be ignoed. + * nodiscard attribute can have a message why the result shall not be ignored. * However GCC attribute and SAL annotation cannot take them. */ #if RBIMPL_HAS_CPP_ATTRIBUTE(nodiscard) diff --git a/include/ruby/internal/attr/noexcept.h b/include/ruby/internal/attr/noexcept.h index ea3001df2a..dd4c667407 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 @@ -78,7 +78,7 @@ #elif defined(__INTEL_CXX11_MODE__) # define RBIMPL_ATTR_NOEXCEPT(_) noexcept(noexcept(_)) -#elif RBIMPL_COMPILER_SINCE(MSVC, 19, 0, 0) +#elif RBIMPL_COMPILER_IS(MSVC) # define RBIMPL_ATTR_NOEXCEPT(_) noexcept(noexcept(_)) #elif __cplusplus >= 201103L diff --git a/include/ruby/internal/attr/nonstring.h b/include/ruby/internal/attr/nonstring.h new file mode 100644 index 0000000000..5ad6ef2a86 --- /dev/null +++ b/include/ruby/internal/attr/nonstring.h @@ -0,0 +1,40 @@ +#ifndef RBIMPL_ATTR_NONSTRING_H /*-*-C++-*-vi:se ft=cpp:*/ +#define RBIMPL_ATTR_NONSTRING_H +/** + * @file + * @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 Defines #RBIMPL_ATTR_NONSTRING. + */ +#include "ruby/internal/has/attribute.h" + +/** Wraps (or simulates) `__attribute__((nonstring))` */ +#if RBIMPL_HAS_ATTRIBUTE(nonstring) +# define RBIMPL_ATTR_NONSTRING() __attribute__((nonstring)) +# if RBIMPL_COMPILER_SINCE(GCC, 15, 0, 0) +# define RBIMPL_ATTR_NONSTRING_ARRAY() RBIMPL_ATTR_NONSTRING() +# elif RBIMPL_COMPILER_SINCE(Clang, 21, 0, 0) +# define RBIMPL_ATTR_NONSTRING_ARRAY() RBIMPL_ATTR_NONSTRING() +# else +# define RBIMPL_ATTR_NONSTRING_ARRAY() /* void */ +# endif +#else +# define RBIMPL_ATTR_NONSTRING() /* void */ +# define RBIMPL_ATTR_NONSTRING_ARRAY() /* void */ +#endif + +#endif /* RBIMPL_ATTR_NONSTRING_H */ diff --git a/include/ruby/internal/attr/packed_struct.h b/include/ruby/internal/attr/packed_struct.h new file mode 100644 index 0000000000..0678b9acc8 --- /dev/null +++ b/include/ruby/internal/attr/packed_struct.h @@ -0,0 +1,43 @@ +#ifndef RBIMPL_ATTR_PACKED_STRUCT_H /*-*-C++-*-vi:se ft=cpp:*/ +#define RBIMPL_ATTR_PACKED_STRUCT_H +/** + * @file + * @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 Defines #RBIMPL_ATTR_PACKED_STRUCT_BEGIN, + * #RBIMPL_ATTR_PACKED_STRUCT_END, + * #RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN, and + * #RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END. + */ +#include "ruby/internal/config.h" + +#ifndef RBIMPL_ATTR_PACKED_STRUCT_BEGIN +# define RBIMPL_ATTR_PACKED_STRUCT_BEGIN() /* void */ +#endif +#ifndef RBIMPL_ATTR_PACKED_STRUCT_END +# define RBIMPL_ATTR_PACKED_STRUCT_END() /* void */ +#endif + +#if UNALIGNED_WORD_ACCESS +# define RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN() RBIMPL_ATTR_PACKED_STRUCT_BEGIN() +# define RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END() RBIMPL_ATTR_PACKED_STRUCT_END() +#else +# define RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN() /* void */ +# define RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END() /* void */ +#endif + +#endif diff --git a/include/ruby/internal/attr/restrict.h b/include/ruby/internal/attr/restrict.h index e39104138c..b12fdc9dbc 100644 --- a/include/ruby/internal/attr/restrict.h +++ b/include/ruby/internal/attr/restrict.h @@ -28,7 +28,7 @@ * `__has_declspec_attribute()` which involves macro substitution. */ /** Wraps (or simulates) `__declspec(restrict)` */ -#if RBIMPL_COMPILER_SINCE(MSVC, 14, 0, 0) +#if RBIMPL_COMPILER_IS(MSVC) # define RBIMPL_ATTR_RESTRICT() __declspec(re ## strict) #elif RBIMPL_HAS_ATTRIBUTE(malloc) diff --git a/include/ruby/internal/compiler_is/msvc.h b/include/ruby/internal/compiler_is/msvc.h index 8a864ea558..824f0ecc21 100644 --- a/include/ruby/internal/compiler_is/msvc.h +++ b/include/ruby/internal/compiler_is/msvc.h @@ -38,19 +38,8 @@ # define RBIMPL_COMPILER_VERSION_MINOR (_MSC_FULL_VER % 10000000 / 100000) # define RBIMPL_COMPILER_VERSION_PATCH (_MSC_FULL_VER % 100000) -#elif defined(_MSC_FULL_VER) -# define RBIMPL_COMPILER_IS_MSVC 1 -# /* _MSC_FULL_VER = XXYYZZZZ */ -# define RBIMPL_COMPILER_VERSION_MAJOR (_MSC_FULL_VER / 1000000) -# define RBIMPL_COMPILER_VERSION_MINOR (_MSC_FULL_VER % 1000000 / 10000) -# define RBIMPL_COMPILER_VERSION_PATCH (_MSC_FULL_VER % 10000) - #else -# define RBIMPL_COMPILER_IS_MSVC 1 -# /* _MSC_VER = XXYY */ -# define RBIMPL_COMPILER_VERSION_MAJOR (_MSC_VER / 100) -# define RBIMPL_COMPILER_VERSION_MINOR (_MSC_VER % 100) -# define RBIMPL_COMPILER_VERSION_PATCH 0 +# error Unsupported MSVC version #endif #endif /* RBIMPL_COMPILER_IS_MSVC_H */ diff --git a/include/ruby/internal/config.h b/include/ruby/internal/config.h index 0c434e5b05..34862ded6e 100644 --- a/include/ruby/internal/config.h +++ b/include/ruby/internal/config.h @@ -50,7 +50,7 @@ # define HAVE_VA_ARGS_MACRO # elif defined(__INTEL_CXX11_MODE__) # define HAVE_VA_ARGS_MACRO -# elif RBIMPL_COMPILER_SINCE(MSVC, 16, 0, 0) +# elif RBIMPL_COMPILER_IS(MSVC) # define HAVE_VA_ARGS_MACRO # else # /* NG, not known. */ @@ -113,6 +113,8 @@ # define UNALIGNED_WORD_ACCESS 1 #elif defined(__powerpc64__) # define UNALIGNED_WORD_ACCESS 1 +#elif defined(__POWERPC__) // __POWERPC__ is defined for ppc and ppc64 on Darwin +# define UNALIGNED_WORD_ACCESS 1 #elif defined(__aarch64__) # define UNALIGNED_WORD_ACCESS 1 #elif defined(__mc68020__) @@ -146,8 +148,4 @@ # undef RBIMPL_TEST3 #endif /* HAVE_VA_ARGS_MACRO */ -#ifndef USE_RVARGC -# define USE_RVARGC 1 -#endif - #endif /* RBIMPL_CONFIG_H */ diff --git a/include/ruby/internal/core/rarray.h b/include/ruby/internal/core/rarray.h index ea37c8dfad..73cc0f5dd9 100644 --- a/include/ruby/internal/core/rarray.h +++ b/include/ruby/internal/core/rarray.h @@ -29,25 +29,13 @@ #include "ruby/internal/core/rbasic.h" #include "ruby/internal/dllexport.h" #include "ruby/internal/fl_type.h" -#include "ruby/internal/rgengc.h" +#include "ruby/internal/gc.h" #include "ruby/internal/stdbool.h" #include "ruby/internal/value.h" #include "ruby/internal/value_type.h" #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 */ @@ -99,6 +80,8 @@ * here is at least incomplete. */ enum ruby_rarray_flags { + /* RUBY_FL_USER0 is for ELTS_SHARED */ + /** * This flag has something to do with memory footprint. If the array is * "small" enough, ruby tries to be creative to abuse padding bits of @@ -118,8 +101,6 @@ enum ruby_rarray_flags { */ RARRAY_EMBED_FLAG = RUBY_FL_USER1, - /* RUBY_FL_USER2 is for ELTS_SHARED */ - /** * When an array employs embedded strategy (see ::RARRAY_EMBED_FLAG), these * bits are used to store the number of elements actually filled into @@ -130,30 +111,8 @@ enum ruby_rarray_flags { * 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. */ -#if USE_RVARGC 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 -#else - RARRAY_EMBED_LEN_MASK = RUBY_FL_USER4 | RUBY_FL_USER3 -#endif - -#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 }; /** @@ -163,13 +122,6 @@ enum ruby_rarray_flags { enum ruby_rarray_consts { /** Where ::RARRAY_EMBED_LEN_MASK resides. */ RARRAY_EMBED_LEN_SHIFT = RUBY_FL_USHIFT + 3 - -#if !USE_RVARGC - , - - /** Max possible number elements that can be embedded. */ - RARRAY_EMBED_LEN_MAX = RBIMPL_EMBED_LEN_MAX_OF(VALUE) -#endif }; /** Ruby's array. */ @@ -228,16 +180,12 @@ struct RArray { * to store its elements. In this case the length is encoded into the * flags. */ -#if USE_RVARGC /* 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) * 2. Zero length arrays are not supported by all compilers */ const VALUE ary[1]; -#else - const VALUE ary[RARRAY_EMBED_LEN_MAX]; -#endif } as; }; @@ -264,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() @@ -346,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 * @@ -383,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); @@ -395,110 +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_START(a) rb_array_ptr_use_start(a, 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. @@ -524,37 +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) - -/** - * @private - * - * This is an implementation detail of #RARRAY_PTR_USE_TRANSIENT. People do - * not use it directly. - */ -#define RARRAY_PTR_USE_START_TRANSIENT(a) rb_array_ptr_use_start(a, 1) - -/** - * @private - * - * This is an implementation detail of #RARRAY_PTR_USE_TRANSIENT. People do - * not use it directly. - */ -#define RARRAY_PTR_USE_END_TRANSIENT(a) rb_array_ptr_use_end(a, 1) - -/** - * 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. * @@ -589,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)); } @@ -604,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..63cdff8e09 100644 --- a/include/ruby/internal/core/rbasic.h +++ b/include/ruby/internal/core/rbasic.h @@ -55,23 +55,27 @@ enum ruby_rvalue_flags { RVALUE_EMBED_LEN_MAX = RBIMPL_RVALUE_EMBED_LEN_MAX }; +#if (SIZEOF_VALUE < SIZEOF_UINT64_T) +#define RBASIC_SHAPE_ID_FIELD 1 +#else +#define RBASIC_SHAPE_ID_FIELD 0 +#endif + /** - * 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,14 +83,18 @@ 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; +#if RBASIC_SHAPE_ID_FIELD + VALUE shape_id; +#endif + #ifdef __cplusplus public: RBIMPL_ATTR_CONSTEXPR(CXX11) @@ -102,8 +110,14 @@ RBasic { RBasic() : flags(RBIMPL_VALUE_NULL), klass(RBIMPL_VALUE_NULL) +#if RBASIC_SHAPE_ID_FIELD + , shape_id(RBIMPL_VALUE_NULL) +#endif { } +# define RBASIC_INIT RBasic() +#else +# define RBASIC_INIT {RBIMPL_VALUE_NULL} #endif }; diff --git a/include/ruby/internal/core/rclass.h b/include/ruby/internal/core/rclass.h index b0b6bfc80c..6f78cc569b 100644 --- a/include/ruby/internal/core/rclass.h +++ b/include/ruby/internal/core/rclass.h @@ -58,7 +58,7 @@ enum ruby_rmodule_flags { * rb_mod_refine() has this flag set. This is the bit which controls * difference between normal inclusion versus refinements. */ - RMODULE_IS_REFINEMENT = RUBY_FL_USER3 + RMODULE_IS_REFINEMENT = RUBY_FL_USER1 }; struct RClass; /* Opaque, declared here for RCLASS() macro. */ diff --git a/include/ruby/internal/core/rdata.h b/include/ruby/internal/core/rdata.h index 43ab3c01e7..cee5e7b5ea 100644 --- a/include/ruby/internal/core/rdata.h +++ b/include/ruby/internal/core/rdata.h @@ -37,12 +37,8 @@ #include "ruby/defines.h" /** @cond INTERNAL_MACRO */ -#ifdef RUBY_UNTYPED_DATA_WARNING -# /* Take that. */ -#elif defined(RUBY_EXPORT) -# define RUBY_UNTYPED_DATA_WARNING 1 -#else -# define RUBY_UNTYPED_DATA_WARNING 0 +#ifndef RUBY_UNTYPED_DATA_WARNING +#define RUBY_UNTYPED_DATA_WARNING 1 #endif #define RBIMPL_DATA_FUNC(f) RBIMPL_CAST((void (*)(void *))(f)) @@ -146,7 +142,10 @@ struct RData { */ RUBY_DATA_FUNC dfree; - /** Pointer to the actual C level struct that you want to wrap. */ + /** Pointer to the actual C level struct that you want to wrap. + * This is after dmark and dfree to allow DATA_PTR to continue to work for + * both RData and non-embedded RTypedData. + */ void *data; }; @@ -181,11 +180,6 @@ VALUE rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_D */ VALUE rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree); -/** - * @private - * Documented in include/ruby/internal/globals.h - */ -RUBY_EXTERN VALUE rb_cObject; RBIMPL_SYMBOL_EXPORT_END() /** @@ -331,15 +325,6 @@ rb_data_object_get_warning(VALUE obj) return rb_data_object_get(obj); } -#if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P) -# define rb_data_object_wrap_warning(klass, ptr, mark, free) \ - RB_GNUC_EXTENSION( \ - __builtin_choose_expr( \ - __builtin_constant_p(klass) && !(klass), \ - rb_data_object_wrap(klass, ptr, mark, free), \ - (rb_data_object_wrap_warning)(klass, ptr, mark, free))) -#endif - /** * This is an implementation detail of #Data_Make_Struct. People don't use it * directly. @@ -361,14 +346,6 @@ rb_data_object_make(VALUE klass, RUBY_DATA_FUNC mark_func, RUBY_DATA_FUNC free_f return result; } -RBIMPL_ATTR_DEPRECATED(("by: rb_data_object_wrap")) -/** @deprecated This function was renamed to rb_data_object_wrap(). */ -static inline VALUE -rb_data_object_alloc(VALUE klass, void *data, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree) -{ - return rb_data_object_wrap(klass, data, dmark, dfree); -} - /** @cond INTERNAL_MACRO */ #define rb_data_object_wrap_0 rb_data_object_wrap #define rb_data_object_wrap_1 rb_data_object_wrap_warning diff --git a/include/ruby/internal/core/rfile.h b/include/ruby/internal/core/rfile.h index f8dddde9e5..a0eb8cb833 100644 --- a/include/ruby/internal/core/rfile.h +++ b/include/ruby/internal/core/rfile.h @@ -25,7 +25,7 @@ /* rb_io_t is in ruby/io.h. The header file has historically not been included * into ruby/ruby.h. We follow that tradition. */ -struct rb_io_t; +struct rb_io; /** * Ruby's File and IO. Ruby's IO are not just file descriptors. They have @@ -38,7 +38,7 @@ struct RFile { struct RBasic basic; /** IO's specific fields. */ - struct rb_io_t *fptr; + struct rb_io *fptr; }; /** diff --git a/include/ruby/internal/core/rhash.h b/include/ruby/internal/core/rhash.h index 61d2c15d87..897c570794 100644 --- a/include/ruby/internal/core/rhash.h +++ b/include/ruby/internal/core/rhash.h @@ -54,19 +54,6 @@ * * @internal * - * Declaration of rb_hash_iter_lev() is at include/ruby/backward.h. - */ -#define RHASH_ITER_LEV(h) rb_hash_iter_lev(h) - -/** - * @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. - * - * @internal - * * Declaration of rb_hash_ifnone() is at include/ruby/backward.h. */ #define RHASH_IFNONE(h) rb_hash_ifnone(h) 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 f2028063a6..99f6470ac1 100644 --- a/include/ruby/internal/core/robject.h +++ b/include/ruby/internal/core/robject.h @@ -37,16 +37,15 @@ /** * Convenient casting macro. * - * @param obj An object, which is in fact an ::RRegexp. - * @return The passed object casted to ::RRegexp. + * @param obj An object, which is in fact an ::RObject. + * @return The passed object casted to ::RObject. */ #define ROBJECT(obj) RBIMPL_CAST((struct RObject *)(obj)) /** @cond INTERNAL_MACRO */ -#define ROBJECT_EMBED_LEN_MAX ROBJECT_EMBED_LEN_MAX -#define ROBJECT_EMBED ROBJECT_EMBED -#define ROBJECT_NUMIV ROBJECT_NUMIV -#define ROBJECT_IVPTR ROBJECT_IVPTR -#define ROBJECT_IV_INDEX_TBL ROBJECT_IV_INDEX_TBL +#define ROBJECT_EMBED_LEN_MAX ROBJECT_EMBED_LEN_MAX +#define ROBJECT_HEAP ROBJECT_HEAP +#define ROBJECT_FIELDS_CAPACITY ROBJECT_FIELDS_CAPACITY +#define ROBJECT_FIELDS ROBJECT_FIELDS /** @endcond */ /** @@ -56,10 +55,12 @@ */ enum ruby_robject_flags { /** - * This flag has something to do with memory footprint. If the object is - * "small" enough, ruby tries to be creative to abuse padding bits of - * struct ::RObject for storing instance variables. This flag denotes that - * situation. + * This flag marks that the object's instance variables are stored in an + * external heap buffer. + * Normally, instance variable references are stored inside the object slot, + * but if it overflow, Ruby may have to allocate a separate buffer and spills + * the instance variables there. + * This flag denotes that situation. * * @warning This bit has to be considered read-only. Setting/clearing * this bit without corresponding fix up must cause immediate @@ -72,16 +73,7 @@ enum ruby_robject_flags { * 3rd parties must not be aware that there even is more than one way to * store instance variables. Might better be hidden. */ - ROBJECT_EMBED = RUBY_FL_USER1 -}; - -/** - * This is an enum because GDB wants it (rather than a macro). People need not - * bother. - */ -enum ruby_robject_consts { - /** Max possible number of instance variables that can be embedded. */ - ROBJECT_EMBED_LEN_MAX = RBIMPL_EMBED_LEN_MAX_OF(VALUE) + ROBJECT_HEAP = RUBY_FL_USER4 }; struct st_table; @@ -103,60 +95,25 @@ struct RObject { * this pattern. */ struct { - - /** - * Number of instance variables. This is per object; objects might - * differ in this field even if they have the identical classes. - */ - uint32_t numiv; - /** Pointer to a C array that holds instance variables. */ - VALUE *ivptr; - - /** - * This is a table that holds instance variable name to index - * mapping. Used when accessing instance variables using names. - * - * @internal - * - * This is a shortcut for `RCLASS_IV_INDEX_TBL(rb_obj_class(obj))`. - */ - struct st_table *iv_index_tbl; + VALUE *fields; } heap; - /** - * Embedded instance variables. When an object is small enough, it + /* Embedded instance variables. When an object is small enough, it * uses this area to store the instance variables. + * + * 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) + * 2. Zero length arrays are not supported by all compilers */ - VALUE ary[ROBJECT_EMBED_LEN_MAX]; + VALUE ary[1]; } as; }; RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** - * Queries the number of instance variables. - * - * @param[in] obj Object in question. - * @return Its number of instance variables. - * @pre `obj` must be an instance of ::RObject. - */ -static inline uint32_t -ROBJECT_NUMIV(VALUE obj) -{ - RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); - - if (RB_FL_ANY_RAW(obj, ROBJECT_EMBED)) { - return ROBJECT_EMBED_LEN_MAX; - } - else { - return ROBJECT(obj)->as.heap.numiv; - } -} - -RBIMPL_ATTR_PURE_UNLESS_DEBUG() -RBIMPL_ATTR_ARTIFICIAL() -/** * Queries the instance variables. * * @param[in] obj Object in question. @@ -168,17 +125,17 @@ RBIMPL_ATTR_ARTIFICIAL() * @shyouhei finds no reason for this to be visible from extension libraries. */ static inline VALUE * -ROBJECT_IVPTR(VALUE obj) +ROBJECT_FIELDS(VALUE obj) { RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); struct RObject *const ptr = ROBJECT(obj); - if (RB_FL_ANY_RAW(obj, ROBJECT_EMBED)) { - return ptr->as.ary; + if (RB_UNLIKELY(RB_FL_ANY_RAW(obj, ROBJECT_HEAP))) { + return ptr->as.heap.fields; } else { - return ptr->as.heap.ivptr; + return ptr->as.ary; } } diff --git a/include/ruby/internal/core/rstring.h b/include/ruby/internal/core/rstring.h index e394ab7dca..35175ea94a 100644 --- a/include/ruby/internal/core/rstring.h +++ b/include/ruby/internal/core/rstring.h @@ -42,13 +42,7 @@ /** @cond INTERNAL_MACRO */ #define RSTRING_NOEMBED RSTRING_NOEMBED -#if !USE_RVARGC -#define RSTRING_EMBED_LEN_MASK RSTRING_EMBED_LEN_MASK -#define RSTRING_EMBED_LEN_SHIFT RSTRING_EMBED_LEN_SHIFT -#define RSTRING_EMBED_LEN_MAX RSTRING_EMBED_LEN_MAX -#endif #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 @@ -162,21 +156,6 @@ enum ruby_rstring_flags { */ RSTRING_NOEMBED = RUBY_FL_USER1, -#if !USE_RVARGC - /** - * When a string employs embedded strategy (see ::RSTRING_NOEMBED), these - * bits are used to store the number of bytes actually filled into - * ::RString::ary. - * - * @internal - * - * 3rd parties must not be aware that there even is more than one way to - * store a string. Might better be hidden. - */ - RSTRING_EMBED_LEN_MASK = RUBY_FL_USER2 | RUBY_FL_USER3 | RUBY_FL_USER4 | - RUBY_FL_USER5 | RUBY_FL_USER6, -#endif - /* Actually, string encodings are also encoded into the flags, using * remaining bits.*/ @@ -202,20 +181,6 @@ enum ruby_rstring_flags { RSTRING_FSTR = RUBY_FL_USER17 }; -#if !USE_RVARGC -/** - * This is an enum because GDB wants it (rather than a macro). People need not - * bother. - */ -enum ruby_rstring_consts { - /** Where ::RSTRING_EMBED_LEN_MASK resides. */ - RSTRING_EMBED_LEN_SHIFT = RUBY_FL_USHIFT + 2, - - /** Max possible number of characters that can be embedded. */ - RSTRING_EMBED_LEN_MAX = RBIMPL_EMBED_LEN_MAX_OF(char) - 1 -}; -#endif - /** * Ruby's String. A string in ruby conceptually has these information: * @@ -233,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 { @@ -241,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 @@ -279,24 +243,12 @@ struct RString { /** Embedded contents. */ struct { -#if USE_RVARGC - 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) * 2. Zero length arrays are not supported by all compilers */ char ary[1]; -#else - /** - * When a string is short enough, it uses this area to store the - * contents themselves. This was impractical in the 20th century, - * but these days 64 bit machines can typically hold 24 bytes here. - * Could be sufficiently large. In this case the length is encoded - * into the flags. - */ - char ary[RSTRING_EMBED_LEN_MAX + 1]; -#endif } embed; } as; }; @@ -409,81 +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. - */ -static inline long -RSTRING_EMBED_LEN(VALUE str) -{ - RBIMPL_ASSERT_TYPE(str, RUBY_T_STRING); - RBIMPL_ASSERT_OR_ASSUME(! RB_FL_ANY_RAW(str, RSTRING_NOEMBED)); - -#if USE_RVARGC - long f = RSTRING(str)->as.embed.len; - return f; -#else - VALUE f = RBASIC(str)->flags; - f &= RSTRING_EMBED_LEN_MASK; - f >>= RSTRING_EMBED_LEN_SHIFT; - return RBIMPL_CAST((long)f); -#endif -} - -RBIMPL_WARNING_PUSH() -#if RBIMPL_COMPILER_IS(Intel) -RBIMPL_WARNING_IGNORED(413) -#endif - -RBIMPL_ATTR_PURE_UNLESS_DEBUG() -RBIMPL_ATTR_ARTIFICIAL() -/** - * @private - * - * "Expands" an embedded string into an ordinal one. This is a function that - * returns aggregated type. The returned struct always has its `as.heap.len` - * an `as.heap.ptr` fields set appropriately. - * - * This is an implementation detail that 3rd parties should never bother. - */ -static inline struct RString -rbimpl_rstring_getmem(VALUE str) -{ - RBIMPL_ASSERT_TYPE(str, RUBY_T_STRING); - - if (RB_FL_ANY_RAW(str, RSTRING_NOEMBED)) { - return *RSTRING(str); - } - else { - /* Expecting compilers to optimize this on-stack struct away. */ - struct RString retval; - retval.as.heap.len = RSTRING_EMBED_LEN(str); - retval.as.heap.ptr = RSTRING(str)->as.embed.ary; - return retval; - } -} - -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; + return RSTRING(str)->len; } RBIMPL_ATTR_ARTIFICIAL() @@ -497,15 +380,13 @@ RBIMPL_ATTR_ARTIFICIAL() static inline char * RSTRING_PTR(VALUE str) { - char *ptr = rbimpl_rstring_getmem(str).as.heap.ptr; + char *ptr = RB_FL_TEST_RAW(str, RSTRING_NOEMBED) ? + RSTRING(str)->as.heap.ptr : + RSTRING(str)->as.embed.ary; - 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 @@ -527,14 +408,17 @@ RBIMPL_ATTR_ARTIFICIAL() static inline char * RSTRING_END(VALUE str) { - struct RString buf = rbimpl_rstring_getmem(str); + char *ptr = RB_FL_TEST_RAW(str, RSTRING_NOEMBED) ? + RSTRING(str)->as.heap.ptr : + RSTRING(str)->as.embed.ary; + long len = RSTRING_LEN(str); - if (RB_UNLIKELY(! buf.as.heap.ptr)) { + if (RUBY_DEBUG && RB_UNLIKELY(!ptr)) { /* Ditto. */ rb_debug_rstring_null_ptr("RSTRING_END"); } - return &buf.as.heap.ptr[buf.as.heap.len]; + return &ptr[len]; } RBIMPL_ATTR_ARTIFICIAL() @@ -563,16 +447,7 @@ RSTRING_LENINT(VALUE str) * @param ptrvar Variable where its contents is stored. * @param lenvar Variable where its length is stored. */ -#ifdef HAVE_STMT_AND_DECL_IN_EXPR -# define RSTRING_GETMEM(str, ptrvar, lenvar) \ - __extension__ ({ \ - struct RString rbimpl_str = rbimpl_rstring_getmem(str); \ - (ptrvar) = rbimpl_str.as.heap.ptr; \ - (lenvar) = rbimpl_str.as.heap.len; \ - }) -#else # define RSTRING_GETMEM(str, ptrvar, lenvar) \ ((ptrvar) = RSTRING_PTR(str), \ (lenvar) = RSTRING_LEN(str)) -#endif /* HAVE_STMT_AND_DECL_IN_EXPR */ #endif /* RBIMPL_RSTRING_H */ diff --git a/include/ruby/internal/core/rstruct.h b/include/ruby/internal/core/rstruct.h index 69be487b59..0028a1bdcd 100644 --- a/include/ruby/internal/core/rstruct.h +++ b/include/ruby/internal/core/rstruct.h @@ -31,18 +31,6 @@ # include "ruby/backward.h" #endif -/** - * @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. - * - * @internal - * - * Declaration of rb_struct_ptr() is at include/ruby/backward.h. - */ -#define RSTRUCT_PTR(st) rb_struct_ptr(st) /** @cond INTERNAL_MACRO */ #define RSTRUCT_LEN RSTRUCT_LEN #define RSTRUCT_SET RSTRUCT_SET diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h index bbf208867d..ec0794e387 100644 --- a/include/ruby/internal/core/rtypeddata.h +++ b/include/ruby/internal/core/rtypeddata.h @@ -37,6 +37,7 @@ #include "ruby/internal/dllexport.h" #include "ruby/internal/error.h" #include "ruby/internal/fl_type.h" +#include "ruby/internal/static_assert.h" #include "ruby/internal/stdbool.h" #include "ruby/internal/value_type.h" @@ -108,11 +109,17 @@ /** @cond INTERNAL_MACRO */ #define RTYPEDDATA_P RTYPEDDATA_P #define RTYPEDDATA_TYPE RTYPEDDATA_TYPE +#define TYPED_DATA_EMBEDDED ((VALUE)1) +#define TYPED_DATA_PTR_MASK (~(TYPED_DATA_EMBEDDED)) +/** @endcond */ + +/** + * Macros to see if each corresponding flag is defined. + */ #define RUBY_TYPED_FREE_IMMEDIATELY RUBY_TYPED_FREE_IMMEDIATELY #define RUBY_TYPED_FROZEN_SHAREABLE RUBY_TYPED_FROZEN_SHAREABLE #define RUBY_TYPED_WB_PROTECTED RUBY_TYPED_WB_PROTECTED #define RUBY_TYPED_PROMOTED1 RUBY_TYPED_PROMOTED1 -/** @endcond */ /** * @private @@ -137,6 +144,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 @@ -151,6 +160,12 @@ rbimpl_typeddata_flags { */ RUBY_TYPED_FROZEN_SHAREABLE = RUBY_FL_SHAREABLE, + // experimental flag + // Similar to RUBY_TYPED_FROZEN_SHAREABLE, but doesn't make shareable + // reachable objects from this T_DATA object on the Ractor.make_shareable. + // If it refers to unshareable objects, simply raise an error. + // RUBY_TYPED_FROZEN_SHAREABLE_NO_REC = RUBY_FL_FINALIZE, + /** * This flag has something to do with our garbage collector. These days * ruby objects are "generational". There are those who are young and @@ -173,10 +188,16 @@ 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 is used to distinguish RTypedData from deprecated RData objects. */ - RUBY_TYPED_PROMOTED1 = RUBY_FL_PROMOTED1 /* THIS FLAG DEPENDS ON Ruby version */ + RUBY_TYPED_FL_IS_TYPED_DATA = RUBY_FL_USERPRIV0, + + /** + * This flag determines whether marking and compaction should be carried out + * using the dmark/dcompact callback functions or whether we should mark + * declaratively using a list of references defined inside the data struct we're wrapping + */ + RUBY_TYPED_DECL_MARKING = RUBY_FL_USER2 }; /** @@ -241,10 +262,15 @@ struct rb_data_type_struct { RUBY_DATA_FUNC dcompact; /** - * This field is reserved for future extension. For now, it must be - * filled with zeros. + * @internal */ - void *reserved[1]; /* For future extension. + void (*handle_weak_references)(void *); + + /** + * This field is reserved for future extension. For now, it must be + * filled with zeros. + */ + void *reserved[7]; /* For future extension. This array *must* be filled with ZERO. */ } function; @@ -342,26 +368,28 @@ struct RTypedData { /** The part that all ruby objects have in common. */ struct RBasic basic; - /** - * This field stores various information about how Ruby should handle a - * data. This roughly resembles a Ruby level class (apart from method - * definition etc.) - */ - const rb_data_type_t *type; + /** Direct reference to the slots that holds instance variables, if any **/ + VALUE fields_obj; /** - * This has to be always 1. + * This is a `const rb_data_type_t *const` value, with the low bits set: * - * @internal + * 1: Set if object is embedded. * - * Why, then, this is not a const ::VALUE? + * This field stores various information about how Ruby should handle a + * data. This roughly resembles a Ruby level class (apart from method + * definition etc.) */ - VALUE typed_flag; + const VALUE type; /** Pointer to the actual C level struct that you want to wrap. */ void *data; }; +#if !defined(__cplusplus) || __cplusplus >= 201103L +RBIMPL_STATIC_ASSERT(data_in_rtypeddata, offsetof(struct RData, data) == offsetof(struct RTypedData, data)); +#endif + RBIMPL_SYMBOL_EXPORT_BEGIN() RBIMPL_ATTR_NONNULL((3)) /** @@ -376,6 +404,7 @@ RBIMPL_ATTR_NONNULL((3)) */ VALUE rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type); +RBIMPL_ATTR_NONNULL((3)) /** * Identical to rb_data_typed_object_wrap(), except it allocates a new data * region internally instead of taking an existing one. The allocation is done @@ -391,6 +420,7 @@ VALUE rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t * */ VALUE rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t *type); +RBIMPL_ATTR_NONNULL(()) /** * Checks for the domestic relationship between the two. * @@ -405,6 +435,7 @@ VALUE rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t */ int rb_typeddata_inherited_p(const rb_data_type_t *child, const rb_data_type_t *parent); +RBIMPL_ATTR_NONNULL((2)) /** * Checks if the given object is of given kind. * @@ -415,6 +446,7 @@ int rb_typeddata_inherited_p(const rb_data_type_t *child, const rb_data_type_t * */ int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type); +RBIMPL_ATTR_NONNULL((2)) /** * Identical to rb_typeddata_is_kind_of(), except it raises exceptions instead * of returning false. @@ -426,8 +458,49 @@ int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type); * @post Upon successful return `obj`'s type is guaranteed `data_type`. */ void *rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type); + +RBIMPL_ATTR_NORETURN() +RBIMPL_ATTR_NONNULL((2)) +/** + * @private + * + * Fails with the given object's type incompatibility to the type. + * + * This is an implementation detail of Check_Type. People don't use it + * directly. + * + * @param[in] obj The object in question. + * @param[in] expected Name of expected data type of `obj`. + */ +void rb_unexpected_object_type(VALUE obj, const char *expected); + +RBIMPL_ATTR_NORETURN() +RBIMPL_ATTR_NONNULL(()) +/** + * @private + * + * Fails with the given object's type incompatibility to the type. + * + * This is an implementation detail of #TypedData_Make_Struct. People don't + * use it directly. + * + * @param[in] actual Actual data type. + * @param[in] expected Expected data type. + */ +void rb_unexpected_typeddata(const rb_data_type_t *actual, const rb_data_type_t *expected); RBIMPL_SYMBOL_EXPORT_END() +#if RUBY_DEBUG +# define RBIMPL_TYPEDDATA_PRECONDITION(obj, unreachable) \ + while (RB_UNLIKELY(!RB_TYPE_P(obj, RUBY_T_DATA))) { \ + rb_unexpected_object_type(obj, "Data"); \ + unreachable; \ + } +#else +# define RBIMPL_TYPEDDATA_PRECONDITION(obj, unreachable) \ + RBIMPL_ASSERT_NOTHING +#endif + /** * Converts sval, a pointer to your struct, into a Ruby object. * @@ -456,14 +529,13 @@ 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) = RBIMPL_CAST((type *)rbimpl_typeddata_get_data(result)); \ RBIMPL_CAST(/*suppress unused variable warnings*/(void)(sval)) /** * Identical to #TypedData_Wrap_Struct, except it allocates a new data region * internally instead of taking an existing one. The allocation is done using - * ruby_calloc(). Hence it makes no sense for `data_type->function.dfree` to - * be anything other than ::RUBY_TYPED_DEFAULT_FREE. + * ruby_calloc(). * * @param klass Ruby level class of the object. * @param type Type name of the C struct. @@ -494,18 +566,37 @@ RBIMPL_SYMBOL_EXPORT_END() sizeof(type)) #endif -/** - * Obtains a C struct from inside of a wrapper Ruby object. - * - * @param obj An instance of ::RTypedData. - * @param type Type name of the C struct. - * @param data_type The data type describing `type`. - * @param sval Variable name of obtained C struct. - * @exception rb_eTypeError `obj` is not a kind of `data_type`. - * @return Unwrapped C struct that `obj` holds. - */ -#define TypedData_Get_Struct(obj,type,data_type,sval) \ - ((sval) = RBIMPL_CAST((type *)rb_check_typeddata((obj), (data_type)))) +static inline bool +rbimpl_typeddata_embedded_p(VALUE obj) +{ + return (RTYPEDDATA(obj)->type) & TYPED_DATA_EMBEDDED; +} + +RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY() +static inline bool +RTYPEDDATA_EMBEDDED_P(VALUE obj) +{ + RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(false)); + + return rbimpl_typeddata_embedded_p(obj); +} + +static inline void * +rbimpl_typeddata_get_data(VALUE obj) +{ + /* We reuse the data pointer in embedded TypedData. */ + return rbimpl_typeddata_embedded_p(obj) ? + RBIMPL_CAST((void *)&RTYPEDDATA_DATA(obj)) : + RTYPEDDATA_DATA(obj); +} + +static inline void * +RTYPEDDATA_GET_DATA(VALUE obj) +{ + RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(NULL)); + + return rbimpl_typeddata_get_data(obj); +} RBIMPL_ATTR_PURE() RBIMPL_ATTR_ARTIFICIAL() @@ -523,7 +614,28 @@ RBIMPL_ATTR_ARTIFICIAL() static inline bool rbimpl_rtypeddata_p(VALUE obj) { - return RTYPEDDATA(obj)->typed_flag == 1; + return FL_TEST_RAW(obj, RUBY_TYPED_FL_IS_TYPED_DATA); +} + +RBIMPL_ATTR_PURE() +RBIMPL_ATTR_ARTIFICIAL() +/** + * @private + * + * Identical to rbimpl_rtypeddata_p(), except it is allowed to call on non-data + * objects. + * + * This is an implementation detail of inline functions defined in this file. + * People don't use it directly. + * + * @param[in] obj Object in question + * @retval true `obj` is an instance of ::RTypedData. + * @retval false `obj` is not an instance of ::RTypedData + */ +static inline bool +rbimpl_obj_typeddata_p(VALUE obj) +{ + return RB_TYPE_P(obj, RUBY_T_DATA) && rbimpl_rtypeddata_p(obj); } RBIMPL_ATTR_PURE_UNLESS_DEBUG() @@ -539,19 +651,14 @@ RBIMPL_ATTR_ARTIFICIAL() static inline bool RTYPEDDATA_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 + RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(false)); return rbimpl_rtypeddata_p(obj); } RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() -/* :TODO: can this function be __attribute__((returns_nonnull)) or not? */ +RBIMPL_ATTR_RETURNS_NONNULL() /** * Queries for the type of given object. * @@ -559,19 +666,77 @@ RBIMPL_ATTR_ARTIFICIAL() * @return Data type struct that corresponds to `obj`. * @pre `obj` must be an instance of ::RTypedData. */ -static inline const struct rb_data_type_struct * +static inline const rb_data_type_t * RTYPEDDATA_TYPE(VALUE obj) { -#if RUBY_DEBUG - if (RB_UNLIKELY(! RTYPEDDATA_P(obj))) { - rb_unexpected_type(obj, RUBY_T_DATA); - RBIMPL_UNREACHABLE_RETURN(NULL); + RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(NULL)); + + VALUE type = RTYPEDDATA(obj)->type & TYPED_DATA_PTR_MASK; + const rb_data_type_t *ptr = RBIMPL_CAST((const rb_data_type_t *)type); + RBIMPL_ASSERT_OR_ASSUME(ptr); + return ptr; +} + +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NONNULL(()) +static inline bool +rbimpl_typeddata_inherited_p_inline(const rb_data_type_t *child, const rb_data_type_t *parent) +{ + do { + if (RB_LIKELY(child == parent)) return true; + } while ((child = child->parent) != NULL); + return false; +} +#define rb_typeddata_inherited_p rbimpl_typeddata_inherited_p_inline + +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NONNULL((2)) +static inline bool +rbimpl_typeddata_is_kind_of_inline(VALUE obj, const rb_data_type_t *data_type) +{ + if (RB_UNLIKELY(!rbimpl_obj_typeddata_p(obj))) return false; + return rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type); +} +#define rb_typeddata_is_kind_of rbimpl_typeddata_is_kind_of_inline + +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NONNULL((2)) +/** + * @private + * + * This is an implementation detail of TypedData_Get_Struct(). Don't use it + * directly. + */ +static inline void * +rbimpl_check_typeddata(VALUE obj, const rb_data_type_t *expected_type) +{ + if (RB_UNLIKELY(!rbimpl_obj_typeddata_p(obj))) { + rb_unexpected_object_type(obj, expected_type->wrap_struct_name); + } + + const rb_data_type_t *actual_type = RTYPEDDATA_TYPE(obj); + if (RB_UNLIKELY(!rb_typeddata_inherited_p(actual_type, expected_type))){ + rb_unexpected_typeddata(actual_type, expected_type); } -#endif - return RTYPEDDATA(obj)->type; + return RTYPEDDATA_GET_DATA(obj); } + +/** + * Obtains a C struct from inside of a wrapper Ruby object. + * + * @param obj An instance of ::RTypedData. + * @param type Type name of the C struct. + * @param data_type The data type describing `type`. + * @param sval Variable name of obtained C struct. + * @exception rb_eTypeError `obj` is not a kind of `data_type`. + * @return Unwrapped C struct that `obj` holds. + */ +#define TypedData_Get_Struct(obj,type,data_type,sval) \ + ((sval) = RBIMPL_CAST((type *)rbimpl_check_typeddata((obj), (data_type)))) + +RBIMPL_ATTR_NONNULL((2)) /** * While we don't stop you from using this function, it seems to be an * implementation detail of #TypedData_Make_Struct, which is preferred over @@ -593,12 +758,4 @@ rb_data_typed_object_make(VALUE klass, const rb_data_type_t *type, void **datap, return result; } -RBIMPL_ATTR_DEPRECATED(("by: rb_data_typed_object_wrap")) -/** @deprecated This function was renamed to rb_data_typed_object_wrap(). */ -static inline VALUE -rb_data_typed_object_alloc(VALUE klass, void *datap, const rb_data_type_t *type) -{ - return rb_data_typed_object_wrap(klass, datap, type); -} - #endif /* RBIMPL_RTYPEDDATA_H */ diff --git a/include/ruby/internal/ctype.h b/include/ruby/internal/ctype.h index 0f7ca6c516..8b24026311 100644 --- a/include/ruby/internal/ctype.h +++ b/include/ruby/internal/ctype.h @@ -498,8 +498,8 @@ RBIMPL_ATTR_ARTIFICIAL() * Our own locale-insensitive version of `tolower(3)`. * * @param[in] c Byte in question to convert. - * @retval c The byte is not listed in in IEEE 1003.1 section - * 7.3.1.1 "upper". + * @retval c The byte is not listed in IEEE 1003.1 section 7.3.1.1 + * "upper". * @retval otherwise Byte converted using the map defined in IEEE 1003.1 * section 7.3.1 "tolower". * @note Not only does this function works under the POSIX Locale, but diff --git a/include/ruby/internal/dllexport.h b/include/ruby/internal/dllexport.h index 08a262209d..71026e7100 100644 --- a/include/ruby/internal/dllexport.h +++ b/include/ruby/internal/dllexport.h @@ -37,9 +37,7 @@ * ``` */ #undef RUBY_EXTERN -#if defined(MJIT_HEADER) && defined(_WIN32) -# define RUBY_EXTERN extern __declspec(dllimport) -#elif defined(RUBY_EXPORT) +#if defined(RUBY_EXPORT) # define RUBY_EXTERN extern #elif defined(_WIN32) # define RUBY_EXTERN extern __declspec(dllimport) @@ -59,36 +57,6 @@ # define RUBY_FUNC_EXPORTED /* void */ #endif -/** - * @cond INTERNAL_MACRO - * - * These MJIT related macros are placed here because translate_mjit_header can - * need them. Extension libraries should not touch. - */ - -/* These macros are used for functions which are exported only for MJIT - and NOT ensured to be exported in future versions. */ - -#if ! defined(MJIT_HEADER) -# define MJIT_FUNC_EXPORTED RUBY_FUNC_EXPORTED -#elif ! RBIMPL_COMPILER_IS(MSVC) -# define MJIT_FUNC_EXPORTED RUBY_FUNC_EXPORTED -#else -# define MJIT_FUNC_EXPORTED static -#endif - -#define MJIT_SYMBOL_EXPORT_BEGIN RUBY_SYMBOL_EXPORT_BEGIN -#define MJIT_SYMBOL_EXPORT_END RUBY_SYMBOL_EXPORT_END - -/* On mswin, MJIT header transformation can't be used since cl.exe can't output - preprocessed output preserving macros. So this `MJIT_STATIC` is needed - to force non-static function to static on MJIT header to avoid symbol conflict. */ -#ifdef MJIT_HEADER -# define MJIT_STATIC static -#else -# define MJIT_STATIC -#endif - /** @endcond */ /** Shortcut macro equivalent to `RUBY_SYMBOL_EXPORT_BEGIN extern "C" {`. diff --git a/include/ruby/internal/encoding/coderange.h b/include/ruby/internal/encoding/coderange.h index 7a81208c9e..c89f871518 100644 --- a/include/ruby/internal/encoding/coderange.h +++ b/include/ruby/internal/encoding/coderange.h @@ -79,7 +79,7 @@ RBIMPL_ATTR_CONST() static inline bool RB_ENC_CODERANGE_CLEAN_P(enum ruby_coderange_type cr) { - return rb_enc_coderange_clean_p(cr); + return rb_enc_coderange_clean_p(RBIMPL_CAST((int)cr)); } RBIMPL_ATTR_PURE_UNLESS_DEBUG() diff --git a/include/ruby/internal/encoding/ctype.h b/include/ruby/internal/encoding/ctype.h index 64aaf0a990..05c314aeb3 100644 --- a/include/ruby/internal/encoding/ctype.h +++ b/include/ruby/internal/encoding/ctype.h @@ -36,8 +36,8 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() * @param[in] p Pointer to a possibly-middle of a character. * @param[in] end End of the string. * @param[in] enc Encoding. - * @retval 0 It isn't. - * @retval otherwise It is. + * @retval false It isn't. + * @retval true It is. */ static inline bool rb_enc_is_newline(const char *p, const char *e, rb_encoding *enc) @@ -53,11 +53,11 @@ rb_enc_is_newline(const char *p, const char *e, rb_encoding *enc) * encoding. The "character type" here is a set of macros defined in onigmo.h, * like `ONIGENC_CTYPE_PUNCT`. * - * @param[in] c An `OnigCodePoint` value. - * @param[in] t An `OnigCtype` value. - * @param[in] enc A `rb_encoding*` value. - * @retval 1 `c` is of `t` in `enc`. - * @retval 0 Otherwise. + * @param[in] c An `OnigCodePoint` value. + * @param[in] t An `OnigCtype` value. + * @param[in] enc A `rb_encoding*` value. + * @retval true `c` is of `t` in `enc`. + * @retval false Otherwise. */ static inline bool rb_enc_isctype(OnigCodePoint c, OnigCtype t, rb_encoding *enc) @@ -68,10 +68,10 @@ rb_enc_isctype(OnigCodePoint c, OnigCtype t, rb_encoding *enc) /** * Identical to rb_isascii(), except it additionally takes an encoding. * - * @param[in] c A code point. - * @param[in] enc An encoding. - * @retval 0 `c` is out of range of ASCII character set in `enc`. - * @retval 1 Otherwise. + * @param[in] c A code point. + * @param[in] enc An encoding. + * @retval false `c` is out of range of ASCII character set in `enc`. + * @retval true Otherwise. * * @internal * @@ -87,10 +87,10 @@ rb_enc_isascii(OnigCodePoint c, rb_encoding *enc) /** * Identical to rb_isalpha(), except it additionally takes an encoding. * - * @param[in] c A code point. - * @param[in] enc An encoding. - * @retval 1 `enc` classifies `c` as "ALPHA". - * @retval 0 Otherwise. + * @param[in] c A code point. + * @param[in] enc An encoding. + * @retval true `enc` classifies `c` as "ALPHA". + * @retval false Otherwise. */ static inline bool rb_enc_isalpha(OnigCodePoint c, rb_encoding *enc) @@ -101,10 +101,10 @@ rb_enc_isalpha(OnigCodePoint c, rb_encoding *enc) /** * Identical to rb_islower(), except it additionally takes an encoding. * - * @param[in] c A code point. - * @param[in] enc An encoding. - * @retval 1 `enc` classifies `c` as "LOWER". - * @retval 0 Otherwise. + * @param[in] c A code point. + * @param[in] enc An encoding. + * @retval true `enc` classifies `c` as "LOWER". + * @retval false Otherwise. */ static inline bool rb_enc_islower(OnigCodePoint c, rb_encoding *enc) @@ -115,10 +115,10 @@ rb_enc_islower(OnigCodePoint c, rb_encoding *enc) /** * Identical to rb_isupper(), except it additionally takes an encoding. * - * @param[in] c A code point. - * @param[in] enc An encoding. - * @retval 1 `enc` classifies `c` as "UPPER". - * @retval 0 Otherwise. + * @param[in] c A code point. + * @param[in] enc An encoding. + * @retval true `enc` classifies `c` as "UPPER". + * @retval false Otherwise. */ static inline bool rb_enc_isupper(OnigCodePoint c, rb_encoding *enc) @@ -127,12 +127,26 @@ rb_enc_isupper(OnigCodePoint c, rb_encoding *enc) } /** + * Identical to rb_iscntrl(), except it additionally takes an encoding. + * + * @param[in] c A code point. + * @param[in] enc An encoding. + * @retval true `enc` classifies `c` as "CNTRL". + * @retval false Otherwise. + */ +static inline bool +rb_enc_iscntrl(OnigCodePoint c, rb_encoding *enc) +{ + return ONIGENC_IS_CODE_CNTRL(enc, c); +} + +/** * Identical to rb_ispunct(), except it additionally takes an encoding. * - * @param[in] c A code point. - * @param[in] enc An encoding. - * @retval 1 `enc` classifies `c` as "PUNCT". - * @retval 0 Otherwise. + * @param[in] c A code point. + * @param[in] enc An encoding. + * @retval true `enc` classifies `c` as "PUNCT". + * @retval false Otherwise. */ static inline bool rb_enc_ispunct(OnigCodePoint c, rb_encoding *enc) @@ -143,10 +157,10 @@ rb_enc_ispunct(OnigCodePoint c, rb_encoding *enc) /** * Identical to rb_isalnum(), except it additionally takes an encoding. * - * @param[in] c A code point. - * @param[in] enc An encoding. - * @retval 1 `enc` classifies `c` as "ANUM". - * @retval 0 Otherwise. + * @param[in] c A code point. + * @param[in] enc An encoding. + * @retval true `enc` classifies `c` as "ANUM". + * @retval false Otherwise. */ static inline bool rb_enc_isalnum(OnigCodePoint c, rb_encoding *enc) @@ -157,10 +171,10 @@ rb_enc_isalnum(OnigCodePoint c, rb_encoding *enc) /** * Identical to rb_isprint(), except it additionally takes an encoding. * - * @param[in] c A code point. - * @param[in] enc An encoding. - * @retval 1 `enc` classifies `c` as "PRINT". - * @retval 0 Otherwise. + * @param[in] c A code point. + * @param[in] enc An encoding. + * @retval true `enc` classifies `c` as "PRINT". + * @retval false Otherwise. */ static inline bool rb_enc_isprint(OnigCodePoint c, rb_encoding *enc) @@ -171,10 +185,10 @@ rb_enc_isprint(OnigCodePoint c, rb_encoding *enc) /** * Identical to rb_isspace(), except it additionally takes an encoding. * - * @param[in] c A code point. - * @param[in] enc An encoding. - * @retval 1 `enc` classifies `c` as "PRINT". - * @retval 0 Otherwise. + * @param[in] c A code point. + * @param[in] enc An encoding. + * @retval true `enc` classifies `c` as "PRINT". + * @retval false Otherwise. */ static inline bool rb_enc_isspace(OnigCodePoint c, rb_encoding *enc) @@ -185,10 +199,10 @@ rb_enc_isspace(OnigCodePoint c, rb_encoding *enc) /** * Identical to rb_isdigit(), except it additionally takes an encoding. * - * @param[in] c A code point. - * @param[in] enc An encoding. - * @retval 1 `enc` classifies `c` as "DIGIT". - * @retval 0 Otherwise. + * @param[in] c A code point. + * @param[in] enc An encoding. + * @retval true `enc` classifies `c` as "DIGIT". + * @retval false Otherwise. */ static inline bool rb_enc_isdigit(OnigCodePoint c, rb_encoding *enc) @@ -235,6 +249,7 @@ RBIMPL_SYMBOL_EXPORT_END() #define rb_enc_isdigit rb_enc_isdigit #define rb_enc_islower rb_enc_islower #define rb_enc_isprint rb_enc_isprint +#define rb_enc_iscntrl rb_enc_iscntrl #define rb_enc_ispunct rb_enc_ispunct #define rb_enc_isspace rb_enc_isspace #define rb_enc_isupper rb_enc_isupper diff --git a/include/ruby/internal/encoding/encoding.h b/include/ruby/internal/encoding/encoding.h index 22deb8f8c9..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); @@ -139,23 +140,6 @@ RBIMPL_ATTR_NOALIAS() int rb_char_to_option_kcode(int c, int *option, int *kcode); /** - * Creates a new encoding, using the passed one as a template. - * - * @param[in] name Name of the creating encoding. - * @param[in] src Template. - * @exception rb_eArgError Duplicated or malformed `name`. - * @return Replicated new encoding's index. - * @post Encoding named `name` is created as a copy of `src`, whose index - * is the return value. - * - * @internal - * - * `name` can be `NULL`, but that just raises an exception. OTOH it seems no - * sanity check is done against `src`...? - */ -int rb_enc_replicate(const char *name, rb_encoding *src); - -/** * Creates a new "dummy" encoding. Roughly speaking, an encoding is dummy when * it is stateful. Notable example of dummy encoding are those defined in * ISO/IEC 2022 @@ -643,10 +627,12 @@ rb_enc_code_to_mbclen(int c, rb_encoding *enc) * Identical to rb_enc_uint_chr(), except it writes back to the passed buffer * instead of allocating one. * - * @param[in] c Code point. - * @param[out] buf Return buffer. - * @param[in] enc Target encoding scheme. - * @post `c` is encoded according to `enc`, then written to `buf`. + * @param[in] c Code point. + * @param[out] buf Return buffer. + * @param[in] enc Target encoding scheme. + * @retval <= 0 `c` is invalid in `enc`. + * @return otherwise Number of bytes written to `buf`. + * @post `c` is encoded according to `enc`, then written to `buf`. * * @internal * diff --git a/include/ruby/internal/encoding/string.h b/include/ruby/internal/encoding/string.h index 6ed7ca1c90..2cfa91c01e 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 @@ -307,13 +307,13 @@ RBIMPL_ATTR_NONNULL(()) /** * Looks for the passed string in the passed buffer. * - * @param[in] x Buffer that potentially includes `y`. + * @param[in] x Query string. * @param[in] m Number of bytes of `x`. - * @param[in] y Query string. + * @param[in] y Buffer that potentially includes `x`. * @param[in] n Number of bytes of `y`. * @param[in] enc Encoding of both `x` and `y`. * @retval -1 Not found. - * @retval otherwise Found index in `x`. + * @retval otherwise Found index in `y`. * @note This API can match at a non-character-boundary. */ long rb_memsearch(const void *x, long m, const void *y, long n, rb_encoding *enc); diff --git a/include/ruby/internal/encoding/transcode.h b/include/ruby/internal/encoding/transcode.h index 60c96a41c9..7f26d2eae9 100644 --- a/include/ruby/internal/encoding/transcode.h +++ b/include/ruby/internal/encoding/transcode.h @@ -476,16 +476,16 @@ enum ruby_econv_flag_type { RUBY_ECONV_UNDEF_HEX_CHARREF = 0x00000030, /** Decorators are there. */ - RUBY_ECONV_DECORATOR_MASK = 0x0000ff00, + RUBY_ECONV_DECORATOR_MASK = 0x0001ff00, /** Newline converters are there. */ - RUBY_ECONV_NEWLINE_DECORATOR_MASK = 0x00003f00, + RUBY_ECONV_NEWLINE_DECORATOR_MASK = 0x00007f00, /** (Unclear; seems unused). */ RUBY_ECONV_NEWLINE_DECORATOR_READ_MASK = 0x00000f00, /** (Unclear; seems unused). */ - RUBY_ECONV_NEWLINE_DECORATOR_WRITE_MASK = 0x00003000, + RUBY_ECONV_NEWLINE_DECORATOR_WRITE_MASK = 0x00007000, /** Universal newline mode. */ RUBY_ECONV_UNIVERSAL_NEWLINE_DECORATOR = 0x00000100, @@ -496,11 +496,14 @@ enum ruby_econv_flag_type { /** CRLF to CR conversion shall happen. */ RUBY_ECONV_CR_NEWLINE_DECORATOR = 0x00002000, + /** CRLF to LF conversion shall happen. */ + RUBY_ECONV_LF_NEWLINE_DECORATOR = 0x00004000, + /** Texts shall be XML-escaped. */ - RUBY_ECONV_XML_TEXT_DECORATOR = 0x00004000, + RUBY_ECONV_XML_TEXT_DECORATOR = 0x00008000, /** Texts shall be AttrValue escaped */ - RUBY_ECONV_XML_ATTR_CONTENT_DECORATOR = 0x00008000, + RUBY_ECONV_XML_ATTR_CONTENT_DECORATOR = 0x00010000, /** (Unclear; seems unused). */ RUBY_ECONV_STATEFUL_DECORATOR_MASK = 0x00f00000, @@ -529,6 +532,7 @@ enum ruby_econv_flag_type { #define ECONV_UNIVERSAL_NEWLINE_DECORATOR RUBY_ECONV_UNIVERSAL_NEWLINE_DECORATOR /**< @old{RUBY_ECONV_UNIVERSAL_NEWLINE_DECORATOR} */ #define ECONV_CRLF_NEWLINE_DECORATOR RUBY_ECONV_CRLF_NEWLINE_DECORATOR /**< @old{RUBY_ECONV_CRLF_NEWLINE_DECORATOR} */ #define ECONV_CR_NEWLINE_DECORATOR RUBY_ECONV_CR_NEWLINE_DECORATOR /**< @old{RUBY_ECONV_CR_NEWLINE_DECORATOR} */ +#define ECONV_LF_NEWLINE_DECORATOR RUBY_ECONV_LF_NEWLINE_DECORATOR /**< @old{RUBY_ECONV_LF_NEWLINE_DECORATOR} */ #define ECONV_XML_TEXT_DECORATOR RUBY_ECONV_XML_TEXT_DECORATOR /**< @old{RUBY_ECONV_XML_TEXT_DECORATOR} */ #define ECONV_XML_ATTR_CONTENT_DECORATOR RUBY_ECONV_XML_ATTR_CONTENT_DECORATOR /**< @old{RUBY_ECONV_XML_ATTR_CONTENT_DECORATOR} */ #define ECONV_STATEFUL_DECORATOR_MASK RUBY_ECONV_STATEFUL_DECORATOR_MASK /**< @old{RUBY_ECONV_STATEFUL_DECORATOR_MASK} */ @@ -543,10 +547,10 @@ enum ruby_econv_flag_type { */ /** Indicates the input is a part of much larger one. */ - RUBY_ECONV_PARTIAL_INPUT = 0x00010000, + RUBY_ECONV_PARTIAL_INPUT = 0x00020000, /** Instructs the converter to stop after output. */ - RUBY_ECONV_AFTER_OUTPUT = 0x00020000, + RUBY_ECONV_AFTER_OUTPUT = 0x00040000, #define ECONV_PARTIAL_INPUT RUBY_ECONV_PARTIAL_INPUT /**< @old{RUBY_ECONV_PARTIAL_INPUT} */ #define ECONV_AFTER_OUTPUT RUBY_ECONV_AFTER_OUTPUT /**< @old{RUBY_ECONV_AFTER_OUTPUT} */ diff --git a/include/ruby/internal/error.h b/include/ruby/internal/error.h index 49e2276cb9..5bf82bfe7d 100644 --- a/include/ruby/internal/error.h +++ b/include/ruby/internal/error.h @@ -50,7 +50,23 @@ typedef enum { /** Warning is for experimental features. */ RB_WARN_CATEGORY_EXPERIMENTAL, - RB_WARN_CATEGORY_ALL_BITS = 0x6 /* no RB_WARN_CATEGORY_NONE bit */ + /** Warning is for performance issues (not enabled by -w). */ + RB_WARN_CATEGORY_PERFORMANCE, + + /** Warning is for checking unused block strictly */ + RB_WARN_CATEGORY_STRICT_UNUSED_BLOCK, + + RB_WARN_CATEGORY_DEFAULT_BITS = ( + (1U << RB_WARN_CATEGORY_DEPRECATED) | + (1U << RB_WARN_CATEGORY_EXPERIMENTAL) | + 0), + + RB_WARN_CATEGORY_ALL_BITS = ( + (1U << RB_WARN_CATEGORY_DEPRECATED) | + (1U << RB_WARN_CATEGORY_EXPERIMENTAL) | + (1U << RB_WARN_CATEGORY_PERFORMANCE) | + (1U << RB_WARN_CATEGORY_STRICT_UNUSED_BLOCK) | + 0) } rb_warning_category_t; /** for rb_readwrite_sys_fail first argument */ @@ -405,11 +421,12 @@ void rb_readwrite_syserr_fail(enum rb_io_wait_readwrite waiting, int err, const RBIMPL_ATTR_COLD() RBIMPL_ATTR_NORETURN() /** + * @private + * * Fails with the given object's type incompatibility to the type. * - * It seems this function is visible from extension libraries only because - * RTYPEDDATA_TYPE() uses it on RUBY_DEBUG. So you can basically ignore it; - * use some other fine-grained method instead. + * This is an implementation detail of Check_Type. People don't use it + * directly. * * @param[in] self The object in question. * @param[in] t Expected type of the object. @@ -469,7 +486,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) /** @@ -484,7 +501,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 @@ -509,7 +527,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(). @@ -522,19 +540,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, ...); @@ -543,8 +562,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(). @@ -554,8 +572,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/eval.h b/include/ruby/internal/eval.h index 34a53849da..23aa1d9580 100644 --- a/include/ruby/internal/eval.h +++ b/include/ruby/internal/eval.h @@ -28,10 +28,12 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() RBIMPL_ATTR_NONNULL(()) /** - * Evaluates the given string in an isolated binding. + * Evaluates the given string. * - * Here "isolated" means that the binding does not inherit any other - * bindings. This behaves same as the binding for required libraries. + * In case it is called from within a C-backended method, the evaluation is + * done under the current binding. However there can be no method. On such + * situation this function evaluates in an isolated binding, like `require` + * runs in a separate one. * * `__FILE__` will be `"(eval)"`, and `__LINE__` starts from 1 in the * evaluation. @@ -39,6 +41,31 @@ RBIMPL_ATTR_NONNULL(()) * @param[in] str Ruby code to evaluate. * @exception rb_eException Raises an exception on error. * @return The evaluated result. + * + * @internal + * + * @shyouhei's old tale about the birth and growth of this function: + * + * At the beginning, there was no rb_eval_string(). @shyouhei heard that + * @shugo, author of Apache httpd's mod_ruby module, requested @matz for this + * API. He wanted a way so that mod_ruby can evaluate ruby scripts one by one, + * separately, in each different contexts. So this function was made. It was + * designed to be a global interpreter entry point like ruby_run_node(). + * + * The way it is implemented however allows extension libraries (not just + * programs like Apache httpd) to call this function. Because its name says + * nothing about the initial design, people started to think of it as an + * orthodox way to call ruby level `eval` method from their extension + * libraries. Even our `extension.rdoc` has had a description of this function + * basically according to this understanding. + * + * The old (mod_ruby like) usage still works. But over time, usages of this + * function from extension libraries got popular, while mod_ruby faded out; is + * no longer maintained now. Devs decided to actively support both. This + * function now auto-detects how it is called, and switches how it works + * depending on it. + * + * @see https://bugs.ruby-lang.org/issues/18780 */ VALUE rb_eval_string(const char *str); @@ -128,7 +155,8 @@ VALUE rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv); * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eNoMethodError No such method. * @exception rb_eException Any exceptions happen inside. * @return What the method evaluates to. @@ -162,7 +190,8 @@ VALUE rb_funcallv_public(VALUE recv, ID mid, int argc, const VALUE *argv); * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eNoMethodError No such method. * @exception rb_eNoMethodError The method is private or protected. * @exception rb_eException Any exceptions happen inside. @@ -234,7 +263,8 @@ VALUE rb_funcall_passing_block(VALUE recv, ID mid, int argc, const VALUE *argv); * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eNoMethodError No such method. * @exception rb_eNoMethodError The method is private or protected. * @exception rb_eException Any exceptions happen inside. @@ -280,7 +310,8 @@ VALUE rb_funcall_with_block(VALUE recv, ID mid, int argc, const VALUE *argv, VAL * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eNoMethodError No such method. * @exception rb_eNoMethodError The method is private or protected. * @exception rb_eException Any exceptions happen inside. @@ -308,7 +339,8 @@ VALUE rb_call_super(int argc, const VALUE *argv); * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eNoMethodError No super method are there. * @exception rb_eException Any exceptions happen inside. * @return What the super method evaluates to. 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 c51bd2e9d9..2afb3f1fa3 100644 --- a/include/ruby/internal/fl_type.h +++ b/include/ruby/internal/fl_type.h @@ -57,13 +57,10 @@ #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} */ #define FL_UNTRUSTED RBIMPL_CAST((VALUE)RUBY_FL_UNTRUSTED) /**< @old{RUBY_FL_UNTRUSTED} */ -#define FL_SEEN_OBJ_ID RBIMPL_CAST((VALUE)RUBY_FL_SEEN_OBJ_ID) /**< @old{RUBY_FL_SEEN_OBJ_ID} */ #define FL_EXIVAR RBIMPL_CAST((VALUE)RUBY_FL_EXIVAR) /**< @old{RUBY_FL_EXIVAR} */ #define FL_FREEZE RBIMPL_CAST((VALUE)RUBY_FL_FREEZE) /**< @old{RUBY_FL_FREEZE} */ @@ -111,13 +108,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 +197,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 +213,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 meaning is type dependent, currently only used by T_DATA. * * @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_USERPRIV0 = (1<<6), /** * This flag has something to do with finalisers. A ruby object can have @@ -271,19 +237,19 @@ ruby_fl_type { RUBY_FL_FINALIZE = (1<<7), /** - * @deprecated This flag once was a thing back in the old days, but makes - * no sense any longer today. Exists here for backwards + * @deprecated This flag was an implementation detail that should never have + * no been exposed. Exists here for backwards * compatibility only. You can safely forget about it. */ - RUBY_FL_TAINT + RUBY_FL_EXIVAR #if defined(RBIMPL_HAVE_ENUM_ATTRIBUTE) - RBIMPL_ATTR_DEPRECATED(("taintedness turned out to be a wrong idea.")) + RBIMPL_ATTR_DEPRECATED(("FL_EXIVAR is an outdated implementation detail, it should not be used.")) #elif defined(_MSC_VER) -# pragma deprecated(RUBY_FL_TAINT) +# pragma deprecated(RUBY_FL_EXIVAR) #endif - = (1<<8), + = 0, /** * This flag has something to do with Ractor. Multiple Ractors run without @@ -297,52 +263,19 @@ ruby_fl_type { */ RUBY_FL_SHAREABLE = (1<<8), - /** - * @deprecated This flag once was a thing back in the old days, but makes - * no sense any longer today. Exists here for backwards - * compatibility only. You can safely forget about it. - */ - RUBY_FL_UNTRUSTED - -#if defined(RBIMPL_HAVE_ENUM_ATTRIBUTE) - RBIMPL_ATTR_DEPRECATED(("trustedness turned out to be a wrong idea.")) -#elif defined(_MSC_VER) -# pragma deprecated(RUBY_FL_UNTRUSTED) -#endif - - = (1<<8), - - /** - * This flag has something to do with object IDs. Unlike in the old days, - * an object's object ID (that a user can query using `Object#object_id`) - * is no longer its physical address represented using Ruby level integers. - * It is now a monotonic-increasing integer unrelated to the underlying - * memory arrangement. Object IDs are assigned when necessary; objects are - * born without one, and will eventually have such property when queried. - * The interpreter has to manage which one is which. This is the flag that - * helps the management. Objects with this flag set are the ones with - * object IDs assigned. - * - * @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_SEEN_OBJ_ID = (1<<9), + /** + * This object weakly refers to other objects. + * + * @internal + */ + RUBY_FL_WEAK_REFERENCE = (1<<9), - /** - * This flag has something to do with instance variables. 3rd parties need - * not know, but there are several ways to store an object's instance - * variables. Objects with this flag use so-called "generic" backend - * storage. This distinction is purely an implementation detail. People - * need not be aware of this working behind-the-scene. - * - * @internal - * - * As of writing everything except ::RObject and RModule use this scheme. - */ - RUBY_FL_EXIVAR = (1<<10), + /** + * This flag is no longer in use + * + * @internal + */ + RUBY_FL_UNUSED10 = (1<<10), /** * This flag has something to do with data immutability. When this flag is @@ -402,7 +335,7 @@ ruby_fl_type { * 3rd parties. It must be an implementation detail that they should never * know. Might better be hidden. */ - RUBY_ELTS_SHARED = RUBY_FL_USER2, + RUBY_ELTS_SHARED = RUBY_FL_USER0, /** * This flag has something to do with an object's class. There are kind of @@ -427,24 +360,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, -}; - -enum { - /** - * @deprecated This flag once was a thing back in the old days, but makes - * no sense any longer today. Exists here for backwards - * compatibility only. You can safely forget about it. - */ - RUBY_FL_DUPPED - -#if defined(RBIMPL_HAVE_ENUM_ATTRIBUTE) - RBIMPL_ATTR_DEPRECATED(("It seems there is no actual usage of this enum.")) -#elif defined(_MSC_VER) -# pragma deprecated(RUBY_FL_DUPPED) -#endif - - = (int)RUBY_T_MASK | (int)RUBY_FL_EXIVAR + RUBY_FL_SINGLETON = RUBY_FL_USER1, }; #undef RBIMPL_HAVE_ENUM_ATTRIBUTE @@ -478,10 +394,8 @@ RB_FL_ABLE(VALUE obj) if (RB_SPECIAL_CONST_P(obj)) { return false; } - else if (RB_TYPE_P(obj, RUBY_T_NODE)) { - return false; - } else { + RBIMPL_ASSERT_OR_ASSUME(!RB_TYPE_P(obj, RUBY_T_NODE)); return true; } } @@ -489,7 +403,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 +451,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 +485,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 +521,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 +541,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 +581,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 +601,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 +636,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 +656,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. @@ -775,121 +689,8 @@ RB_FL_REVERSE(VALUE obj, VALUE flags) RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() -RBIMPL_ATTR_DEPRECATED(("taintedness turned out to be a wrong idea.")) -/** - * @deprecated This function 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. - * - * @param[in] obj Object in question. - * @return false always. - */ -static inline bool -RB_OBJ_TAINTABLE(VALUE obj) -{ - return false; -} - -RBIMPL_ATTR_PURE_UNLESS_DEBUG() -RBIMPL_ATTR_ARTIFICIAL() -RBIMPL_ATTR_DEPRECATED(("taintedness turned out to be a wrong idea.")) -/** - * @deprecated This function 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. - * - * @param[in] obj Object in question. - * @return false always. - */ -static inline VALUE -RB_OBJ_TAINTED_RAW(VALUE obj) -{ - return false; -} - -RBIMPL_ATTR_PURE_UNLESS_DEBUG() -RBIMPL_ATTR_ARTIFICIAL() -RBIMPL_ATTR_DEPRECATED(("taintedness turned out to be a wrong idea.")) -/** - * @deprecated This function 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. - * - * @param[in] obj Object in question. - * @return false always. - */ -static inline bool -RB_OBJ_TAINTED(VALUE obj) -{ - return false; -} - -RBIMPL_ATTR_ARTIFICIAL() -RBIMPL_ATTR_DEPRECATED(("taintedness turned out to be a wrong idea.")) -/** - * @deprecated This function 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. - * - * @param[in] obj Object in question. - */ -static inline void -RB_OBJ_TAINT_RAW(VALUE obj) -{ - return; -} - -RBIMPL_ATTR_ARTIFICIAL() -RBIMPL_ATTR_DEPRECATED(("taintedness turned out to be a wrong idea.")) -/** - * @deprecated This function 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. - * - * @param[in] obj Object in question. - */ -static inline void -RB_OBJ_TAINT(VALUE obj) -{ - return; -} - -RBIMPL_ATTR_ARTIFICIAL() -RBIMPL_ATTR_DEPRECATED(("taintedness turned out to be a wrong idea.")) -/** - * @deprecated This function 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. - * - * @param[in] dst Victim object. - * @param[in] src Infectant object. - */ -static inline void -RB_OBJ_INFECT_RAW(VALUE dst, VALUE src) -{ - return; -} - -RBIMPL_ATTR_ARTIFICIAL() -RBIMPL_ATTR_DEPRECATED(("taintedness turned out to be a wrong idea.")) -/** - * @deprecated This function 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. - * - * @param[in] dst Victim object. - * @param[in] src Infectant object. - */ -static inline void -RB_OBJ_INFECT(VALUE dst, VALUE src) -{ - return; -} - -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. @@ -928,34 +729,29 @@ RB_OBJ_FROZEN(VALUE obj) } } -RBIMPL_ATTR_ARTIFICIAL() +RUBY_SYMBOL_EXPORT_BEGIN /** - * This is an implenentation detail of RB_OBJ_FREEZE(). 3rd parties need not - * use this. Just always use RB_OBJ_FREEZE(). + * Prevents further modifications to the given object. ::rb_eFrozenError shall + * be raised if modification is attempted. * - * @param[out] obj Object in question. + * @param[out] x Object in question. + * @exception rb_eNoMemError Failed to allocate memory for the frozen + * representation of the object. */ -static inline void -RB_OBJ_FREEZE_RAW(VALUE obj) -{ - RB_FL_SET_RAW(obj, RUBY_FL_FREEZE); -} +void rb_obj_freeze_inline(VALUE obj); +RUBY_SYMBOL_EXPORT_END +RBIMPL_ATTR_ARTIFICIAL() /** - * Prevents further modifications to the given object. ::rb_eFrozenError shall - * be raised if modification is attempted. + * This is an implementation detail of RB_OBJ_FREEZE(). 3rd parties need not + * use this. Just always use RB_OBJ_FREEZE(). * - * @param[out] x Object in question. + * @param[out] obj Object in question. */ static inline void -rb_obj_freeze_inline(VALUE x) +RB_OBJ_FREEZE_RAW(VALUE obj) { - if (RB_FL_ABLE(x)) { - RB_OBJ_FREEZE_RAW(x); - if (RBASIC_CLASS(x) && !(RBASIC(x)->flags & RUBY_FL_SINGLETON)) { - rb_freeze_singleton_class(x); - } - } + rb_obj_freeze_inline(obj); } #endif /* RBIMPL_FL_TYPE_H */ diff --git a/include/ruby/internal/gc.h b/include/ruby/internal/gc.h index 66fc14e511..21c2b670b3 100644 --- a/include/ruby/internal/gc.h +++ b/include/ruby/internal/gc.h @@ -20,16 +20,379 @@ * extension libraries. They could be written in C++98. * @brief Registering values to the GC. */ +#include "ruby/internal/config.h" + +#ifdef STDC_HEADERS +# include <stddef.h> /* size_t */ +#endif + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> /* ssize_t */ +#endif + +#include "ruby/assert.h" +#include "ruby/internal/attr/cold.h" +#include "ruby/internal/attr/nonnull.h" +#include "ruby/internal/attr/noreturn.h" +#include "ruby/internal/attr/artificial.h" +#include "ruby/internal/attr/maybe_unused.h" +#include "ruby/internal/attr/pure.h" #include "ruby/internal/dllexport.h" +#include "ruby/internal/special_consts.h" +#include "ruby/internal/stdbool.h" #include "ruby/internal/value.h" RBIMPL_SYMBOL_EXPORT_BEGIN() +#define RUBY_REF_EDGE(s, p) offsetof(s, p) +#define RUBY_REFS_LIST_PTR(l) (RUBY_DATA_FUNC)(uintptr_t)(l) +#define RUBY_REF_END SIZE_MAX +#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 */ + +RBIMPL_ATTR_COLD() +RBIMPL_ATTR_NORETURN() +/** + * Triggers out-of-memory error. If possible it raises ::rb_eNoMemError. But + * because we are running out of memory that is not always doable. This + * function tries hard to show something, but ultimately can die silently. + * + * @exception rb_eNoMemError Raises it if possible. + */ +void rb_memerror(void); + +RBIMPL_ATTR_PURE() +/** + * Queries if the GC is busy. + * + * @retval 0 It isn't. + * @retval 1 It is. + */ +int rb_during_gc(void); + +RBIMPL_ATTR_NONNULL((1)) +/** + * Marks objects between the two pointers. This is one of the GC utility + * functions that you can call when you design your own + * ::rb_data_type_struct::dmark. + * + * @pre Continuous memory region from `start` to `end` shall be fully + * addressable. + * @param[out] start Pointer to an array of objects. + * @param[out] end Pointer that terminates the array of objects. + * @post Objects from `start` (included) to `end` (excluded) are marked. + * + * @internal + * + * `end` can be NULL... But that just results in no-op. + */ +void rb_gc_mark_locations(const VALUE *start, const VALUE *end); + +/** + * Identical to rb_mark_hash(), except it marks only values of the table and + * leave their associated keys unmarked. This is one of the GC utility + * functions that you can call when you design your own + * ::rb_data_type_struct::dmark. + * + * @warning Of course it can break GC. Leave it unused if unsure. + * @param[in] tbl A table to mark. + * @post Values stored in `tbl` are marked. + */ +void rb_mark_tbl(struct st_table *tbl); + +/** + * Identical to rb_mark_tbl(), except it marks objects using + * rb_gc_mark_movable(). This is one of the GC utility functions that you can + * call when you design your own ::rb_data_type_struct::dmark. + * + * @warning Of course it can break GC. Leave it unused if unsure. + * @param[in] tbl A table to mark. + * @post Values stored in `tbl` are marked. + */ +void rb_mark_tbl_no_pin(struct st_table *tbl); + +/** + * Identical to rb_mark_hash(), except it marks only keys of the table and + * leave their associated values unmarked. This is one of the GC utility + * functions that you can call when you design your own + * ::rb_data_type_struct::dmark. + * + * @warning Of course it can break GC. Leave it unused if unsure. + * @param[in] tbl A table to mark. + * @post Keys stored in `tbl` are marked. + */ +void rb_mark_set(struct st_table *tbl); + /** - * Inform the garbage collector that `valptr` points to a live Ruby object that - * should not be moved. Note that extensions should use this API on global - * constants instead of assuming constants defined in Ruby are always alive. - * Ruby code can remove global constants. + * Marks keys and values associated inside of the given table. This is one of + * the GC utility functions that you can call when you design your own + * ::rb_data_type_struct::dmark. + * + * @param[in] tbl A table to mark. + * @post Objects stored in `tbl` are marked. + */ +void rb_mark_hash(struct st_table *tbl); + +/** + * Updates references inside of tables. After you marked values using + * rb_mark_tbl_no_pin(), the objects inside of the table could of course be + * moved. This function is to fixup those references. You can call this from + * your ::rb_data_type_struct::dcompact. + * + * @param[out] ptr A table that potentially includes moved references. + * @post Moved references, if any, are corrected. + */ +void rb_gc_update_tbl_refs(st_table *ptr); + +/** + * Identical to rb_gc_mark(), except it allows the passed value be a + * non-object. For instance pointers to different type of memory regions are + * allowed here. Such values are silently ignored. This is one of the GC + * utility functions that you can call when you design your own + * ::rb_data_type_struct::dmark. + * + * @param[out] obj A possible object. + * @post `obj` is marked, if possible. + */ +void rb_gc_mark_maybe(VALUE obj); + +/** + * Marks an object. This is one of the GC utility functions that you can call + * when you design your own ::rb_data_type_struct::dmark. + * + * @param[out] obj Arbitrary Ruby object. + * @post `obj` is marked. + */ +void rb_gc_mark(VALUE obj); + +/** + * Maybe this is the only function provided for C extensions to control the + * pinning of objects, so let us describe it in detail. These days Ruby's GC + * is copying. As far as an object's physical address is guaranteed unused, it + * can move around the object space. Our GC engine rearranges these objects + * after it reclaims unreachable objects from our object space, so that the + * space is compact (improves memory locality). This is called the + * "compaction" phase, and works well most of the time... as far as there are + * no C extensions. C extensions complicate the scenario because Ruby core + * cannot detect any use of the physical address of an object inside of C + * functions. In order to prevent memory corruptions, objects observable from + * C extensions are "pinned"; they stick to where they are born until they die, + * just in case any C extensions touch their raw pointers. This variant of + * scheme is called "Mostly-Copying" garbage collector. Authors of C + * extensions, however, can extremely carefully write them to become + * compaction-aware. To do so avoid referring to a Ruby object from inside of + * your struct in the first place. But if that is not possible, use this + * function from your ::rb_data_type_struct::dmark then. This way objects + * marked using it are considered movable. If you chose this way you have to + * manually fix up locations of such moved pointers using rb_gc_location(). + * + * @see Bartlett, Joel F., "Compacting Garbage Collection with Ambiguous + * Roots", ACM SIGPLAN Lisp Pointers Volume 1 Issue 6 pp. 3-12, + * April-May-June, 1988. https://doi.org/10.1145/1317224.1317225 + * + * @param[in] obj Object that is movable. + * @post Values stored in `tbl` are marked. + */ +void rb_gc_mark_movable(VALUE obj); + +/** + * Finds a new "location" of an object. An object can be moved on compaction. + * This function projects its new abode, or just returns the passed object if + * not moved. This is one of the GC utility functions that you can call when + * you design your own ::rb_data_type_struct::dcompact. + * + * @param[in] obj An object, possibly already moved to somewhere else. + * @return An object, which holds the current contents of former `obj`. + */ +VALUE rb_gc_location(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 + * procedures kicked by this API is called a "full" GC. + * + * - It immediately scans the entire object space to sort the dead. + * - It immediately reclaims any single dead bodies to reuse later. + * + * It is worth noting that the procedures above do not include evaluations of + * finalisers. They run later. + * + * @internal + * + * Finalisers are deferred until we can handle interrupts. See + * `rb_postponed_job_flush` in vm_trace.c. + * + * Of course there are GC that are not "full". For instance this one and the + * GC which runs when we are running out of memory are different. See + * `gc_profile_record_flag` defined in gc.c for the kinds of GC. + * + * In spite of the name this is not what everything that a GC can trigger. As + * of writing it seems this function does not trigger compaction. But this + * might change in future. + */ +void rb_gc(void); + +/** + * Copy&paste an object's finaliser to another. This is one of the GC utility + * functions that you can call when you design your own `initialize_copy`, + * `initialize_dup`, `initialize_clone`. + * + * @param[out] dst Destination object. + * @param[in] src Source object. + * @post `dst` and `src` share the same finaliser. + * + * @internal + * + * But isn't it easier for you to call super, and let `Object#initialize_copy` + * call this function instead? + */ +void rb_gc_copy_finalizer(VALUE dst, VALUE src); + +/** + * (Re-) enables GC. This makes sense only after you called rb_gc_disable(). + * + * @retval RUBY_Qtrue GC was disabled before. + * @retval RUBY_Qfalse GC was enabled before. + * @post GC is enabled. + * + * @internal + * + * This is one of such exceptional functions that does not raise both Ruby + * exceptions and C++ exceptions. + */ +VALUE rb_gc_enable(void); + +/** + * Disables GC. This prevents automatic GC runs when the process is running + * out of memory. Such situations shall result in rb_memerror(). However this + * does not prevent users from manually invoking rb_gc(). That should work. + * People abused this by disabling GC at the beginning of an event loop, + * process events without GC overheads, then manually force reclaiming garbage + * at the bottom of the loop. However because our GC is now much smarter than + * just calling rb_gc(), this technique is proven to be sub-optimal these days. + * It is believed that there is currently practically no needs of this + * function. + * + * @retval RUBY_Qtrue GC was disabled before. + * @retval RUBY_Qfalse GC was enabled before. + * @post GC is disabled. + */ +VALUE rb_gc_disable(void); + +/** + * Identical to rb_gc(), except the return value. + * + * @return Always returns ::RUBY_Qnil. + */ +VALUE rb_gc_start(void); + +/** + * Assigns a finaliser for an object. Each objects can have objects (typically + * blocks) that run immediately after that object dies. They are called + * finalisers of an object. This function associates a finaliser object with a + * target object. + * + * @note Note that finalisers run _after_ the object they finalise dies. You + * cannot for instance call its methods. + * @note If your finaliser references the object it finalises that object + * loses any chance to become a garbage; effectively leaks memory until + * the end of the process. + * + * @param[in] obj Target to finalise. + * @param[in] block Something `call`able. + * @exception rb_eRuntimeError Somehow `obj` cannot have finalisers. + * @exception rb_eFrozenError `obj` is frozen. + * @exception rb_eArgError `block` doesn't respond to `call`. + * @return The passed `block`. + * @post `block` runs after `obj` dies. + */ +VALUE rb_define_finalizer(VALUE obj, VALUE block); + +/** + * Modifies the object so that it has no finalisers at all. This function is + * mainly provided for symmetry. No practical usages can be thought of. + * + * @param[out] obj Object to clear its finalisers. + * @exception rb_eFrozenError `obj` is frozen. + * @return The passed `obj`. + * @post `obj` has no finalisers. + * @note There is no way to undefine a specific part of many finalisers + * that `obj` could have. All you can do is to clear them all. + */ +VALUE rb_undefine_finalizer(VALUE obj); + +/** + * Identical to rb_gc_stat(), with "count" parameter. + * + * @return Lifetime total number of runs of GC. + */ +size_t rb_gc_count(void); + +/** + * Obtains various GC related profiles. The parameter can be either a Symbol + * or a Hash. If a Hash is passed, it is filled with everything currently + * available. If a Symbol is passed just that portion is returned. + * + * Possible variations of keys you can pass here change from version to + * version. You can get the list of known keys by passing an empty hash and + * let it be filled. + * + * @param[in,out] key_or_buf A Symbol, or a Hash. + * @exception rb_eTypeError Neither Symbol nor Hash. + * @exception rb_eFrozenError Frozen hash is passed. + * @return In case a Hash is passed it returns 0. Otherwise the + * profile value associated with the given key is returned. + * @post In case a Hash is passed it is filled with values. + */ +size_t rb_gc_stat(VALUE key_or_buf); + +/** + * Obtains various info regarding the most recent GC run. This includes for + * instance the reason of the GC. The parameter can be either a Symbol or a + * Hash. If a Hash is passed, it is filled with everything currently + * available. If a Symbol is passed just that portion is returned. + * + * Possible variations of keys you can pass here change from version to + * version. You can get the list of known keys by passing an empty hash and + * let it be filled. + * + * @param[in,out] key_or_buf A Symbol, or a Hash. + * @exception rb_eTypeError Neither Symbol nor Hash. + * @exception rb_eFrozenError Frozen hash is passed. + * @return In case a Hash is passed it returns that hash. Otherwise + * the profile value associated with the given key is returned. + * @post In case a Hash is passed it is filled with values. + */ +VALUE rb_gc_latest_gc_info(VALUE key_or_buf); + +/** + * Informs that there are external memory usages. Our GC runs when we are + * running out of memory. The amount of memory, however, can increase/decrease + * behind-the-scene. For instance DLLs can allocate memories using `mmap(2)` + * etc, which are opaque to us. Registering such external allocations using + * this function enables proper detection of how much memories an object used + * as a whole. That will trigger GCs more often than it would otherwise. You + * can also pass negative numbers here, to indicate that such external + * allocations are gone. + * + * @param[in] diff Amount of memory increased(+)/decreased(-). + */ +void rb_gc_adjust_memory_usage(ssize_t diff); + +/** + * Inform the garbage collector that the global or static variable pointed by + * `valptr` stores a live Ruby object that should not be moved. Note that + * extensions should use this API on global constants instead of assuming + * constants defined in Ruby are always alive. Ruby code can remove global + * constants. + * + * Because this registration itself has a possibility to trigger a GC, this + * function must be called before any GC-able objects is assigned to the + * address pointed by `valptr`. */ void rb_gc_register_address(VALUE *valptr); @@ -54,4 +417,410 @@ void rb_gc_register_mark_object(VALUE object); RBIMPL_SYMBOL_EXPORT_END() +/** + * @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. + */ +#undef USE_RGENGC +#define USE_RGENGC 1 + +/** + * @deprecated This macro seems broken. Setting this to anything other than + * zero just doesn't compile. We need to KonMari. + */ +#ifndef USE_RGENGC_LOGGING_WB_UNPROTECT +# define USE_RGENGC_LOGGING_WB_UNPROTECT 0 +#endif + +/** + * @private + * + * This is a compile-time flag to enable/disable write barrier for + * struct ::RArray. It has to be set at the time ruby itself compiles. Makes + * no sense for 3rd parties. + */ +#ifndef RGENGC_WB_PROTECTED_ARRAY +# define RGENGC_WB_PROTECTED_ARRAY 1 +#endif + +/** + * @private + * + * This is a compile-time flag to enable/disable write barrier for + * struct ::RHash. It has to be set at the time ruby itself compiles. Makes + * no sense for 3rd parties. + */ +#ifndef RGENGC_WB_PROTECTED_HASH +# define RGENGC_WB_PROTECTED_HASH 1 +#endif + +/** + * @private + * + * This is a compile-time flag to enable/disable write barrier for + * struct ::RStruct. It has to be set at the time ruby itself compiles. Makes + * no sense for 3rd parties. + */ +#ifndef RGENGC_WB_PROTECTED_STRUCT +# define RGENGC_WB_PROTECTED_STRUCT 1 +#endif + +/** + * @private + * + * This is a compile-time flag to enable/disable write barrier for + * struct ::RString. It has to be set at the time ruby itself compiles. Makes + * no sense for 3rd parties. + */ +#ifndef RGENGC_WB_PROTECTED_STRING +# define RGENGC_WB_PROTECTED_STRING 1 +#endif + +/** + * @private + * + * This is a compile-time flag to enable/disable write barrier for + * struct ::RObject. It has to be set at the time ruby itself compiles. Makes + * no sense for 3rd parties. + */ +#ifndef RGENGC_WB_PROTECTED_OBJECT +# define RGENGC_WB_PROTECTED_OBJECT 1 +#endif + +/** + * @private + * + * This is a compile-time flag to enable/disable write barrier for + * struct ::RRegexp. It has to be set at the time ruby itself compiles. Makes + * no sense for 3rd parties. + */ +#ifndef RGENGC_WB_PROTECTED_REGEXP +# define RGENGC_WB_PROTECTED_REGEXP 1 +#endif + +/** + * @private + * + * This is a compile-time flag to enable/disable write barrier for + * struct ::RMatch. It has to be set at the time ruby itself compiles. Makes + * no sense for 3rd parties. + */ +#ifndef RGENGC_WB_PROTECTED_MATCH +# define RGENGC_WB_PROTECTED_MATCH 1 +#endif + +/** + * @private + * + * This is a compile-time flag to enable/disable write barrier for + * struct ::RClass. It has to be set at the time ruby itself compiles. Makes + * no sense for 3rd parties. + */ +#ifndef RGENGC_WB_PROTECTED_CLASS +# define RGENGC_WB_PROTECTED_CLASS 1 +#endif + +/** + * @private + * + * This is a compile-time flag to enable/disable write barrier for + * struct ::RFloat. It has to be set at the time ruby itself compiles. Makes + * no sense for 3rd parties. + */ +#ifndef RGENGC_WB_PROTECTED_FLOAT +# define RGENGC_WB_PROTECTED_FLOAT 1 +#endif + +/** + * @private + * + * This is a compile-time flag to enable/disable write barrier for + * struct ::RComplex. It has to be set at the time ruby itself compiles. + * Makes no sense for 3rd parties. + */ +#ifndef RGENGC_WB_PROTECTED_COMPLEX +# define RGENGC_WB_PROTECTED_COMPLEX 1 +#endif + +/** + * @private + * + * This is a compile-time flag to enable/disable write barrier for + * struct ::RRational. It has to be set at the time ruby itself compiles. + * Makes no sense for 3rd parties. + */ +#ifndef RGENGC_WB_PROTECTED_RATIONAL +# define RGENGC_WB_PROTECTED_RATIONAL 1 +#endif + +/** + * @private + * + * This is a compile-time flag to enable/disable write barrier for + * struct ::RBignum. It has to be set at the time ruby itself compiles. Makes + * no sense for 3rd parties. + */ +#ifndef RGENGC_WB_PROTECTED_BIGNUM +# define RGENGC_WB_PROTECTED_BIGNUM 1 +#endif + +/** + * @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. + * + * @internal + * + * @shyouhei doesn't think anybody uses this right now. + */ +#ifndef RGENGC_WB_PROTECTED_NODE_CREF +# define RGENGC_WB_PROTECTED_NODE_CREF 1 +#endif + +/** + * @defgroup rgengc Write barrier (WB) interfaces: + * + * @note The following core interfaces can be changed in the future. Please + * catch up if you want to insert WB into C-extensions correctly. + * + * @{ + */ + +/** + * Declaration of a "back" pointer. This is a write barrier for new reference + * from "old" generation to "young" generation. It writes `young` into + * `*slot`, which is a pointer inside of `old`. + * + * @param[in] old An old object. + * @param[in] slot A pointer inside of `old`. + * @param[out] young A young object. + */ +#define RB_OBJ_WRITE(old, slot, young) \ + RBIMPL_CAST(rb_obj_write((VALUE)(old), (VALUE *)(slot), (VALUE)(young), __FILE__, __LINE__)) + +/** + * Identical to #RB_OBJ_WRITE(), except it doesn't write any values, but only a + * WB declaration. `oldv` is replaced value with `b` (not used in current + * Ruby). + * + * @param[in] old An old object. + * @param[in] oldv An object previously stored inside of `old`. + * @param[out] young A young object. + */ +#define RB_OBJ_WRITTEN(old, oldv, young) \ + RBIMPL_CAST(rb_obj_written((VALUE)(old), (VALUE)(oldv), (VALUE)(young), __FILE__, __LINE__)) +/** @} */ + +#define OBJ_PROMOTED_RAW RB_OBJ_PROMOTED_RAW /**< @old{RB_OBJ_PROMOTED_RAW} */ +#define OBJ_PROMOTED RB_OBJ_PROMOTED /**< @old{RB_OBJ_PROMOTED} */ +#define OBJ_WB_UNPROTECT RB_OBJ_WB_UNPROTECT /**< @old{RB_OBJ_WB_UNPROTECT} */ + +/** + * Asserts that the passed object is not fenced by write barriers. Objects of + * such property do not contribute to generational GCs. They are scanned + * always. + * + * @param[out] x An object that would not be protected by the barrier. + */ +#define RB_OBJ_WB_UNPROTECT(x) rb_obj_wb_unprotect(x, __FILE__, __LINE__) + +/** + * Identical to #RB_OBJ_WB_UNPROTECT(), except it can also assert that the + * given object is of given type. + * + * @param[in] type One of `ARRAY`, `STRING`, etc. + * @param[out] obj An object of `type` that would not be protected. + * + * @internal + * + * @shyouhei doesn't understand why this has to be visible from extensions. + */ +#define RB_OBJ_WB_UNPROTECT_FOR(type, obj) \ + (RGENGC_WB_PROTECTED_##type ? OBJ_WB_UNPROTECT(obj) : obj) + +/** + * @private + * + * This is an implementation detail of rb_obj_wb_unprotect(). People don't use + * it directly. + */ +#define RGENGC_LOGGING_WB_UNPROTECT rb_gc_unprotect_logging + +/** @cond INTERNAL_MACRO */ +#define RB_OBJ_PROMOTED_RAW RB_OBJ_PROMOTED_RAW +#define RB_OBJ_PROMOTED RB_OBJ_PROMOTED +/** @endcond */ + +RBIMPL_SYMBOL_EXPORT_BEGIN() +/** + * This is the implementation of #RB_OBJ_WRITE(). People don't use it + * directly. + * + * @param[in] old An object that points to `young`. + * @param[out] young An object that is referenced from `old`. + */ +void rb_gc_writebarrier(VALUE old, VALUE young); + +/** + * This is the implementation of #RB_OBJ_WB_UNPROTECT(). People don't use it + * directly. + * + * @param[out] obj An object that does not participate in WB. + */ +void rb_gc_writebarrier_unprotect(VALUE obj); + +#if USE_RGENGC_LOGGING_WB_UNPROTECT +/** + * @private + * + * This is the implementation of #RGENGC_LOGGING_WB_UNPROTECT(). People + * don't use it directly. + * + * @param[in] objptr Don't know why this is a pointer to void but in + * reality this is a pointer to an object that is about + * to be un-protected. + * @param[in] filename Pass C's `__FILE__` here. + * @param[in] line Pass C's `__LINE__` here. + */ +void rb_gc_unprotect_logging(void *objptr, const char *filename, int line); +#endif + +RBIMPL_SYMBOL_EXPORT_END() + +RBIMPL_ATTR_PURE_UNLESS_DEBUG() +RBIMPL_ATTR_ARTIFICIAL() +/** + * This is the implementation of #RB_OBJ_PROMOTED(). People don't use it + * directly. + * + * @param[in] obj An object to query. + * @retval true The object is "promoted". + * @retval false The object is young. Have not experienced GC at all. + */ +static inline bool +RB_OBJ_PROMOTED_RAW(VALUE obj) +{ + RBIMPL_ASSERT_OR_ASSUME(RB_FL_ABLE(obj)); + return RB_FL_ANY_RAW(obj, RUBY_FL_PROMOTED); +} + +RBIMPL_ATTR_PURE_UNLESS_DEBUG() +RBIMPL_ATTR_ARTIFICIAL() +/** + * Tests if the object is "promoted" -- that is, whether the object experienced + * one or more GC marks. + * + * @param[in] obj An object to query. + * @retval true The object is "promoted". + * @retval false The object is young. Have not experienced GC at all. + * @note Hello, is anyone actively calling this function? @shyouhei have + * never seen any actual usages outside of the GC implementation + * itself. + */ +static inline bool +RB_OBJ_PROMOTED(VALUE obj) +{ + if (! RB_FL_ABLE(obj)) { + return false; + } + else { + return RB_OBJ_PROMOTED_RAW(obj); + } +} + +/** + * This is the implementation of #RB_OBJ_WB_UNPROTECT(). People don't use it + * directly. + * + * @param[out] x An object that does not participate in WB. + * @param[in] filename C's `__FILE__` of the caller function. + * @param[in] line C's `__LINE__` of the caller function. + * @return x + */ +static inline VALUE +rb_obj_wb_unprotect( + VALUE x, + RBIMPL_ATTR_MAYBE_UNUSED() + const char *filename, + RBIMPL_ATTR_MAYBE_UNUSED() + int line) +{ +#if USE_RGENGC_LOGGING_WB_UNPROTECT + RGENGC_LOGGING_WB_UNPROTECT(RBIMPL_CAST((void *)x), filename, line); +#endif + rb_gc_writebarrier_unprotect(x); + return x; +} + +/** + * @private + * + * This is the implementation of #RB_OBJ_WRITTEN(). People don't use it + * directly. + * + * @param[in] a An old object. + * @param[in] oldv An object previously stored inside of `old`. + * @param[out] b A young object. + * @param[in] filename C's `__FILE__` of the caller function. + * @param[in] line C's `__LINE__` of the caller function. + * @return a + */ +static inline VALUE +rb_obj_written( + VALUE a, + RBIMPL_ATTR_MAYBE_UNUSED() + VALUE oldv, + VALUE b, + RBIMPL_ATTR_MAYBE_UNUSED() + const char *filename, + RBIMPL_ATTR_MAYBE_UNUSED() + int line) +{ +#if USE_RGENGC_LOGGING_WB_UNPROTECT + RGENGC_LOGGING_OBJ_WRITTEN(a, oldv, b, filename, line); +#endif + + if (!RB_SPECIAL_CONST_P(b)) { + rb_gc_writebarrier(a, b); + } + + return a; +} + +/** + * @private + * + * This is the implementation of #RB_OBJ_WRITE(). People don't use it + * directly. + * + * @param[in] a An old object. + * @param[in] slot A pointer inside of `old`. + * @param[out] b A young object. + * @param[in] filename C's `__FILE__` of the caller function. + * @param[in] line C's `__LINE__` of the caller function. + * @return a + */ +static inline VALUE +rb_obj_write( + VALUE a, VALUE *slot, VALUE b, + RBIMPL_ATTR_MAYBE_UNUSED() + const char *filename, + RBIMPL_ATTR_MAYBE_UNUSED() + int line) +{ +#ifdef RGENGC_LOGGING_WRITE + RGENGC_LOGGING_WRITE(a, slot, b, filename, line); +#endif + + *slot = b; + + rb_obj_written(a, RUBY_Qundef /* ignore `oldv' now */, b, filename, line); + return a; +} + #endif /* RBIMPL_GC_H */ diff --git a/include/ruby/internal/globals.h b/include/ruby/internal/globals.h index 5a414fc472..9beb215c0c 100644 --- a/include/ruby/internal/globals.h +++ b/include/ruby/internal/globals.h @@ -68,6 +68,7 @@ RUBY_EXTERN VALUE rb_cBasicObject; /**< `BasicObject` class. */ RUBY_EXTERN VALUE rb_cObject; /**< `Object` class. */ RUBY_EXTERN VALUE rb_cArray; /**< `Array` class. */ RUBY_EXTERN VALUE rb_cBinding; /**< `Binding` class. */ +RUBY_EXTERN VALUE rb_cBox; /**< `Ruby::Box` class. */ RUBY_EXTERN VALUE rb_cClass; /**< `Class` class. */ RUBY_EXTERN VALUE rb_cDir; /**< `Dir` class. */ RUBY_EXTERN VALUE rb_cEncoding; /**< `Encoding` class. */ @@ -91,10 +92,11 @@ RUBY_EXTERN VALUE rb_cRandom; /**< `Random` class. */ RUBY_EXTERN VALUE rb_cRange; /**< `Range` class. */ RUBY_EXTERN VALUE rb_cRational; /**< `Rational` class. */ RUBY_EXTERN VALUE rb_cRegexp; /**< `Regexp` class. */ +RUBY_EXTERN VALUE rb_cSet; /**< `Set` 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/builtin.h b/include/ruby/internal/has/builtin.h index 243ba2a34c..8e7fb173d8 100644 --- a/include/ruby/internal/has/builtin.h +++ b/include/ruby/internal/has/builtin.h @@ -48,6 +48,7 @@ # /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66970 */ # define RBIMPL_HAS_BUILTIN(_) (RBIMPL_HAS_BUILTIN_ ## _) # define RBIMPL_HAS_BUILTIN___builtin_add_overflow RBIMPL_COMPILER_SINCE(GCC, 5, 1, 0) +# define RBIMPL_HAS_BUILTIN___builtin_add_overflow_p RBIMPL_COMPILER_SINCE(GCC, 7, 0, 0) # define RBIMPL_HAS_BUILTIN___builtin_alloca RBIMPL_COMPILER_SINCE(GCC, 0, 0, 0) # define RBIMPL_HAS_BUILTIN___builtin_alloca_with_align RBIMPL_COMPILER_SINCE(GCC, 6, 1, 0) # define RBIMPL_HAS_BUILTIN___builtin_assume 0 @@ -75,6 +76,7 @@ # define RBIMPL_HAS_BUILTIN___builtin_rotateright32 0 # define RBIMPL_HAS_BUILTIN___builtin_rotateright64 0 # define RBIMPL_HAS_BUILTIN___builtin_sub_overflow RBIMPL_COMPILER_SINCE(GCC, 5, 1, 0) +# define RBIMPL_HAS_BUILTIN___builtin_sub_overflow_p RBIMPL_COMPILER_SINCE(GCC, 7, 0, 0) # define RBIMPL_HAS_BUILTIN___builtin_unreachable RBIMPL_COMPILER_SINCE(GCC, 4, 5, 0) # /* Note that "0, 0, 0" might be inaccurate. */ @@ -82,6 +84,7 @@ # /* Take config.h definition when available */ # define RBIMPL_HAS_BUILTIN(_) ((RBIMPL_HAS_BUILTIN_ ## _)+0) # define RBIMPL_HAS_BUILTIN___builtin_add_overflow HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW +# define RBIMPL_HAS_BUILTIN___builtin_add_overflow_p HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW_P # define RBIMPL_HAS_BUILTIN___builtin_alloca 0 # define RBIMPL_HAS_BUILTIN___builtin_alloca_with_align HAVE_BUILTIN___BUILTIN_ALLOCA_WITH_ALIGN # define RBIMPL_HAS_BUILTIN___builtin_assume 0 @@ -107,6 +110,7 @@ # define RBIMPL_HAS_BUILTIN___builtin_rotateright64 0 # define RBIMPL_HAS_BUILTIN___builtin_popcountll HAVE_BUILTIN___BUILTIN_POPCOUNTLL # define RBIMPL_HAS_BUILTIN___builtin_sub_overflow HAVE_BUILTIN___BUILTIN_SUB_OVERFLOW +# define RBIMPL_HAS_BUILTIN___builtin_sub_overflow_p HAVE_BUILTIN___BUILTIN_SUB_OVERFLOW_P # if defined(HAVE___BUILTIN_UNREACHABLE) # define RBIMPL_HAS_BUILTIN___builtin_unreachable 1 # else 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 17964bf810..b2cc6b132d 100644 --- a/include/ruby/internal/intern/array.h +++ b/include/ruby/internal/intern/array.h @@ -107,14 +107,14 @@ VALUE rb_ary_new_from_args(long n, ...); VALUE rb_ary_new_from_values(long n, const VALUE *elts); /** - * Allocates a "temporary" array. This is a hidden empty array. Handy on - * occasions. + * Allocates a hidden (no class) empty array. * * @param[in] capa Designed capacity of the array. * @return A hidden, empty array. * @see rb_obj_hide() */ -VALUE rb_ary_tmp_new(long capa); +VALUE rb_ary_hidden_new(long capa); +#define rb_ary_tmp_new rb_ary_hidden_new /** * Destroys the given array for no reason. @@ -144,7 +144,13 @@ void rb_ary_free(VALUE ary); */ void rb_ary_modify(VALUE ary); -/** @alias{rb_obj_freeze} */ +/** + * Freeze an array, preventing further modifications. The underlying buffer may + * be shrunk before freezing to conserve memory. + * + * @param[out] obj Object assumed to be an array to freeze. + * @see RB_OBJ_FREEZE() + */ VALUE rb_ary_freeze(VALUE obj); RBIMPL_ATTR_PURE() @@ -187,7 +193,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 2181ab93c7..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); @@ -200,6 +200,18 @@ VALUE rb_class_descendants(VALUE klass); */ VALUE rb_class_subclasses(VALUE klass); + +/** + * Returns the attached object for a singleton class. + * If the given class is not a singleton class, raises a TypeError. + * + * @param[in] klass A class. + * @return The object which has the singleton class `klass`. + * + * @internal + */ +VALUE rb_class_attached_object(VALUE klass); + /** * Generates an array of symbols, which are the list of method names defined in * the passed class. diff --git a/include/ruby/internal/intern/complex.h b/include/ruby/internal/intern/complex.h index e111bd8ced..1efc093631 100644 --- a/include/ruby/internal/intern/complex.h +++ b/include/ruby/internal/intern/complex.h @@ -87,10 +87,6 @@ VALUE rb_complex_new(VALUE real, VALUE imag); */ VALUE rb_complex_new_polar(VALUE abs, VALUE arg); -RBIMPL_ATTR_DEPRECATED(("by: rb_complex_new_polar")) -/** @old{rb_complex_new_polar} */ -VALUE rb_complex_polar(VALUE abs, VALUE arg); - RBIMPL_ATTR_PURE() /** * Queries the real part of the passed Complex. diff --git a/include/ruby/internal/intern/cont.h b/include/ruby/internal/intern/cont.h index 37493009f5..2d813ceb9d 100644 --- a/include/ruby/internal/intern/cont.h +++ b/include/ruby/internal/intern/cont.h @@ -39,6 +39,28 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() VALUE rb_fiber_new(rb_block_call_func_t func, VALUE callback_obj); /** + * Creates a Fiber instance from a C-backended block with the specified + * storage. + * + * If the given storage is Qundef or Qtrue, this function is equivalent to + * rb_fiber_new() which inherits storage from the current fiber. + * + * Specifying Qtrue is experimental and may be changed in the future. + * + * If the given storage is Qnil, this function will lazy initialize the + * internal storage which starts of empty (without any inheritance). + * + * Otherwise, the given storage is used as the internal storage. + * + * @param[in] func A function, to become the fiber's body. + * @param[in] callback_obj Passed as-is to `func`. + * @param[in] storage The way to set up the storage for the fiber. + * @return An allocated new instance of rb_cFiber, which is ready to be + * "resume"d. + */ +VALUE rb_fiber_new_storage(rb_block_call_func_t func, VALUE callback_obj, VALUE storage); + +/** * Queries the fiber which is calling this function. Any ruby execution * context has its fiber, either explicitly or implicitly. * @@ -126,7 +148,8 @@ VALUE rb_fiber_resume(VALUE fiber, int argc, const VALUE *argv); * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eFiberError `fiber` is terminated etc. * @exception rb_eException Any exceptions happen in `fiber`. * @return Either what was yielded or the last value of the fiber body. @@ -170,7 +193,8 @@ VALUE rb_fiber_yield(int argc, const VALUE *argv); * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eException What was raised using `Fiber#raise`. * @return (See rb_fiber_resume() for details) */ @@ -225,7 +249,8 @@ VALUE rb_fiber_transfer(VALUE fiber, int argc, const VALUE *argv); * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eFiberError (See above) * @exception rb_eException What was raised using `Fiber#raise`. * @return (See rb_fiber_resume() for details) @@ -253,7 +278,7 @@ VALUE rb_fiber_transfer_kw(VALUE fiber, int argc, const VALUE *argv, int kw_spla * @exception rb_eFiberError `fiber` is terminated etc. * @return (See rb_fiber_resume() for details) */ -VALUE rb_fiber_raise(VALUE fiber, int argc, const VALUE *argv); +VALUE rb_fiber_raise(VALUE fiber, int argc, VALUE *argv); RBIMPL_SYMBOL_EXPORT_END() diff --git a/include/ruby/internal/intern/enumerator.h b/include/ruby/internal/intern/enumerator.h index 20e5d7c6fc..00804d786a 100644 --- a/include/ruby/internal/intern/enumerator.h +++ b/include/ruby/internal/intern/enumerator.h @@ -100,7 +100,8 @@ VALUE rb_enumeratorize_with_size(VALUE recv, VALUE meth, int argc, const VALUE * * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eTypeError `meth` is not an instance of ::rb_cSymbol. * @return A new instance of ::rb_cEnumerator which, when yielded, * enumerates by calling `meth` on `recv` with `argv`. @@ -186,7 +187,8 @@ RBIMPL_SYMBOL_EXPORT_END() * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @return A new instance of ::rb_cEnumerator which, when yielded, * enumerates by calling the current method on `recv` with `argv`. */ @@ -220,7 +222,8 @@ RBIMPL_SYMBOL_EXPORT_END() * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @note This macro may return inside. */ #define RETURN_SIZED_ENUMERATOR_KW(obj, argc, argv, size_fn, kw_splat) do { \ @@ -250,7 +253,8 @@ RBIMPL_SYMBOL_EXPORT_END() * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @note This macro may return inside. */ #define RETURN_ENUMERATOR_KW(obj, argc, argv, kw_splat) \ diff --git a/include/ruby/internal/intern/error.h b/include/ruby/internal/intern/error.h index 9c153cbac5..1fd9ec2f51 100644 --- a/include/ruby/internal/intern/error.h +++ b/include/ruby/internal/intern/error.h @@ -235,7 +235,9 @@ RBIMPL_ATTR_NORETURN() * @param[in] max Maximum allowed `argc`. * @exception rb_eArgError Always. */ -MJIT_STATIC void rb_error_arity(int argc, int min, int max); +void rb_error_arity(int argc, int min, int max); + +void rb_str_modify(VALUE str); RBIMPL_SYMBOL_EXPORT_END() @@ -244,12 +246,7 @@ RBIMPL_SYMBOL_EXPORT_END() * * Does anyone use this? Remain not deleted for compatibility. */ -#define rb_check_frozen_internal(obj) do { \ - VALUE frozen_obj = (obj); \ - if (RB_UNLIKELY(RB_OBJ_FROZEN(frozen_obj))) { \ - rb_error_frozen_object(frozen_obj); \ - } \ - } while (0) +#define rb_check_frozen_internal rb_check_frozen /** @alias{rb_check_frozen} */ static inline void @@ -258,9 +255,16 @@ rb_check_frozen_inline(VALUE obj) if (RB_UNLIKELY(RB_OBJ_FROZEN(obj))) { rb_error_frozen_object(obj); } + + /* ref: internal CHILLED_STRING_P() + This is an implementation detail subject to change. */ + if (RB_UNLIKELY(RB_TYPE_P(obj, T_STRING) && FL_TEST_RAW(obj, RUBY_FL_USER2 | RUBY_FL_USER3))) { // STR_CHILLED + rb_str_modify(obj); + } } -/** @alias{rb_check_frozen} */ +/* rb_check_frozen() is available as a symbol, but have + * the inline version take priority for native consumers. */ #define rb_check_frozen rb_check_frozen_inline /** diff --git a/include/ruby/internal/intern/file.h b/include/ruby/internal/intern/file.h index 2dc60c7ba7..8508b7ab9e 100644 --- a/include/ruby/internal/intern/file.h +++ b/include/ruby/internal/intern/file.h @@ -24,6 +24,9 @@ #include "ruby/internal/attr/pure.h" #include "ruby/internal/dllexport.h" #include "ruby/internal/value.h" +#if !defined RUBY_EXPORT && !defined RUBY_NO_OLD_COMPATIBILITY +# include "ruby/backward.h" +#endif RBIMPL_SYMBOL_EXPORT_BEGIN() @@ -206,7 +209,7 @@ int rb_is_absolute_path(const char *path); * unpredictable. POSIX's `<sys/stat.h>` states that "the use of * this field is unspecified" then. */ -off_t rb_file_size(VALUE file); +rb_off_t rb_file_size(VALUE file); RBIMPL_SYMBOL_EXPORT_END() diff --git a/include/ruby/internal/intern/gc.h b/include/ruby/internal/intern/gc.h deleted file mode 100644 index e7b8008729..0000000000 --- a/include/ruby/internal/intern/gc.h +++ /dev/null @@ -1,392 +0,0 @@ -#ifndef RBIMPL_INTERN_GC_H /*-*-C++-*-vi:se ft=cpp:*/ -#define RBIMPL_INTERN_GC_H -/** - * @file - * @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 Public APIs related to ::rb_mGC. - */ -#include "ruby/internal/config.h" - -#ifdef STDC_HEADERS -# include <stddef.h> /* size_t */ -#endif - -#if HAVE_SYS_TYPES_H -# include <sys/types.h> /* ssize_t */ -#endif - -#include "ruby/internal/attr/cold.h" -#include "ruby/internal/attr/noreturn.h" -#include "ruby/internal/attr/nonnull.h" -#include "ruby/internal/attr/pure.h" -#include "ruby/internal/dllexport.h" -#include "ruby/internal/value.h" - -RBIMPL_SYMBOL_EXPORT_BEGIN() - -/* gc.c */ - -RBIMPL_ATTR_COLD() -RBIMPL_ATTR_NORETURN() -/** - * Triggers out-of-memory error. If possible it raises ::rb_eNoMemError. But - * because we are running out of memory that is not always doable. This - * function tries hard to show something, but ultimately can die silently. - * - * @exception rb_eNoMemError Raises it if possible. - */ -void rb_memerror(void); - -RBIMPL_ATTR_PURE() -/** - * Queries if the GC is busy. - * - * @retval 0 It isn't. - * @retval 1 It is. - */ -int rb_during_gc(void); - -RBIMPL_ATTR_NONNULL((1)) -/** - * Marks objects between the two pointers. This is one of the GC utility - * functions that you can call when you design your own - * ::rb_data_type_struct::dmark. - * - * @pre Continuous memory region from `start` to `end` shall be fully - * addressable. - * @param[out] start Pointer to an array of objects. - * @param[out] end Pointer that terminates the array of objects. - * @post Objects from `start` to `end`, both inclusive, are marked. - * - * @internal - * - * `end` can be NULL... But that just results in no-op. - */ -void rb_gc_mark_locations(const VALUE *start, const VALUE *end); - -/** - * Identical to rb_mark_hash(), except it marks only values of the table and - * leave their associated keys unmarked. This is one of the GC utility - * functions that you can call when you design your own - * ::rb_data_type_struct::dmark. - * - * @warning Of course it can break GC. Leave it unused if unsure. - * @param[in] tbl A table to mark. - * @post Values stored in `tbl` are marked. - */ -void rb_mark_tbl(struct st_table *tbl); - -/** - * Identical to rb_mark_tbl(), except it marks objects using - * rb_gc_mark_movable(). This is one of the GC utility functions that you can - * call when you design your own ::rb_data_type_struct::dmark. - * - * @warning Of course it can break GC. Leave it unused if unsure. - * @param[in] tbl A table to mark. - * @post Values stored in `tbl` are marked. - */ -void rb_mark_tbl_no_pin(struct st_table *tbl); - -/** - * Identical to rb_mark_hash(), except it marks only keys of the table and - * leave their associated values unmarked. This is one of the GC utility - * functions that you can call when you design your own - * ::rb_data_type_struct::dmark. - * - * @warning Of course it can break GC. Leave it unused if unsure. - * @param[in] tbl A table to mark. - * @post Keys stored in `tbl` are marked. - */ -void rb_mark_set(struct st_table *tbl); - -/** - * Marks keys and values associated inside of the given table. This is one of - * the GC utility functions that you can call when you design your own - * ::rb_data_type_struct::dmark. - * - * @param[in] tbl A table to mark. - * @post Objects stored in `tbl` are marked. - */ -void rb_mark_hash(struct st_table *tbl); - -/** - * Updates references inside of tables. After you marked values using - * rb_mark_tbl_no_pin(), the objects inside of the table could of course be - * moved. This function is to fixup those references. You can call this from - * your ::rb_data_type_struct::dcompact. - * - * @param[out] ptr A table that potentially includes moved references. - * @post Moved references, if any, are corrected. - */ -void rb_gc_update_tbl_refs(st_table *ptr); - -/** - * Identical to rb_gc_mark(), except it allows the passed value be a - * non-object. For instance pointers to different type of memory regions are - * allowed here. Such values are silently ignored. This is one of the GC - * utility functions that you can call when you design your own - * ::rb_data_type_struct::dmark. - * - * @param[out] obj A possible object. - * @post `obj` is marked, if possible. - */ -void rb_gc_mark_maybe(VALUE obj); - -/** - * Marks an object. This is one of the GC utility functions that you can call - * when you design your own ::rb_data_type_struct::dmark. - * - * @param[out] obj Arbitrary Ruby object. - * @post `obj` is marked. - */ -void rb_gc_mark(VALUE obj); - -/** - * Maybe this is the only function provided for C extensions to control the - * pinning of objects, so let us describe it in detail. These days Ruby's GC - * is copying. As far as an object's physical address is guaranteed unused, it - * can move around the object space. Our GC engine rearranges these objects - * after it reclaims unreachable objects from our object space, so that the - * space is compact (improves memory locality). This is called the - * "compaction" phase, and works well most of the time... as far as there are - * no C extensions. C extensions complicate the scenario because Ruby core - * cannot detect any use of the physical address of an object inside of C - * functions. In order to prevent memory corruptions, objects observable from - * C extensions are "pinned"; they stick to where they are born until they die, - * just in case any C extensions touch their raw pointers. This variant of - * scheme is called "Mostly-Copying" garbage collector. Authors of C - * extensions, however, can extremely carefully write them to become - * compaction-aware. To do so avoid referring to a Ruby object from inside of - * your struct in the first place. But if that is not possible, use this - * function from your ::rb_data_type_struct::dmark then. This way objects - * marked using it are considered movable. If you chose this way you have to - * manually fix up locations of such moved pointers using rb_gc_location(). - * - * @see Bartlett, Joel F., "Compacting Garbage Collection with Ambiguous - * Roots", ACM SIGPLAN Lisp Pointers Volume 1 Issue 6 pp. 3-12, - * April-May-June, 1988. https://doi.org/10.1145/1317224.1317225 - * - * @param[in] obj Object that is movable. - * @post Values stored in `tbl` are marked. - */ -void rb_gc_mark_movable(VALUE obj); - -/** - * Finds a new "location" of an object. An object can be moved on compaction. - * This function projects its new abode, or just returns the passed object if - * not moved. This is one of the GC utility functions that you can call when - * you design your own ::rb_data_type_struct::dcompact. - * - * @param[in] obj An object, possibly already moved to somewhere else. - * @return An object, which holds the current contents of former `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 - * procedures kicked by this API is called a "full" GC. - * - * - It immediately scans the entire object space to sort the dead. - * - It immediately reclaims any single dead bodies to reuse later. - * - * It is worth noting that the procedures above do not include evaluations of - * finalisers. They run later. - * - * @internal - * - * Finalisers are deferred until we can handle interrupts. See - * `rb_postponed_job_flush` in vm_trace.c. - * - * Of course there are GC that are not "full". For instance this one and the - * GC which runs when we are running out of memory are different. See - * `gc_profile_record_flag` defined in gc.c for the kinds of GC. - * - * In spite of the name this is not what everything that a GC can trigger. As - * of writing it seems this function does not trigger compaction. But this - * might change in future. - */ -void rb_gc(void); - -/** - * Copy&paste an object's finaliser to another. This is one of the GC utility - * functions that you can call when you design your own `initialize_copy`, - * `initialize_dup`, `initialize_clone`. - * - * @param[out] dst Destination object. - * @param[in] src Source object. - * @post `dst` and `src` share the same finaliser. - * - * @internal - * - * But isn't it easier for you to call super, and let `Object#initialize_copy` - * call this function instead? - */ -void rb_gc_copy_finalizer(VALUE dst, VALUE src); - -/** - * (Re-) enables GC. This makes sense only after you called rb_gc_disable(). - * - * @retval RUBY_Qtrue GC was disabled before. - * @retval RUBY_Qfalse GC was enabled before. - * @post GC is enabled. - * - * @internal - * - * This is one of such exceptional functions that does not raise both Ruby - * exceptions and C++ exceptions. - */ -VALUE rb_gc_enable(void); - -/** - * Disables GC. This prevents automatic GC runs when the process is running - * out of memory. Such situations shall result in rb_memerror(). However this - * does not prevent users from manually invoking rb_gc(). That should work. - * People abused this by disabling GC at the beginning of an event loop, - * process events without GC overheads, then manually force reclaiming garbage - * at the bottom of the loop. However because our GC is now much smarter than - * just calling rb_gc(), this technique is proven to be sub-optimal these days. - * It is believed that there is currently practically no needs of this - * function. - * - * @retval RUBY_Qtrue GC was disabled before. - * @retval RUBY_Qfalse GC was enabled before. - * @post GC is disabled. - */ -VALUE rb_gc_disable(void); - -/** - * Identical to rb_gc(), except the return value. - * - * @return Always returns ::RUBY_Qnil. - */ -VALUE rb_gc_start(void); - -/** - * Assigns a finaliser for an object. Each objects can have objects (typically - * blocks) that run immediately after that object dies. They are called - * finalisers of an object. This function associates a finaliser object with a - * target object. - * - * @note Note that finalisers run _after_ the object they finalise dies. You - * cannot for instance call its methods. - * @note If your finaliser references the object it finalises that object - * loses any chance to become a garbage; effectively leaks memory until - * the end of the process. - * - * @param[in] obj Target to finalise. - * @param[in] block Something `call`able. - * @exception rb_eRuntimeError Somehow `obj` cannot have finalisers. - * @exception rb_eFrozenError `obj` is frozen. - * @exception rb_eArgError `block` doesn't respond to `call`. - * @return The passed `block`. - * @post `block` runs after `obj` dies. - */ -VALUE rb_define_finalizer(VALUE obj, VALUE block); - -/** - * Modifies the object so that it has no finalisers at all. This function is - * mainly provided for symmetry. No practical usages can be thought of. - * - * @param[out] obj Object to clear its finalisers. - * @exception rb_eFrozenError `obj` is frozen. - * @return The passed `obj`. - * @post `obj` has no finalisers. - * @note There is no way to undefine a specific part of many finalisers - * that `obj` could have. All you can do is to clear them all. - */ -VALUE rb_undefine_finalizer(VALUE obj); - -/** - * Identical to rb_gc_stat(), with "count" parameter. - * - * @return Lifetime total number of runs of GC. - */ -size_t rb_gc_count(void); - -/** - * Obtains various GC related profiles. The parameter can be either a Symbol - * or a Hash. If a Hash is passed, it is filled with everything currently - * available. If a Symbol is passed just that portion is returned. - * - * Possible variations of keys you can pass here change from version to - * version. You can get the list of known keys by passing an empty hash and - * let it be filled. - * - * @param[in,out] key_or_buf A Symbol, or a Hash. - * @exception rb_eTypeError Neither Symbol nor Hash. - * @exception rb_eFrozenError Frozen hash is passed. - * @return In case a Hash is passed it returns 0. Otherwise the - * profile value associated with the given key is returned. - * @post In case a Hash is passed it is filled with values. - */ -size_t rb_gc_stat(VALUE key_or_buf); - -/** - * Obtains various info regarding the most recent GC run. This includes for - * instance the reason of the GC. The parameter can be either a Symbol or a - * Hash. If a Hash is passed, it is filled with everything currently - * available. If a Symbol is passed just that portion is returned. - * - * Possible variations of keys you can pass here change from version to - * version. You can get the list of known keys by passing an empty hash and - * let it be filled. - * - * @param[in,out] key_or_buf A Symbol, or a Hash. - * @exception rb_eTypeError Neither Symbol nor Hash. - * @exception rb_eFrozenError Frozen hash is passed. - * @return In case a Hash is passed it returns that hash. Otherwise - * the profile value associated with the given key is returned. - * @post In case a Hash is passed it is filled with values. - */ -VALUE rb_gc_latest_gc_info(VALUE key_or_buf); - -/** - * Informs that there are external memory usages. Our GC runs when we are - * running out of memory. The amount of memory, however, can increase/decrease - * behind-the-scene. For instance DLLs can allocate memories using `mmap(2)` - * etc, which are opaque to us. Registering such external allocations using - * this function enables proper detection of how much memories an object used - * as a whole. That will trigger GCs more often than it would otherwise. You - * can also pass negative numbers here, to indicate that such external - * allocations are gone. - * - * @param[in] diff Amount of memory increased(+)/decreased(-). - */ -void rb_gc_adjust_memory_usage(ssize_t diff); - -RBIMPL_SYMBOL_EXPORT_END() - -#endif /* RBIMPL_INTERN_GC_H */ diff --git a/include/ruby/internal/intern/hash.h b/include/ruby/internal/intern/hash.h index af8dfd5d8f..504770fa5f 100644 --- a/include/ruby/internal/intern/hash.h +++ b/include/ruby/internal/intern/hash.h @@ -284,20 +284,6 @@ typedef VALUE rb_hash_update_func(VALUE newkey, VALUE oldkey, VALUE value); */ VALUE rb_hash_update_by(VALUE hash1, VALUE hash2, rb_hash_update_func *func); -/* file.c */ - -/** - * This function is mysterious. What it does is not immediately obvious. Also - * what it does seems platform dependent. - * - * @param[in] path A local path. - * @retval 0 The "check" succeeded. - * @retval otherwise The "check" failed. - */ -int rb_path_check(const char *path); - -/* hash.c */ - /** * Destructively removes every environment variables of the running process. * diff --git a/include/ruby/internal/intern/io.h b/include/ruby/internal/intern/io.h index 02c249723e..b9eb258cc1 100644 --- a/include/ruby/internal/intern/io.h +++ b/include/ruby/internal/intern/io.h @@ -385,7 +385,7 @@ VALUE rb_io_puts(int argc, const VALUE *argv, VALUE io); * @param[in] fd Target file descriptor. * @param[in] flags Flags, e.g. `O_CREAT|O_EXCL` * @param[in] path The path of the file that backs `fd`, for diagnostics. - * @return An allocated instance of ::rb_cIO. + * @return An allocated instance of ::rb_cIO with the autoclose flag set. * @note Leave `path` NULL if you don't know. */ VALUE rb_io_fdopen(int fd, int flags, const char *path); 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 19af49b140..3897639a0a 100644 --- a/include/ruby/internal/intern/object.h +++ b/include/ruby/internal/intern/object.h @@ -80,7 +80,8 @@ VALUE rb_class_new_instance(int argc, const VALUE *argv, VALUE klass); * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eTypeError `klass`'s allocator is undefined. * @exception rb_eException Any exceptions can happen inside. * @return An allocated new instance of `klass`. @@ -92,8 +93,8 @@ VALUE rb_class_new_instance_kw(int argc, const VALUE *argv, VALUE klass, int kw_ * * @param[in] lhs Comparison left hand side. * @param[in] rhs Comparison right hand side. - * @retval RUBY_Qtrue They are equal. - * @retval RUBY_Qfalse Otherwise. + * @retval non-zero They are equal. + * @retval 0 Otherwise. * @note This function actually calls `lhs.eql?(rhs)` so you cannot * implement your class' `#eql?` method using it. */ @@ -151,13 +152,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/proc.h b/include/ruby/internal/intern/proc.h index b8c3c5e146..2635d672eb 100644 --- a/include/ruby/internal/intern/proc.h +++ b/include/ruby/internal/intern/proc.h @@ -101,7 +101,8 @@ VALUE rb_proc_call(VALUE recv, VALUE args); * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `args`' last is not a keyword argument. * - RB_PASS_KEYWORDS `args`' last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eException Any exceptions happen inside. * @return What the proc evaluates to. */ @@ -141,7 +142,8 @@ VALUE rb_proc_call_with_block(VALUE recv, int argc, const VALUE *argv, VALUE pro * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `args`' last is not a keyword argument. * - RB_PASS_KEYWORDS `args`' last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eException Any exceptions happen inside. * @return What the proc evaluates to. */ @@ -245,7 +247,8 @@ VALUE rb_method_call(int argc, const VALUE *argv, VALUE recv); * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `args`' last is not a keyword argument. * - RB_PASS_KEYWORDS `args`' last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eTypeError `recv` is not a method. * @exception rb_eException Any exceptions happen inside. * @return What the method returns. @@ -279,7 +282,8 @@ VALUE rb_method_call_with_block(int argc, const VALUE *argv, VALUE recv, VALUE p * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `args`' last is not a keyword argument. * - RB_PASS_KEYWORDS `args`' last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @exception rb_eTypeError `recv` is not a method. * @exception rb_eException Any exceptions happen inside. * @return What the method returns. 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..ba75213618 100644 --- a/include/ruby/internal/intern/select.h +++ b/include/ruby/internal/intern/select.h @@ -72,11 +72,13 @@ struct timeval; * someone else, vastly varies among operating systems. You would better avoid * touching an fd from more than one threads. * + * NOTE: this function is used in native extensions, so change its API with care. + * * @internal * * 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/select/posix.h b/include/ruby/internal/intern/select/posix.h index 5f828e66e2..0a9b0b2e51 100644 --- a/include/ruby/internal/intern/select/posix.h +++ b/include/ruby/internal/intern/select/posix.h @@ -136,7 +136,7 @@ rb_fd_max(const rb_fdset_t *f) } /** @cond INTERNAL_MACRO */ -/* :FIXME: What are these? They don't exist for shibling implementations. */ +/* :FIXME: What are these? They don't exist for sibling implementations. */ #define rb_fd_init_copy(d, s) (*(d) = *(s)) #define rb_fd_term(f) ((void)(f)) /** @endcond */ diff --git a/include/ruby/internal/intern/select/win32.h b/include/ruby/internal/intern/select/win32.h index edaf7a8523..b7301e63f3 100644 --- a/include/ruby/internal/intern/select/win32.h +++ b/include/ruby/internal/intern/select/win32.h @@ -206,7 +206,7 @@ rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src) * property we heavily touch the internals of MSVCRT. We `CreateFile` a * `"NUL"` alongside of a socket and directly manipulate its `struct ioinfo`. * This is of course a very dirty hack. If we could design the API today we - * could use `CancellIoEx`. But we are older than that Win32 API. + * could use `CancelIoEx`. But we are older than that Win32 API. */ static inline int rb_fd_select(int n, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout) @@ -253,7 +253,7 @@ rb_fd_max(const rb_fdset_t *f) const fd_set *p = f->fdset; RBIMPL_ASSERT_OR_ASSUME(p); - return p->fd_count; + return RBIMPL_CAST((int)p->fd_count); } #endif /* RBIMPL_INTERN_SELECT_WIN32_H */ diff --git a/include/ruby/internal/intern/set.h b/include/ruby/internal/intern/set.h new file mode 100644 index 0000000000..f4ff8665e2 --- /dev/null +++ b/include/ruby/internal/intern/set.h @@ -0,0 +1,111 @@ +#ifndef RBIMPL_INTERN_SET_H /*-*-C++-*-vi:se ft=cpp:*/ +#define RBIMPL_INTERN_SET_H +/** + * @file + * @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 Public APIs related to ::rb_cSet. + */ +#include "ruby/internal/attr/nonnull.h" +#include "ruby/internal/dllexport.h" +#include "ruby/internal/value.h" + +RBIMPL_SYMBOL_EXPORT_BEGIN() + +/* set.c */ + +RBIMPL_ATTR_NONNULL(()) +/** + * Iterates over a set. Calls func with each element of the set and the + * argument given. func should return ST_CONTINUE, ST_STOP, or ST_DELETE. + * + * @param[in] set An instance of ::rb_cSet to iterate over. + * @param[in] func Callback function to yield. + * @param[in] arg Passed as-is to `func`. + * @exception rb_eRuntimeError `set` was tampered during iterating. + */ +void rb_set_foreach(VALUE set, int (*func)(VALUE element, VALUE arg), VALUE arg); + +/** + * Creates a new, empty set object. + * + * @return An allocated new instance of ::rb_cSet. + */ +VALUE rb_set_new(void); + +/** + * Identical to rb_set_new(), except it additionally specifies how many elements + * it is expected to contain. This way you can create a set that is large enough + * for your need. For large sets, it means it won't need to be reallocated + * much, improving performance. + * + * @param[in] capa Designed capacity of the set. + * @return An empty Set, whose capacity is `capa`. + */ +VALUE rb_set_new_capa(size_t capa); + +/** + * Whether the set contains the given element. + * + * @param[in] set Set to look into. + * @param[in] element Set element to look for. + * @return true if element is in the set, falst otherwise. + */ +bool rb_set_lookup(VALUE set, VALUE element); + +/** + * Adds element to set. + * + * @param[in] set Target set table to modify. + * @param[in] element Arbitrary Ruby object. + * @exception rb_eFrozenError `set` is frozen. + * @return true if element was not already in set, false otherwise + * @post `element` is in `set`. + */ +bool rb_set_add(VALUE set, VALUE element); + +/** + * Removes all entries from set. + * + * @param[out] set Target to clear. + * @exception rb_eFrozenError `set`is frozen. + * @return The passed `set` + * @post `set` has no elements. + */ +VALUE rb_set_clear(VALUE set); + +/** + * Removes the element from from set. + * + * @param[in] set Target set to modify. + * @param[in] element Key to delete. + * @retval true if element was already in set, false otherwise + * @post `set` does not have `element` as an element. + */ +bool rb_set_delete(VALUE set, VALUE element); + +/** + * Returns the number of elements in the set. + * + * @param[in] set A set object. + * @return The size of the set. + */ +size_t rb_set_size(VALUE set); + +RBIMPL_SYMBOL_EXPORT_END() + +#endif /* RBIMPL_INTERN_SET_H */ 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..2ec08fc81f 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. @@ -444,8 +444,8 @@ VALUE rb_str_to_interned_str(VALUE str); * terminating NUL character. * @exception rb_eArgError `len` is negative. * @return A found or created instance of ::rb_cString, of `len` bytes - * length, of "binary" encoding, whose contents are identical to - * that of `ptr`. + * length, of US-ASCII or "binary" encoding, whose contents are + * identical to that of `ptr`. * @pre At least `len` bytes of continuous memory region shall be * accessible via `ptr`. */ @@ -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. @@ -591,10 +591,9 @@ void rb_must_asciicompat(VALUE obj); VALUE rb_str_dup(VALUE str); /** - * I guess there is no use case of this function in extension libraries, but - * this is a routine identical to rb_str_dup(), except it always creates an - * instance of ::rb_cString regardless of the given object's class. This makes - * the most sense when the passed string is formerly hidden by rb_obj_hide(). + * Like rb_str_dup(), but always create an instance of ::rb_cString + * regardless of the given object's class. This makes the most sense + * when the passed string is formerly hidden by rb_obj_hide(). * * @param[in] str A string, possibly hidden. * @return A duplicated new instance of ::rb_cString. @@ -970,8 +969,8 @@ st_index_t rb_str_hash(VALUE str); * * @param[in] str1 A string. * @param[in] str2 Another string. - * @retval 1 They have identical contents, length, and encodings. - * @retval 0 Otherwise. + * @retval 0 They have identical contents, length, and encodings. + * @retval 1 Otherwise. * @pre Both objects must not be any arbitrary objects except * ::RString. * @@ -1686,10 +1685,10 @@ rbimpl_exc_new_cstr(VALUE exc, const char *str) * Length of a string literal. * * @param[in] str A C String literal. - * @return An integer constant expression that represents `str`'s length, - * in bytes, not including the terminating NUL character. + * @return An integer constant expression that represents the number of + * `str`'s elements, not including the terminating NUL character. */ -#define rb_strlen_lit(str) (sizeof(str "") - 1) +#define rb_strlen_lit(str) ((sizeof(str "") / sizeof(str ""[0])) - 1) /** * Identical to rb_str_new_static(), except it cannot take string variables. 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/thread.h b/include/ruby/internal/intern/thread.h index 716375acd7..4d87452745 100644 --- a/include/ruby/internal/intern/thread.h +++ b/include/ruby/internal/intern/thread.h @@ -61,10 +61,10 @@ int rb_thread_wait_fd(int fd); int rb_thread_fd_writable(int fd); /** - * Notifies a closing of a file descriptor to other threads. Multiple threads - * can wait for the given file descriptor at once. If such file descriptor is - * closed, threads need to start propagating their exceptions. This is the API - * to kick that process. + * This funciton is now a no-op. It was previously used to interrupt threads + * that were using the given file descriptor and wait for them to finish. + * + * @deprecated Use IO with RUBY_IO_MODE_EXTERNAL and `rb_io_close` instead. * * @param[in] fd A file descriptor. * @note This function blocks until all the threads waiting for such fd diff --git a/include/ruby/internal/intern/vm.h b/include/ruby/internal/intern/vm.h index 76af796b54..f0b54c702c 100644 --- a/include/ruby/internal/intern/vm.h +++ b/include/ruby/internal/intern/vm.h @@ -89,7 +89,8 @@ VALUE rb_check_funcall(VALUE recv, ID mid, int argc, const VALUE *argv); * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @retval RUBY_Qundef `recv` doesn't respond to `mid`. * @retval otherwise What the method evaluates to. */ @@ -106,9 +107,11 @@ VALUE rb_check_funcall_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int k * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `arg`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `arg`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @return What the command evaluates to. */ +RBIMPL_ATTR_DEPRECATED_INTERNAL(4.0) VALUE rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat); /** @@ -229,8 +232,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/iterator.h b/include/ruby/internal/iterator.h index 5f706460f8..891045363e 100644 --- a/include/ruby/internal/iterator.h +++ b/include/ruby/internal/iterator.h @@ -265,48 +265,6 @@ int rb_block_given_p(void); */ void rb_need_block(void); -#ifndef __cplusplus -RBIMPL_ATTR_DEPRECATED(("by: rb_block_call since 1.9")) -#endif -/** - * Old way to iterate a block. - * - * @deprecated This is an old API. Use rb_block_call() instead. - * @warning The passed function must at least once call a ruby method - * (to handle interrupts etc.) - * @param[in] func1 A function that could yield a value. - * @param[in,out] data1 Passed to `func1` - * @param[in] proc A function acts as a block. - * @param[in,out] data2 Passed to `proc` as the data2 parameter. - * @return What `func1` returns. - */ -VALUE rb_iterate(VALUE (*func1)(VALUE), VALUE data1, rb_block_call_func_t proc, VALUE data2); - -#ifdef __cplusplus -namespace ruby { -namespace backward { -/** - * Old way to iterate a block. - * - * @deprecated This is an old API. Use rb_block_call() instead. - * @warning The passed function must at least once call a ruby method - * (to handle interrupts etc.) - * @param[in] iter A function that could yield a value. - * @param[in,out] data1 Passed to `func1` - * @param[in] bl A function acts as a block. - * @param[in,out] data2 Passed to `proc` as the data2 parameter. - * @return What `func1` returns. - */ -static inline VALUE -rb_iterate_deprecated(VALUE (*iter)(VALUE), VALUE data1, rb_block_call_func_t bl, VALUE data2) -{ - return ::rb_iterate(iter, data1, bl, data2); -}}} - -RBIMPL_ATTR_DEPRECATED(("by: rb_block_call since 1.9")) -VALUE rb_iterate(VALUE (*func1)(VALUE), VALUE data1, rb_block_call_func_t proc, VALUE data2); -#endif - /** * Identical to rb_funcallv(), except it additionally passes a function as a * block. When the method yields, `proc` is called with the yielded value as @@ -337,7 +295,8 @@ VALUE rb_block_call(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_cal * @param[in] kw_splat Handling of keyword parameters: * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument. * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument. - * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block. + * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method + * was called with keyword arguments. * @return What `obj.mid` returns. */ VALUE rb_block_call_kw(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t proc, VALUE data2, int kw_splat); diff --git a/include/ruby/internal/memory.h b/include/ruby/internal/memory.h index 0f59262a91..cd099f85db 100644 --- a/include/ruby/internal/memory.h +++ b/include/ruby/internal/memory.h @@ -40,7 +40,12 @@ #if defined(_MSC_VER) && defined(_WIN64) # include <intrin.h> +# if defined(_M_AMD64) # pragma intrinsic(_umul128) +# endif +# if defined(_M_ARM64) +# pragma intrinsic(__umulh) +# endif #endif #include "ruby/internal/attr/alloc_size.h" @@ -56,13 +61,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) @@ -287,12 +293,12 @@ typedef uint128_t DSIZE_T; RBIMPL_CAST((type *)alloca(rbimpl_size_mul_or_raise(sizeof(type), (n)))) /** - * Identical to #RB_ALLOCV_N(), except it implicitly assumes the type of array - * is ::VALUE. + * Identical to #RB_ALLOCV_N(), except that it allocates a number of bytes and + * returns a void* . * * @param v A variable to hold the just-in-case opaque Ruby object. * @param n Size of allocation, in bytes. - * @return An array of `n` bytes of ::VALUE. + * @return A void pointer to `n` bytes storage. * @note `n` may be evaluated twice. */ #define RB_ALLOCV(v, n) \ @@ -402,7 +408,8 @@ typedef uint128_t DSIZE_T; /** * @private * - * This is an implementation detail of rbimpl_size_mul_overflow(). + * This is an implementation detail of rbimpl_size_mul_overflow() and + * rbimpl_size_add_overflow(). * * @internal * @@ -410,9 +417,9 @@ typedef uint128_t DSIZE_T; * nothing more than std::variant<std::size_t> if we could use recent C++, but * reality is we cannot. */ -struct rbimpl_size_mul_overflow_tag { - bool left; /**< Whether overflow happened or not. */ - size_t right; /**< Multiplication result. */ +struct rbimpl_size_overflow_tag { + bool overflowed; /**< Whether overflow happened or not. */ + size_t result; /**< Calculation result. */ }; RBIMPL_SYMBOL_EXPORT_BEGIN() @@ -482,6 +489,18 @@ RBIMPL_ATTR_NORETURN() */ void ruby_malloc_size_overflow(size_t x, size_t y); +RBIMPL_ATTR_NORETURN() +/** + * @private + * + * This is an implementation detail. People don't use this directly. + * + * @param[in] x Arbitrary value. + * @param[in] y Arbitrary value. + * @exception rb_eArgError `x` + `y` would integer overflow. + */ +void ruby_malloc_add_size_overflow(size_t x, size_t y); + #ifdef HAVE_RB_GC_GUARDED_PTR_VAL volatile VALUE *rb_gc_guarded_ptr_val(volatile VALUE *ptr, VALUE val); #endif @@ -554,39 +573,46 @@ RBIMPL_ATTR_CONST() * * @param[in] x Arbitrary value. * @param[in] y Arbitrary value. - * @return `{ left, right }`, where `left` is whether there is an integer - * overflow or not, and `right` is a (possibly overflowed) result - * of `x` * `y`. + * @return `{ overflowed, result }`, where `overflowed` is whether there is + * an integer overflow or not, and `result` is a (possibly + * overflowed) result of `x` * `y`. * * @internal * * This is in fact also an implementation detail of ruby_xmalloc2() etc. */ -static inline struct rbimpl_size_mul_overflow_tag +static inline struct rbimpl_size_overflow_tag rbimpl_size_mul_overflow(size_t x, size_t y) { - struct rbimpl_size_mul_overflow_tag ret = { false, 0, }; + struct rbimpl_size_overflow_tag ret = { false, 0, }; + +#if defined(ckd_mul) + ret.overflowed = ckd_mul(&ret.result, x, y); -#if RBIMPL_HAS_BUILTIN(__builtin_mul_overflow) - ret.left = __builtin_mul_overflow(x, y, &ret.right); +#elif RBIMPL_HAS_BUILTIN(__builtin_mul_overflow) + ret.overflowed = __builtin_mul_overflow(x, y, &ret.result); #elif defined(DSIZE_T) RB_GNUC_EXTENSION DSIZE_T dx = x; RB_GNUC_EXTENSION DSIZE_T dy = y; RB_GNUC_EXTENSION DSIZE_T dz = dx * dy; - ret.left = dz > SIZE_MAX; - ret.right = RBIMPL_CAST((size_t)dz); + ret.overflowed = dz > SIZE_MAX; + ret.result = RBIMPL_CAST((size_t)dz); -#elif defined(_MSC_VER) && defined(_WIN64) +#elif defined(_MSC_VER) && defined(_M_AMD64) unsigned __int64 dp = 0; unsigned __int64 dz = _umul128(x, y, &dp); - ret.left = RBIMPL_CAST((bool)dp); - ret.right = RBIMPL_CAST((size_t)dz); + ret.overflowed = RBIMPL_CAST((bool)dp); + ret.result = RBIMPL_CAST((size_t)dz); + +#elif defined(_MSC_VER) && defined(_M_ARM64) + ret.overflowed = __umulh(x, y) != 0; + ret.result = x * y; #else /* https://wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap */ - ret.left = (y != 0) && (x > SIZE_MAX / y); - ret.right = x * y; + ret.overflowed = (y != 0) && (x > SIZE_MAX / y); + ret.result = x * y; #endif return ret; @@ -610,11 +636,11 @@ rbimpl_size_mul_overflow(size_t x, size_t y) static inline size_t rbimpl_size_mul_or_raise(size_t x, size_t y) { - struct rbimpl_size_mul_overflow_tag size = + struct rbimpl_size_overflow_tag size = rbimpl_size_mul_overflow(x, y); - if (RB_LIKELY(! size.left)) { - return size.right; + if (RB_LIKELY(! size.overflowed)) { + return size.result; } else { ruby_malloc_size_overflow(x, y); @@ -622,6 +648,81 @@ rbimpl_size_mul_or_raise(size_t x, size_t y) } } +#if defined(__DOXYGEN__) +RBIMPL_ATTR_CONSTEXPR(CXX14) +#elif RBIMPL_COMPILER_SINCE(GCC, 7, 0, 0) +RBIMPL_ATTR_CONSTEXPR(CXX14) /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70507 */ +#elif RBIMPL_COMPILER_SINCE(Clang, 7, 0, 0) +RBIMPL_ATTR_CONSTEXPR(CXX14) /* https://bugs.llvm.org/show_bug.cgi?id=37633 */ +#endif +RBIMPL_ATTR_CONST() +/** + * @private + * + * This is an implementation detail. People don't use this directly. + * + * @param[in] x Arbitrary value. + * @param[in] y Arbitrary value. + * @return `{ overflowed, result }`, where `overflowed` is whether there is + * an integer overflow or not, and `result` is a (possibly + * overflowed) result of `x` + `y`. + * + * @internal + */ +static inline struct rbimpl_size_overflow_tag +rbimpl_size_add_overflow(size_t x, size_t y) +{ + struct rbimpl_size_overflow_tag ret = { false, 0, }; + +#if defined(ckd_add) + ret.overflowed = ckd_add(&ret.result, x, y); + +#elif RBIMPL_HAS_BUILTIN(__builtin_add_overflow) + ret.overflowed = __builtin_add_overflow(x, y, &ret.result); + +#elif defined(DSIZE_T) + RB_GNUC_EXTENSION DSIZE_T dx = x; + RB_GNUC_EXTENSION DSIZE_T dy = y; + RB_GNUC_EXTENSION DSIZE_T dz = dx + dy; + ret.overflowed = dz > SIZE_MAX; + ret.result = (size_t)dz; + +#else + ret.result = x + y; + ret.overflowed = ret.result < y; + +#endif + + return ret; +} + +/** + * @private + * + * This is an implementation detail. People don't use this directly. + * + * @param[in] x Arbitrary value. + * @param[in] y Arbitrary value. + * @exception rb_eArgError Multiplication could integer overflow. + * @return `x` + `y`. + * + * @internal + */ +static inline size_t +rbimpl_size_add_or_raise(size_t x, size_t y) +{ + struct rbimpl_size_overflow_tag size = + rbimpl_size_add_overflow(x, y); + + if (RB_LIKELY(!size.overflowed)) { + return size.result; + } + else { + ruby_malloc_add_size_overflow(x, y); + RBIMPL_UNREACHABLE_RETURN(0); + } +} + /** * This is an implementation detail of #RB_ALLOCV_N(). People don't use this * directly. @@ -639,7 +740,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 a8a5557a25..13030ae279 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. @@ -158,38 +109,4 @@ void rb_singleton_class_attached(VALUE klass, VALUE obj); void rb_copy_generic_ivar(VALUE clone, VALUE obj); RBIMPL_SYMBOL_EXPORT_END() -RBIMPL_ATTR_DEPRECATED(("This is no longer how Object#clone works.")) -/** - * @deprecated Not sure exactly when but at some time, the implementation of - * `Object#clone` stopped using this function. It remained - * untouched for a while, and then @shyouhei realised that they - * are no longer doing the same thing. It seems nobody seriously - * uses this function any longer. Let's just abandon it. - * - * @param[out] clone The destination object. - * @param[in] obj The source object. - */ -static inline void -rb_clone_setup(VALUE clone, VALUE obj) -{ - return; -} - -RBIMPL_ATTR_DEPRECATED(("This is no longer how Object#dup works.")) -/** - * @deprecated Not sure exactly when but at some time, the implementation of - * `Object#dup` stopped using this function. It remained - * untouched for a while, and then @shyouhei realised that they - * are no longer the same thing. It seems nobody seriously uses - * this function any longer. Let's just abandon it. - * - * @param[out] dup The destination object. - * @param[in] obj The source object. - */ -static inline void -rb_dup_setup(VALUE dup, VALUE obj) -{ - return; -} - #endif /* RBIMPL_NEWOBJ_H */ diff --git a/include/ruby/internal/rgengc.h b/include/ruby/internal/rgengc.h deleted file mode 100644 index 7ea04442f6..0000000000 --- a/include/ruby/internal/rgengc.h +++ /dev/null @@ -1,443 +0,0 @@ -#ifndef RBIMPL_RGENGC_H /*-*-C++-*-vi:se ft=cpp:*/ -#define RBIMPL_RGENGC_H -/** - * @file - * @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 RGENGC write-barrier APIs. - * @see Sasada, K., "Gradual write-barrier insertion into a Ruby - * interpreter", in proceedings of the 2019 ACM SIGPLAN - * International Symposium on Memory Management (ISMM 2019), pp - * 115-121, 2019. https://doi.org/10.1145/3315573.3329986 - */ -#include "ruby/internal/attr/artificial.h" -#include "ruby/internal/attr/maybe_unused.h" -#include "ruby/internal/attr/pure.h" -#include "ruby/internal/dllexport.h" -#include "ruby/internal/special_consts.h" -#include "ruby/internal/stdbool.h" -#include "ruby/internal/value.h" -#include "ruby/assert.h" - -/** - * @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. - */ -#undef USE_RGENGC -#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. - */ -#ifndef USE_RGENGC_LOGGING_WB_UNPROTECT -# define USE_RGENGC_LOGGING_WB_UNPROTECT 0 -#endif - -/** - * @private - * - * This is a compile-time flag to enable/disable write barrier for - * struct ::RArray. It has to be set at the time ruby itself compiles. Makes - * no sense for 3rd parties. - */ -#ifndef RGENGC_WB_PROTECTED_ARRAY -# define RGENGC_WB_PROTECTED_ARRAY 1 -#endif - -/** - * @private - * - * This is a compile-time flag to enable/disable write barrier for - * struct ::RHash. It has to be set at the time ruby itself compiles. Makes - * no sense for 3rd parties. - */ -#ifndef RGENGC_WB_PROTECTED_HASH -# define RGENGC_WB_PROTECTED_HASH 1 -#endif - -/** - * @private - * - * This is a compile-time flag to enable/disable write barrier for - * struct ::RStruct. It has to be set at the time ruby itself compiles. Makes - * no sense for 3rd parties. - */ -#ifndef RGENGC_WB_PROTECTED_STRUCT -# define RGENGC_WB_PROTECTED_STRUCT 1 -#endif - -/** - * @private - * - * This is a compile-time flag to enable/disable write barrier for - * struct ::RString. It has to be set at the time ruby itself compiles. Makes - * no sense for 3rd parties. - */ -#ifndef RGENGC_WB_PROTECTED_STRING -# define RGENGC_WB_PROTECTED_STRING 1 -#endif - -/** - * @private - * - * This is a compile-time flag to enable/disable write barrier for - * struct ::RObject. It has to be set at the time ruby itself compiles. Makes - * no sense for 3rd parties. - */ -#ifndef RGENGC_WB_PROTECTED_OBJECT -# define RGENGC_WB_PROTECTED_OBJECT 1 -#endif - -/** - * @private - * - * This is a compile-time flag to enable/disable write barrier for - * struct ::RRegexp. It has to be set at the time ruby itself compiles. Makes - * no sense for 3rd parties. - */ -#ifndef RGENGC_WB_PROTECTED_REGEXP -# define RGENGC_WB_PROTECTED_REGEXP 1 -#endif - -/** - * @private - * - * This is a compile-time flag to enable/disable write barrier for - * struct ::RClass. It has to be set at the time ruby itself compiles. Makes - * no sense for 3rd parties. - */ -#ifndef RGENGC_WB_PROTECTED_CLASS -# define RGENGC_WB_PROTECTED_CLASS 1 -#endif - -/** - * @private - * - * This is a compile-time flag to enable/disable write barrier for - * struct ::RFloat. It has to be set at the time ruby itself compiles. Makes - * no sense for 3rd parties. - */ -#ifndef RGENGC_WB_PROTECTED_FLOAT -# define RGENGC_WB_PROTECTED_FLOAT 1 -#endif - -/** - * @private - * - * This is a compile-time flag to enable/disable write barrier for - * struct ::RComplex. It has to be set at the time ruby itself compiles. - * Makes no sense for 3rd parties. - */ -#ifndef RGENGC_WB_PROTECTED_COMPLEX -# define RGENGC_WB_PROTECTED_COMPLEX 1 -#endif - -/** - * @private - * - * This is a compile-time flag to enable/disable write barrier for - * struct ::RRational. It has to be set at the time ruby itself compiles. - * Makes no sense for 3rd parties. - */ -#ifndef RGENGC_WB_PROTECTED_RATIONAL -# define RGENGC_WB_PROTECTED_RATIONAL 1 -#endif - -/** - * @private - * - * This is a compile-time flag to enable/disable write barrier for - * struct ::RBignum. It has to be set at the time ruby itself compiles. Makes - * no sense for 3rd parties. - */ -#ifndef RGENGC_WB_PROTECTED_BIGNUM -# define RGENGC_WB_PROTECTED_BIGNUM 1 -#endif - -/** - * @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. - * - * @internal - * - * @shyouhei doesn't think anybody uses this right now. - */ -#ifndef RGENGC_WB_PROTECTED_NODE_CREF -# define RGENGC_WB_PROTECTED_NODE_CREF 1 -#endif - -/** - * @defgroup rgengc Write barrier (WB) interfaces: - * - * @note The following core interfaces can be changed in the future. Please - * catch up if you want to insert WB into C-extensions correctly. - * - * @{ - */ - -/** - * Declaration of a "back" pointer. This is a write barrier for new reference - * from "old" generation to "young" generation. It writes `young` into - * `*slot`, which is a pointer inside of `old`. - * - * @param[in] old An old object. - * @param[in] slot A pointer inside of `old`. - * @param[out] young A young object. - */ -#define RB_OBJ_WRITE(old, slot, young) \ - RBIMPL_CAST(rb_obj_write((VALUE)(old), (VALUE *)(slot), (VALUE)(young), __FILE__, __LINE__)) - -/** - * Identical to #RB_OBJ_WRITE(), except it doesn't write any values, but only a - * WB declaration. `oldv` is replaced value with `b` (not used in current - * Ruby). - * - * @param[in] old An old object. - * @param[in] oldv An object previously stored inside of `old`. - * @param[out] young A young object. - */ -#define RB_OBJ_WRITTEN(old, oldv, young) \ - RBIMPL_CAST(rb_obj_written((VALUE)(old), (VALUE)(oldv), (VALUE)(young), __FILE__, __LINE__)) -/** @} */ - -#define OBJ_PROMOTED_RAW RB_OBJ_PROMOTED_RAW /**< @old{RB_OBJ_PROMOTED_RAW} */ -#define OBJ_PROMOTED RB_OBJ_PROMOTED /**< @old{RB_OBJ_PROMOTED} */ -#define OBJ_WB_UNPROTECT RB_OBJ_WB_UNPROTECT /**< @old{RB_OBJ_WB_UNPROTECT} */ - -/** - * Asserts that the passed object is not fenced by write barriers. Objects of - * such property do not contribute to generational GCs. They are scanned - * always. - * - * @param[out] x An object that would not be protected by the barrier. - */ -#define RB_OBJ_WB_UNPROTECT(x) rb_obj_wb_unprotect(x, __FILE__, __LINE__) - -/** - * Identical to #RB_OBJ_WB_UNPROTECT(), except it can also assert that the - * given object is of given type. - * - * @param[in] type One of `ARRAY`, `STRING`, etc. - * @param[out] obj An object of `type` that would not be protected. - * - * @internal - * - * @shyouhei doesn't understand why this has to be visible from extensions. - */ -#define RB_OBJ_WB_UNPROTECT_FOR(type, obj) \ - (RGENGC_WB_PROTECTED_##type ? OBJ_WB_UNPROTECT(obj) : obj) - -/** - * @private - * - * This is an implementation detail of rb_obj_wb_unprotect(). People don't use - * it directly. - */ -#define RGENGC_LOGGING_WB_UNPROTECT rb_gc_unprotect_logging - -/** @cond INTERNAL_MACRO */ -#define RB_OBJ_PROMOTED_RAW RB_OBJ_PROMOTED_RAW -#define RB_OBJ_PROMOTED RB_OBJ_PROMOTED -/** @endcond */ - -RBIMPL_SYMBOL_EXPORT_BEGIN() -/** - * This is the implementation of #RB_OBJ_WRITE(). People don't use it - * directly. - * - * @param[in] old An object that points to `young`. - * @param[out] young An object that is referenced from `old`. - */ -void rb_gc_writebarrier(VALUE old, VALUE young); - -/** - * This is the implementation of #RB_OBJ_WB_UNPROTECT(). People don't use it - * directly. - * - * @param[out] obj An object that does not participate in WB. - */ -void rb_gc_writebarrier_unprotect(VALUE obj); - -#if USE_RGENGC_LOGGING_WB_UNPROTECT -/** - * @private - * - * This is the implementation of #RGENGC_LOGGING_WB_UNPROTECT(). People - * don't use it directly. - * - * @param[in] objptr Don't know why this is a pointer to void but in - * reality this is a pointer to an object that is about - * to be un-protected. - * @param[in] filename Pass C's `__FILE__` here. - * @param[in] line Pass C's `__LINE__` here. - */ -void rb_gc_unprotect_logging(void *objptr, const char *filename, int line); -#endif - -RBIMPL_SYMBOL_EXPORT_END() - -RBIMPL_ATTR_PURE_UNLESS_DEBUG() -RBIMPL_ATTR_ARTIFICIAL() -/** - * This is the implementation of #RB_OBJ_PROMOTED(). People don't use it - * directly. - * - * @param[in] obj An object to query. - * @retval true The object is "promoted". - * @retval false The object is young. Have not experienced GC at all. - */ -static inline bool -RB_OBJ_PROMOTED_RAW(VALUE obj) -{ - RBIMPL_ASSERT_OR_ASSUME(RB_FL_ABLE(obj)); - return RB_FL_ANY_RAW(obj, RUBY_FL_PROMOTED); -} - -RBIMPL_ATTR_PURE_UNLESS_DEBUG() -RBIMPL_ATTR_ARTIFICIAL() -/** - * Tests if the object is "promoted" -- that is, whether the object experienced - * one or more GC marks. - * - * @param[in] obj An object to query. - * @retval true The object is "promoted". - * @retval false The object is young. Have not experienced GC at all. - * @note Hello, is anyone actively calling this function? @shyouhei have - * never seen any actual usages outside of the GC implementation - * itself. - */ -static inline bool -RB_OBJ_PROMOTED(VALUE obj) -{ - if (! RB_FL_ABLE(obj)) { - return false; - } - else { - return RB_OBJ_PROMOTED_RAW(obj); - } -} - -/** - * This is the implementation of #RB_OBJ_WB_UNPROTECT(). People don't use it - * directly. - * - * @param[out] x An object that does not participate in WB. - * @param[in] filename C's `__FILE__` of the caller function. - * @param[in] line C's `__LINE__` of the caller function. - * @return x - */ -static inline VALUE -rb_obj_wb_unprotect( - VALUE x, - RBIMPL_ATTR_MAYBE_UNUSED() - const char *filename, - RBIMPL_ATTR_MAYBE_UNUSED() - int line) -{ -#if USE_RGENGC_LOGGING_WB_UNPROTECT - RGENGC_LOGGING_WB_UNPROTECT(RBIMPL_CAST((void *)x), filename, line); -#endif - rb_gc_writebarrier_unprotect(x); - return x; -} - -/** - * @private - * - * This is the implementation of #RB_OBJ_WRITTEN(). People don't use it - * directly. - * - * @param[in] a An old object. - * @param[in] oldv An object previously stored inside of `old`. - * @param[out] b A young object. - * @param[in] filename C's `__FILE__` of the caller function. - * @param[in] line C's `__LINE__` of the caller function. - * @return a - */ -static inline VALUE -rb_obj_written( - VALUE a, - RBIMPL_ATTR_MAYBE_UNUSED() - VALUE oldv, - VALUE b, - RBIMPL_ATTR_MAYBE_UNUSED() - const char *filename, - RBIMPL_ATTR_MAYBE_UNUSED() - int line) -{ -#if USE_RGENGC_LOGGING_WB_UNPROTECT - RGENGC_LOGGING_OBJ_WRITTEN(a, oldv, b, filename, line); -#endif - - if (!RB_SPECIAL_CONST_P(b)) { - rb_gc_writebarrier(a, b); - } - - return a; -} - -/** - * @private - * - * This is the implementation of #RB_OBJ_WRITE(). People don't use it - * directly. - * - * @param[in] a An old object. - * @param[in] slot A pointer inside of `old`. - * @param[out] b A young object. - * @param[in] filename C's `__FILE__` of the caller function. - * @param[in] line C's `__LINE__` of the caller function. - * @return a - */ -static inline VALUE -rb_obj_write( - VALUE a, VALUE *slot, VALUE b, - RBIMPL_ATTR_MAYBE_UNUSED() - const char *filename, - RBIMPL_ATTR_MAYBE_UNUSED() - int line) -{ -#ifdef RGENGC_LOGGING_WRITE - RGENGC_LOGGING_WRITE(a, slot, b, filename, line); -#endif - - *slot = b; - - rb_obj_written(a, RUBY_Qundef /* ignore `oldv' now */, b, filename, line); - return a; -} - -#endif /* RBIMPL_RGENGC_H */ diff --git a/include/ruby/internal/scan_args.h b/include/ruby/internal/scan_args.h index cf5b18f77d..2dbc1ee7bc 100644 --- a/include/ruby/internal/scan_args.h +++ b/include/ruby/internal/scan_args.h @@ -75,7 +75,7 @@ * Pass keywords if current method is called with keywords, useful for argument * delegation */ -#define RB_PASS_CALLED_KEYWORDS rb_keyword_given_p() +#define RB_PASS_CALLED_KEYWORDS !!rb_keyword_given_p() /** @} */ @@ -100,7 +100,7 @@ RBIMPL_ATTR_NONNULL((2, 3)) * param-arg-spec := pre-arg-spec [post-arg-spec] / post-arg-spec / * pre-opt-post-arg-spec * pre-arg-spec := num-of-leading-mandatory-args - [num-of-optional-args] + * [num-of-optional-args] * post-arg-spec := sym-for-variable-length-args * [num-of-trailing-mandatory-args] * pre-opt-post-arg-spec := num-of-leading-mandatory-args num-of-optional-args diff --git a/include/ruby/internal/special_consts.h b/include/ruby/internal/special_consts.h index 38934e4da3..1e2636da48 100644 --- a/include/ruby/internal/special_consts.h +++ b/include/ruby/internal/special_consts.h @@ -76,6 +76,8 @@ #define RB_SPECIAL_CONST_P RB_SPECIAL_CONST_P #define RB_STATIC_SYM_P RB_STATIC_SYM_P #define RB_TEST RB_TEST +#define RB_UNDEF_P RB_UNDEF_P +#define RB_NIL_OR_UNDEF_P RB_NIL_OR_UNDEF_P /** @endcond */ /** special constants - i.e. non-zero and non-fixnum constants */ @@ -94,9 +96,9 @@ ruby_special_consts { RUBY_SYMBOL_FLAG, /**< Flag to denote a static symbol. */ #elif USE_FLONUM RUBY_Qfalse = 0x00, /* ...0000 0000 */ + RUBY_Qnil = 0x04, /* ...0000 0100 */ RUBY_Qtrue = 0x14, /* ...0001 0100 */ - RUBY_Qnil = 0x08, /* ...0000 1000 */ - RUBY_Qundef = 0x34, /* ...0011 0100 */ + RUBY_Qundef = 0x24, /* ...0010 0100 */ RUBY_IMMEDIATE_MASK = 0x07, /* ...0000 0111 */ RUBY_FIXNUM_FLAG = 0x01, /* ...xxxx xxx1 */ RUBY_FLONUM_MASK = 0x03, /* ...0000 0011 */ @@ -104,14 +106,14 @@ ruby_special_consts { RUBY_SYMBOL_FLAG = 0x0c, /* ...xxxx 1100 */ #else RUBY_Qfalse = 0x00, /* ...0000 0000 */ - RUBY_Qtrue = 0x02, /* ...0000 0010 */ - RUBY_Qnil = 0x04, /* ...0000 0100 */ - RUBY_Qundef = 0x06, /* ...0000 0110 */ + RUBY_Qnil = 0x02, /* ...0000 0010 */ + RUBY_Qtrue = 0x06, /* ...0000 0110 */ + RUBY_Qundef = 0x0a, /* ...0000 1010 */ RUBY_IMMEDIATE_MASK = 0x03, /* ...0000 0011 */ RUBY_FIXNUM_FLAG = 0x01, /* ...xxxx xxx1 */ RUBY_FLONUM_MASK = 0x00, /* any values ANDed with FLONUM_MASK cannot be FLONUM_FLAG */ RUBY_FLONUM_FLAG = 0x02, /* ...0000 0010 */ - RUBY_SYMBOL_FLAG = 0x0e, /* ...0000 1110 */ + RUBY_SYMBOL_FLAG = 0x0e, /* ...xxxx 1110 */ #endif RUBY_SPECIAL_SHIFT = 8 /**< Least significant 8 bits are reserved. */ @@ -136,16 +138,25 @@ static inline bool RB_TEST(VALUE obj) { /* + * if USE_FLONUM * Qfalse: ....0000 0000 - * Qnil: ....0000 1000 - * ~Qnil: ....1111 0111 + * Qnil: ....0000 0100 + * ~Qnil: ....1111 1011 * v ....xxxx xxxx * ---------------------------- - * RTEST(v) ....xxxx 0xxx + * RTEST(v) ....xxxx x0xx + * + * if ! USE_FLONUM + * Qfalse: ....0000 0000 + * Qnil: ....0000 0010 + * ~Qnil: ....1111 1101 + * v ....xxxx xxxx + * ---------------------------- + * RTEST(v) ....xxxx xx0x * * 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() @@ -168,6 +179,62 @@ RBIMPL_ATTR_CONST() RBIMPL_ATTR_CONSTEXPR(CXX11) RBIMPL_ATTR_ARTIFICIAL() /** + * Checks if the given object is undef. + * + * @param[in] obj An arbitrary ruby object. + * @retval true `obj` is ::RUBY_Qundef. + * @retval false Anything else. + */ +static inline bool +RB_UNDEF_P(VALUE obj) +{ + return obj == RUBY_Qundef; +} + +RBIMPL_ATTR_CONST() +RBIMPL_ATTR_CONSTEXPR(CXX14) +RBIMPL_ATTR_ARTIFICIAL() +/** + * Checks if the given object is nil or undef. Can be used to see if + * a keyword argument is not given or given `nil`. + * + * @param[in] obj An arbitrary ruby object. + * @retval true `obj` is ::RUBY_Qnil or ::RUBY_Qundef. + * @retval false Anything else. + */ +static inline bool +RB_NIL_OR_UNDEF_P(VALUE obj) +{ + /* + * if USE_FLONUM + * Qundef: ....0010 0100 + * Qnil: ....0000 0100 + * mask: ....1101 1111 + * common_bits: ....0000 0100 + * --------------------------------- + * Qnil & mask ....0000 0100 + * Qundef & mask ....0000 0100 + * + * if ! USE_FLONUM + * Qundef: ....0000 1010 + * Qnil: ....0000 0010 + * mask: ....1111 0111 + * common_bits: ....0000 0010 + * ---------------------------- + * Qnil & mask ....0000 0010 + * Qundef & mask ....0000 0010 + * + * NIL_OR_UNDEF_P(v) can be true only when v is Qundef or Qnil. + */ + const VALUE mask = RBIMPL_CAST((VALUE)~(RUBY_Qundef ^ RUBY_Qnil)); + const VALUE common_bits = RUBY_Qundef & RUBY_Qnil; + return (obj & mask) == common_bits; +} + +RBIMPL_ATTR_CONST() +RBIMPL_ATTR_CONSTEXPR(CXX11) +RBIMPL_ATTR_ARTIFICIAL() +/** * Checks if the given object is a so-called Fixnum. * * @param[in] obj An arbitrary ruby object. @@ -259,7 +326,7 @@ RBIMPL_ATTR_ARTIFICIAL() static inline bool RB_SPECIAL_CONST_P(VALUE obj) { - return RB_IMMEDIATE_P(obj) || ! RB_TEST(obj); + return (obj == RUBY_Qfalse) || RB_IMMEDIATE_P(obj); } RBIMPL_ATTR_CONST() @@ -279,7 +346,7 @@ RBIMPL_ATTR_CONSTEXPR(CXX11) static inline VALUE rb_special_const_p(VALUE obj) { - return RB_SPECIAL_CONST_P(obj) * RUBY_Qtrue; + return (unsigned int)RB_SPECIAL_CONST_P(obj) * RUBY_Qtrue; } /** diff --git a/include/ruby/internal/static_assert.h b/include/ruby/internal/static_assert.h index 594c2b2917..30bfd3bb79 100644 --- a/include/ruby/internal/static_assert.h +++ b/include/ruby/internal/static_assert.h @@ -23,13 +23,14 @@ #include <assert.h> #include "ruby/internal/has/extension.h" #include "ruby/internal/compiler_since.h" +#include "ruby/internal/attr/maybe_unused.h" /** @cond INTERNAL_MACRO */ #if defined(__cplusplus) && defined(__cpp_static_assert) # /* https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations */ # define RBIMPL_STATIC_ASSERT0 static_assert -#elif defined(__cplusplus) && RBIMPL_COMPILER_SINCE(MSVC, 16, 0, 0) +#elif defined(__cplusplus) && RBIMPL_COMPILER_IS(MSVC) # define RBIMPL_STATIC_ASSERT0 static_assert #elif defined(__INTEL_CXX11_MODE__) @@ -71,7 +72,7 @@ #else # define RBIMPL_STATIC_ASSERT(name, expr) \ - typedef int static_assert_ ## name ## _check[1 - 2 * !(expr)] + RBIMPL_ATTR_MAYBE_UNUSED() typedef int static_assert_ ## name ## _check[1 - 2 * !(expr)] #endif #endif /* RBIMPL_STATIC_ASSERT_H */ diff --git a/include/ruby/internal/stdbool.h b/include/ruby/internal/stdbool.h index 1ca61136ba..5d9026434b 100644 --- a/include/ruby/internal/stdbool.h +++ b/include/ruby/internal/stdbool.h @@ -27,25 +27,13 @@ #elif defined(__cplusplus) # /* bool is a keyword in C++. */ -# if defined(HAVE_STDBOOL_H) && (__cplusplus >= 201103L) -# include <cstdbool> -# endif -# # ifndef __bool_true_false_are_defined # define __bool_true_false_are_defined # endif -#elif defined(HAVE_STDBOOL_H) -# /* Take stdbool.h definition. */ +#else +# /* Take stdbool.h definition. It exists since GCC 3.0 and VS 2015. */ # include <stdbool.h> - -#elif !defined(HAVE__BOOL) -typedef unsigned char _Bool; -# /* See also http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2229.htm */ -# define bool _Bool -# define true ((_Bool)+1) -# define false ((_Bool)+0) -# define __bool_true_false_are_defined #endif #endif /* RBIMPL_STDBOOL_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/symbol.h b/include/ruby/internal/symbol.h index 869a31115c..8bfd686fbe 100644 --- a/include/ruby/internal/symbol.h +++ b/include/ruby/internal/symbol.h @@ -101,12 +101,11 @@ ID rb_intern(const char *name); ID rb_intern2(const char *name, long len); /** - * Identical to rb_intern(), except it takes an instance of ::rb_cString. + * Identical to rb_intern(), except it takes a `T_STRING` object. * * @param[in] str The name of the id. - * @pre `str` must either be an instance of ::rb_cSymbol, or an instance - * of ::rb_cString, or responds to `#to_str` method. - * @exception rb_eTypeError Can't convert `str` into ::rb_cString. + * @pre `rb_type(str)` must be `T_STRING`. + * @exception rb_eEncodingError `str` contains invalid character(s). * @exception rb_eRuntimeError Too many symbols. * @return A (possibly new) id whose value is the given str. * @note These days Ruby internally has two kinds of symbols @@ -121,10 +120,17 @@ ID rb_intern_str(VALUE str); * Retrieves the name mapped to the given id. * * @param[in] id An id to query. - * @retval NULL No such id ever existed in the history. + * @retval NULL Unknown id. * @retval otherwise A name that the id represents. * @note The return value is managed by the interpreter. Don't pass it * to free(). + * @note The underlying name can contain internal NUL bytes, so the return + * value might be a truncated representation due to the nature of C + * strings. + * @note This C string is backed by an underlying Ruby string. The Ruby + * string may move during GC compaction which would make this + * C string point to invalid memory. Do not use the return value + * of this function after a potential GC entry point. */ const char *rb_id2name(ID id); @@ -159,34 +165,40 @@ RBIMPL_ATTR_NONNULL(()) * of ::rb_cSymbol, or an instance of ::rb_cString, or responds * to `#to_str` method. * @exception rb_eTypeError Can't convert `*namep` into ::rb_cString. - * @exception rb_eEncodingError Given string is non-ASCII. + * @exception rb_eEncodingError Given string contains invalid character(s). * @retval 0 No such id ever existed in the history. * @retval otherwise The id that represents the given name. * @post The object that `*namep` points to is a converted result * object, which is always an instance of either ::rb_cSymbol * or ::rb_cString. + * @see rb_str_to_str * @see https://bugs.ruby-lang.org/issues/5072 - * - * @internal - * - * @shyouhei doesn't know why this has to raise rb_eEncodingError. */ ID rb_check_id(volatile VALUE *namep); /** - * @copydoc rb_intern_str() - * - * @internal + * Identical to rb_intern_str(), except it tries to convert the parameter object + * to an instance of ::rb_cString or its subclasses. * - * :FIXME: Can anyone tell us what is the difference between this one and - * rb_intern_str()? As far as @shyouhei reads the implementation it seems what - * rb_to_id() does is is just waste some CPU time, then call rb_intern_str(). - * He hopes he is wrong. + * @param[in] str The name of the id. + * @pre `str` must either be an instance of ::rb_cSymbol, or an instance + * of ::rb_cString, or responds to `#to_str` method. + * @exception rb_eTypeError Can't convert `str` into ::rb_cString. + * @exception rb_eEncodingError Given string contains invalid character(s). + * @exception rb_eRuntimeError Too many symbols. + * @return A (possibly new) id whose value is the given str. + * @note These days Ruby internally has two kinds of symbols + * (static/dynamic). Symbols created using this function would + * become static ones; i.e. would never be garbage collected. It + * is up to you to avoid memory leaks. Think twice before using + * it. + * @see rb_str_to_str */ ID rb_to_id(VALUE str); /** - * Identical to rb_id2name(), except it returns a Ruby's String instead of C's. + * Identical to rb_id2name(), except it returns a frozen Ruby String instead of + * a C String. * * @param[in] id An id to query. * @retval RUBY_Qfalse No such id ever existed in the history. @@ -201,14 +213,14 @@ ID rb_to_id(VALUE str); VALUE rb_id2str(ID id); /** - * Identical to rb_id2str(), except it takes an instance of ::rb_cSymbol rather - * than an ::ID. + * Obtain a frozen string representation of a symbol (not including the leading + * colon). Done without any object allocations. * - * @param[in] id An id to query. - * @retval RUBY_Qfalse No such id ever existed in the history. - * @retval otherwise An instance of ::rb_cString with the name of id. + * @param[in] symbol A ::rb_cSymbol instance to query. + * @return A frozen instance of ::rb_cString with the name of `symbol`. + * @note This does not create a permanent ::ID using the symbol. */ -VALUE rb_sym2str(VALUE id); +VALUE rb_sym2str(VALUE symbol); /** * Identical to rb_intern_str(), except it generates a dynamic symbol if @@ -237,17 +249,14 @@ RBIMPL_ATTR_NONNULL(()) * of ::rb_cSymbol, or an instance of ::rb_cString, or responds * to `#to_str` method. * @exception rb_eTypeError Can't convert `*namep` into ::rb_cString. - * @exception rb_eEncodingError Given string is non-ASCII. + * @exception rb_eEncodingError Given string contains invalid character(s). * @retval RUBY_Qnil No such id ever existed in the history. * @retval otherwise The id that represents the given name. * @post The object that `*namep` points to is a converted result * object, which is always an instance of either ::rb_cSymbol * or ::rb_cString. * @see https://bugs.ruby-lang.org/issues/5072 - * - * @internal - * - * @shyouhei doesn't know why this has to raise rb_eEncodingError. + * @see rb_str_to_str */ VALUE rb_check_symbol(volatile VALUE *namep); RBIMPL_SYMBOL_EXPORT_END() @@ -308,8 +317,9 @@ rbimpl_intern_const(ID *ptr, const char *str) } /** - * Old implementation detail of rb_intern(). - * @deprecated Does anyone use it? Preserved for backward compat. + * Returns the cached ID for the given str in var, in compiler + * independent manner. Use this instead of GCC specific rb_intern() + * when you want to cache the ID on all platforms certainly. */ #define RUBY_CONST_ID(var, str) \ do { \ @@ -318,7 +328,8 @@ rbimpl_intern_const(ID *ptr, const char *str) } while (0) #if defined(HAVE_STMT_AND_DECL_IN_EXPR) -/* __builtin_constant_p and statement expression is available +/* GCC specific shorthand for RUBY_CONST_ID(). + * __builtin_constant_p and statement expression is available * since gcc-2.7.2.3 at least. */ #define rb_intern(str) \ (RBIMPL_CONSTANT_P(str) ? \ diff --git a/include/ruby/internal/value_type.h b/include/ruby/internal/value_type.h index 977f60a009..b47d8afb97 100644 --- a/include/ruby/internal/value_type.h +++ b/include/ruby/internal/value_type.h @@ -96,10 +96,11 @@ #define RB_TYPE_P RB_TYPE_P #define Check_Type Check_Type -#if !RUBY_DEBUG -# define RBIMPL_ASSERT_TYPE(v, t) RBIMPL_ASSERT_OR_ASSUME(RB_TYPE_P((v), (t))) +#ifdef RBIMPL_VA_OPT_ARGS +# define RBIMPL_ASSERT_TYPE(v, t) \ + RBIMPL_ASSERT_OR_ASSUME(RB_TYPE_P(v, t), "actual type: %d", rb_type(v)) #else -# define RBIMPL_ASSERT_TYPE Check_Type +# define RBIMPL_ASSERT_TYPE(v, t) RBIMPL_ASSERT_OR_ASSUME(RB_TYPE_P(v, t)) #endif /** @endcond */ @@ -129,8 +130,8 @@ ruby_value_type { RUBY_T_RATIONAL = 0x0f, /**< @see struct ::RRational */ RUBY_T_NIL = 0x11, /**< @see ::RUBY_Qnil */ - RUBY_T_TRUE = 0x12, /**< @see ::RUBY_Qfalse */ - RUBY_T_FALSE = 0x13, /**< @see ::RUBY_Qtrue */ + RUBY_T_TRUE = 0x12, /**< @see ::RUBY_Qtrue */ + RUBY_T_FALSE = 0x13, /**< @see ::RUBY_Qfalse */ RUBY_T_SYMBOL = 0x14, /**< @see struct ::RSymbol */ RUBY_T_FIXNUM = 0x15, /**< Integers formerly known as Fixnums. */ RUBY_T_UNDEF = 0x16, /**< @see ::RUBY_Qundef */ @@ -443,7 +444,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/internal/warning_push.h b/include/ruby/internal/warning_push.h index f5981633f8..91d62cb00d 100644 --- a/include/ruby/internal/warning_push.h +++ b/include/ruby/internal/warning_push.h @@ -79,7 +79,7 @@ */ #define RBIMPL_WARNING_IGNORED(flag) __pragma(warning(disable: flag)) -#elif RBIMPL_COMPILER_SINCE(MSVC, 12, 0, 0) +#elif RBIMPL_COMPILER_IS(MSVC) # /* Not sure exactly when but it seems VC++ 6.0 is a version with it.*/ # define RBIMPL_WARNING_PUSH() __pragma(warning(push)) # define RBIMPL_WARNING_POP() __pragma(warning(pop)) diff --git a/include/ruby/internal/xmalloc.h b/include/ruby/internal/xmalloc.h index 57552e4e7d..132bc478ce 100644 --- a/include/ruby/internal/xmalloc.h +++ b/include/ruby/internal/xmalloc.h @@ -283,110 +283,6 @@ void ruby_xfree(void *ptr) RBIMPL_ATTR_NOEXCEPT(free(ptr)) ; -#if USE_GC_MALLOC_OBJ_INFO_DETAILS -# define ruby_xmalloc(s1) ruby_xmalloc_with_location(s1, __FILE__, __LINE__) -# define ruby_xmalloc2(s1, s2) ruby_xmalloc2_with_location(s1, s2, __FILE__, __LINE__) -# define ruby_xcalloc(s1, s2) ruby_xcalloc_with_location(s1, s2, __FILE__, __LINE__) -# define ruby_xrealloc(ptr, s1) ruby_xrealloc_with_location(ptr, s1, __FILE__, __LINE__) -# define ruby_xrealloc2(ptr, s1, s2) ruby_xrealloc2_with_location(ptr, s1, s2, __FILE__, __LINE__) - -RBIMPL_ATTR_NODISCARD() -RBIMPL_ATTR_RESTRICT() -RBIMPL_ATTR_RETURNS_NONNULL() -RBIMPL_ATTR_ALLOC_SIZE((1)) -void *ruby_xmalloc_body(size_t size) -RBIMPL_ATTR_NOEXCEPT(malloc(size)) -; - -RBIMPL_ATTR_NODISCARD() -RBIMPL_ATTR_RESTRICT() -RBIMPL_ATTR_RETURNS_NONNULL() -RBIMPL_ATTR_ALLOC_SIZE((1,2)) -void *ruby_xmalloc2_body(size_t nelems, size_t elemsiz) -RBIMPL_ATTR_NOEXCEPT(malloc(nelems * elemsiz)) -; - -RBIMPL_ATTR_NODISCARD() -RBIMPL_ATTR_RESTRICT() -RBIMPL_ATTR_RETURNS_NONNULL() -RBIMPL_ATTR_ALLOC_SIZE((1,2)) -void *ruby_xcalloc_body(size_t nelems, size_t elemsiz) -RBIMPL_ATTR_NOEXCEPT(calloc(nelems, elemsiz)) -; - -RBIMPL_ATTR_NODISCARD() -RBIMPL_ATTR_RETURNS_NONNULL() -RBIMPL_ATTR_ALLOC_SIZE((2)) -void *ruby_xrealloc_body(void *ptr, size_t newsiz) -RBIMPL_ATTR_NOEXCEPT(realloc(ptr, newsiz)) -; - -RBIMPL_ATTR_NODISCARD() -RBIMPL_ATTR_RETURNS_NONNULL() -RBIMPL_ATTR_ALLOC_SIZE((2,3)) -void *ruby_xrealloc2_body(void *ptr, size_t newelems, size_t newsiz) -RBIMPL_ATTR_NOEXCEPT(realloc(ptr, newelems * newsiz)) -; - -RUBY_EXTERN const char *ruby_malloc_info_file; -RUBY_EXTERN int ruby_malloc_info_line; - -static inline void * -ruby_xmalloc_with_location(size_t s, const char *file, int line) -{ - void *ptr; - ruby_malloc_info_file = file; - ruby_malloc_info_line = line; - ptr = ruby_xmalloc_body(s); - ruby_malloc_info_file = NULL; - return ptr; -} - -static inline void * -ruby_xmalloc2_with_location(size_t s1, size_t s2, const char *file, int line) -{ - void *ptr; - ruby_malloc_info_file = file; - ruby_malloc_info_line = line; - ptr = ruby_xmalloc2_body(s1, s2); - ruby_malloc_info_file = NULL; - return ptr; -} - -static inline void * -ruby_xcalloc_with_location(size_t s1, size_t s2, const char *file, int line) -{ - void *ptr; - ruby_malloc_info_file = file; - ruby_malloc_info_line = line; - ptr = ruby_xcalloc_body(s1, s2); - ruby_malloc_info_file = NULL; - return ptr; -} - -static inline void * -ruby_xrealloc_with_location(void *ptr, size_t s, const char *file, int line) -{ - void *rptr; - ruby_malloc_info_file = file; - ruby_malloc_info_line = line; - rptr = ruby_xrealloc_body(ptr, s); - ruby_malloc_info_file = NULL; - return rptr; -} - -static inline void * -ruby_xrealloc2_with_location(void *ptr, size_t s1, size_t s2, const char *file, int line) -{ - void *rptr; - ruby_malloc_info_file = file; - ruby_malloc_info_line = line; - rptr = ruby_xrealloc2_body(ptr, s1, s2); - ruby_malloc_info_file = NULL; - return rptr; -} -#endif - RBIMPL_SYMBOL_EXPORT_END() #endif /* RBIMPL_XMALLOC_H */ diff --git a/include/ruby/io.h b/include/ruby/io.h index b5e33c1cf9..ed0967abad 100644 --- a/include/ruby/io.h +++ b/include/ruby/io.h @@ -49,20 +49,43 @@ /** @endcond */ #include "ruby/internal/attr/const.h" +#include "ruby/internal/attr/packed_struct.h" #include "ruby/internal/attr/pure.h" #include "ruby/internal/attr/noreturn.h" #include "ruby/internal/dllexport.h" #include "ruby/internal/value.h" -#include "ruby/backward/2/attributes.h" /* PACKED_STRUCT_UNALIGNED */ // IO#wait, IO#wait_readable, IO#wait_writable, IO#wait_priority are defined by this implementation. #define RUBY_IO_WAIT_METHODS +// Used as the default timeout argument to `rb_io_wait` to use the `IO#timeout` value. +#define RUBY_IO_TIMEOUT_DEFAULT Qnil + RBIMPL_SYMBOL_EXPORT_BEGIN() struct stat; struct timeval; +#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC) +# define RUBY_USE_STATX 0 +#elif defined(HAVE_STRUCT_STATX_STX_BTIME) +# define RUBY_USE_STATX 1 +struct statx; +#else +# define RUBY_USE_STATX 0 +#endif + +#if RUBY_USE_STATX +typedef struct statx rb_io_stat_data; +#else +typedef struct stat rb_io_stat_data; +#endif + +/** + * Indicates that a timeout has occurred while performing an IO operation. + */ +RUBY_EXTERN VALUE rb_eIOTimeoutError; + /** * Type of events that an IO can wait. * @@ -70,17 +93,20 @@ struct timeval; * * This is visible from extension libraries because `io/wait` wants it. */ -typedef enum { +enum rb_io_event { RUBY_IO_READABLE = RB_WAITFD_IN, /**< `IO::READABLE` */ RUBY_IO_WRITABLE = RB_WAITFD_OUT, /**< `IO::WRITABLE` */ RUBY_IO_PRIORITY = RB_WAITFD_PRI, /**< `IO::PRIORITY` */ -} rb_io_event_t; +}; + +typedef enum rb_io_event rb_io_event_t; /** * IO buffers. This is an implementation detail of ::rb_io_t::wbuf and * ::rb_io_t::rbuf. People don't manipulate it directly. */ -PACKED_STRUCT_UNALIGNED(struct rb_io_buffer_t { +RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN() +struct rb_io_internal_buffer { /** Pointer to the underlying memory region, of at least `capa` bytes. */ char *ptr; /* off + len <= capa */ @@ -93,141 +119,38 @@ PACKED_STRUCT_UNALIGNED(struct rb_io_buffer_t { /** Designed capacity of the buffer. */ int capa; -}); +} RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END(); /** @alias{rb_io_buffer_t} */ -typedef struct rb_io_buffer_t rb_io_buffer_t; - -/** Ruby's IO, metadata and buffers. */ -typedef struct rb_io_t { - - /** The IO's Ruby level counterpart. */ - VALUE self; - - /** stdio ptr for read/write, if available. */ - FILE *stdio_file; - - /** file descriptor. */ - int fd; - - /** mode flags: FMODE_XXXs */ - int mode; - - /** child's pid (for pipes) */ - rb_pid_t pid; - - /** number of lines read */ - int lineno; - - /** pathname for file */ - VALUE pathv; - - /** finalize proc */ - void (*finalize)(struct rb_io_t*,int); - - /** Write buffer. */ - 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. - */ - rb_io_buffer_t rbuf; - +typedef struct rb_io_internal_buffer rb_io_buffer_t; + +/** Decomposed encoding flags (e.g. `"enc:enc2""`). */ +/* + * enc enc2 read action write action + * NULL NULL force_encoding(default_external) write the byte sequence of str + * e1 NULL force_encoding(e1) convert str.encoding to e1 + * e1 e2 convert from e2 to e1 convert str.encoding to e2 + */ +struct rb_io_encoding { + /** Internal encoding. */ + rb_encoding *enc; + /** External encoding. */ + rb_encoding *enc2; /** - * Duplex IO object, if set. + * Flags. * - * @see rb_io_set_write_io() + * @see enum ::ruby_econv_flag_type */ - VALUE tied_io_for_writing; - - /** Decomposed encoding flags (e.g. `"enc:enc2""`). */ - /* - * enc enc2 read action write action - * NULL NULL force_encoding(default_external) write the byte sequence of str - * e1 NULL force_encoding(e1) convert str.encoding to e1 - * e1 e2 convert from e2 to e1 convert str.encoding to e2 - */ - struct rb_io_enc_t { - /** Internal encoding. */ - rb_encoding *enc; - - /** External encoding. */ - rb_encoding *enc2; - - /** - * Flags. - * - * @see enum ::ruby_econv_flag_type - */ - int ecflags; - - /** - * Flags as Ruby hash. - * - * @internal - * - * This is set. But used from nowhere maybe? - */ - VALUE ecopts; - } encs; /**< Decomposed encoding flags. */ - - /** Encoding converter used when reading from this IO. */ - rb_econv_t *readconv; - + int ecflags; /** - * rb_io_ungetc() destination. This buffer is read before checking - * ::rb_io_t::rbuf - */ - rb_io_buffer_t cbuf; - - /** Encoding converter used when writing to this IO. */ - rb_econv_t *writeconv; - - /** - * This is, when set, an instance of ::rb_cString which holds the "common" - * encoding. Write conversion can convert strings twice... In case - * 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. - */ - VALUE writeconv_asciicompat; - - /** Whether ::rb_io_t::writeconv is already set up. */ - int writeconv_initialized; - - /** - * Value of ::rb_io_t::rb_io_enc_t::ecflags stored right before - * initialising ::rb_io_t::writeconv. - */ - int writeconv_pre_ecflags; - - /** - * Value of ::rb_io_t::rb_io_enc_t::ecopts stored right before initialising - * ::rb_io_t::writeconv. - */ - VALUE writeconv_pre_ecopts; - - /** - * This is a Ruby level mutex. It avoids multiple threads to write to an - * IO at once; helps for instance rb_io_puts() to ensure newlines right - * next to its arguments. + * Flags as Ruby hash. * - * This of course doesn't help inter-process IO interleaves, though. + * @internal + * + * This is set. But used from nowhere maybe? */ - VALUE write_lock; -} rb_io_t; - -/** @alias{rb_io_enc_t} */ -typedef struct rb_io_enc_t 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 + VALUE ecopts; +}; /** * @name Possible flags for ::rb_io_t::mode @@ -318,7 +241,16 @@ typedef struct rb_io_enc_t rb_io_enc_t; * Setting this one and #FMODE_BINMODE at the same time is a contradiction. */ #define FMODE_TEXTMODE 0x00001000 -/* #define FMODE_PREP 0x00010000 */ +/** + * This flag means that an IO object is wrapping an "external" file descriptor, + * which is owned by something outside the Ruby interpreter (usually a C extension). + * Ruby will not close this file when the IO object is garbage collected. + * If this flag is set, then IO#autoclose? is false, and vice-versa. + * + * This flag was previously called FMODE_PREP internally. + */ +#define FMODE_EXTERNAL 0x00010000 + /* #define FMODE_SIGNAL_ON_EPIPE 0x00020000 */ /** @@ -332,6 +264,166 @@ typedef struct rb_io_enc_t rb_io_enc_t; /** @} */ +enum rb_io_mode { + RUBY_IO_MODE_EXTERNAL = FMODE_EXTERNAL, + + RUBY_IO_MODE_READABLE = FMODE_READABLE, + RUBY_IO_MODE_WRITABLE = FMODE_WRITABLE, + RUBY_IO_MODE_READABLE_WRITABLE = (RUBY_IO_MODE_READABLE|RUBY_IO_MODE_WRITABLE), + + RUBY_IO_MODE_BINARY = FMODE_BINMODE, + RUBY_IO_MODE_TEXT = FMODE_TEXTMODE, + RUBY_IO_MODE_TEXT_SET_ENCODING_FROM_BOM = FMODE_SETENC_BY_BOM, + + RUBY_IO_MODE_SYNCHRONISED = FMODE_SYNC, + + RUBY_IO_MODE_TTY = FMODE_TTY, + + RUBY_IO_MODE_DUPLEX = FMODE_DUPLEX, + + RUBY_IO_MODE_APPEND = FMODE_APPEND, + RUBY_IO_MODE_CREATE = FMODE_CREATE, + RUBY_IO_MODE_EXCLUSIVE = FMODE_EXCL, + RUBY_IO_MODE_TRUNCATE = FMODE_TRUNC, +}; + +typedef enum rb_io_mode rb_io_mode_t; + +#ifndef HAVE_RB_IO_T +#define HAVE_RB_IO_T 1 +/** Ruby's IO, metadata and buffers. */ +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")) + enum rb_io_mode 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; + + /** + * Duplex IO object, if set. + * + * @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; + + /** + * This is, when set, an instance of ::rb_cString which holds the "common" + * encoding. Write conversion can convert strings twice... In case + * 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; + + /** + * This is a Ruby level mutex. It avoids multiple threads to write to an + * IO at once; helps for instance rb_io_puts() to ensure newlines right + * next to its arguments. + * + * 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; +}; +#endif + +typedef struct rb_io rb_io_t; + +/** @alias{rb_io_enc_t} */ +typedef struct rb_io_encoding rb_io_enc_t; + +/** + * Allocate a new IO object, with the given file descriptor. + */ +VALUE rb_io_open_descriptor(VALUE klass, int descriptor, int mode, VALUE path, VALUE timeout, struct rb_io_encoding *encoding); + +/** + * Returns whether or not the underlying IO is closed. + * + * @return Whether the underlying IO is closed. + */ +VALUE rb_io_closed_p(VALUE io); + /** * Queries the underlying IO pointer. * @@ -402,14 +494,14 @@ rb_io_t *rb_io_make_open_file(VALUE obj); * like this: * * ```CXX - * typedef struct rb_io_t { + * typedef struct rb_io { * FILE *f; // stdio ptr for read/write * FILE *f2; // additional ptr for rw pipes * int mode; // mode flags * int pid; // child's pid (for pipes) * int lineno; // number of lines read * char *path; // pathname for file - * void (*finalize) _((struct rb_io_t*,int)); // finalize proc + * void (*finalize) _((struct rb_io*,int)); // finalize proc * } rb_io_t; *``` * @@ -473,7 +565,7 @@ FILE *rb_fdopen(int fd, const char *modestr); * * rb_io_modestr_fmode() is not a pure function because it raises. */ -int rb_io_modestr_fmode(const char *modestr); +enum rb_io_mode rb_io_modestr_fmode(const char *modestr); /** * Identical to rb_io_modestr_fmode(), except it returns a mixture of `O_` @@ -691,6 +783,12 @@ VALUE rb_io_set_write_io(VALUE io, VALUE w); void rb_io_set_nonblock(rb_io_t *fptr); /** + * Returns the path for the given IO. + * + */ +VALUE rb_io_path(VALUE io); + +/** * Returns an integer representing the numeric file descriptor for * <em>io</em>. * @@ -700,6 +798,12 @@ void rb_io_set_nonblock(rb_io_t *fptr); int rb_io_descriptor(VALUE io); /** + * Get the mode of the IO. + * + */ +int rb_io_mode(VALUE io); + +/** * This function breaks down the option hash that `IO#initialize` takes into * components. This is an implementation detail of rb_io_extract_modeenc() * today. People prefer that API instead. @@ -716,7 +820,7 @@ int rb_io_descriptor(VALUE io); * @post `enc2_p` is the specified external encoding. * @post `fmode_p` is the specified set of `FMODE_` modes. */ -int rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2_p, int *fmode_p); +int rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2_p, enum rb_io_mode *fmode_p); /** * This function can be seen as an extended version of @@ -785,7 +889,7 @@ int rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding ** * ) -> void * ``` */ -void rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash, int *oflags_p, int *fmode_p, rb_io_enc_t *convconfig_p); +void rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash, int *oflags_p, enum rb_io_mode *fmode_p, rb_io_enc_t *convconfig_p); /* :TODO: can this function be __attribute__((warn_unused_result)) or not? */ /** @@ -846,13 +950,37 @@ int rb_io_wait_writable(int fd); int rb_wait_for_single_fd(int fd, int events, struct timeval *tv); /** + * Get the timeout associated with the specified io object. + * + * @param[in] io An IO object. + * @retval RUBY_Qnil There is no associated timeout. + * @retval Otherwise The timeout value. + */ +VALUE rb_io_timeout(VALUE io); + +/** + * Set the timeout associated with the specified io object. This timeout is + * used as a best effort timeout to prevent operations from blocking forever. + * + * @param[in] io An IO object. + * @param[in] timeout A timeout value. Must respond to #to_f. + * @ + */ +VALUE rb_io_set_timeout(VALUE io, VALUE timeout); + +/** * Blocks until the passed IO is ready for the passed events. The "events" * here is a Ruby level integer, which is an OR-ed value of `IO::READABLE`, * `IO::WRITable`, and `IO::PRIORITY`. * + * If timeout is `Qnil`, it will use the default timeout as given by + * `rb_io_timeout(io)`. + * * @param[in] io An IO object to wait. * @param[in] events See above. * @param[in] timeout Time, or numeric seconds since UNIX epoch. + * If Qnil, use the default timeout. If Qfalse + * or Qundef, wait forever. * @exception rb_eIOError `io` is not open. * @exception rb_eRangeError `timeout` is out of range. * @exception rb_eSystemCallError `select(2)` failed for some reason. @@ -876,6 +1004,9 @@ VALUE rb_io_wait(VALUE io, VALUE events, VALUE timeout); * } * ``` * + * On timeout, ::RUBY_Qfalse is returned. Unless you are specifically handling + * the timeouts, you should typically raise ::rb_eIOTimeoutError in this case. + * * @param[in] error System errno. * @param[in] io An IO object to wait. * @param[in] events An integer set of interests. @@ -884,19 +1015,19 @@ VALUE rb_io_wait(VALUE io, VALUE events, VALUE timeout); * @exception rb_eRangeError `timeout` is out of range. * @exception rb_eSystemCallError `select(2)` failed for some reason. * @retval RUBY_Qfalse Operation timed out. + * @retval RUBY_Qnil Operation failed for some other reason (errno). * @retval Otherwise Actual events reached. * - * @internal - * - * This function to return ::RUBY_Qfalse on timeout could be unintended. It - * seems timeout feature has some rough edge. */ VALUE rb_io_maybe_wait(int error, VALUE io, VALUE events, VALUE timeout); /** * Blocks until the passed IO is ready for reading, if that makes sense for the - * passed errno. This is a special case of rb_io_maybe_wait() that only - * concerns for reading. + * passed errno. This is a special case of rb_io_maybe_wait() that is + * only concerned with reading and handles the timeout. + * + * If you do not want the default timeout handling, consider using + * ::rb_io_maybe_wait directly. * * @param[in] error System errno. * @param[in] io An IO object to wait. @@ -904,20 +1035,18 @@ VALUE rb_io_maybe_wait(int error, VALUE io, VALUE events, VALUE timeout); * @exception rb_eIOError `io` is not open. * @exception rb_eRangeError `timeout` is out of range. * @exception rb_eSystemCallError `select(2)` failed for some reason. - * @exception rb_eTypeError Operation timed out. - * @return Always returns ::RUBY_IO_READABLE. - * - * @internal - * - * Because rb_io_maybe_wait() returns ::RUBY_Qfalse on timeout, this function - * fails to convert that value to `int`, and raises ::rb_eTypeError. + * @exception rb_eIOTimeoutError The wait operation timed out. + * @retval Otherwise Always returns ::RUBY_IO_READABLE. */ int rb_io_maybe_wait_readable(int error, VALUE io, VALUE timeout); /** * Blocks until the passed IO is ready for writing, if that makes sense for the - * passed errno. This is a special case of rb_io_maybe_wait() that only - * concernsfor writing. + * passed errno. This is a special case of rb_io_maybe_wait() that is + * only concerned with writing, and handles the timeout. + * + * If you do not want the default timeout handling, consider using + * ::rb_io_maybe_wait directly. * * @param[in] error System errno. * @param[in] io An IO object to wait. @@ -925,13 +1054,8 @@ int rb_io_maybe_wait_readable(int error, VALUE io, VALUE timeout); * @exception rb_eIOError `io` is not open. * @exception rb_eRangeError `timeout` is out of range. * @exception rb_eSystemCallError `select(2)` failed for some reason. - * @exception rb_eTypeError Operation timed out. - * @return Always returns ::RUBY_IO_WRITABLE. - * - * @internal - * - * Because rb_io_maybe_wait() returns ::RUBY_Qfalse on timeout, this function - * fails to convert that value to `int`, and raises ::rb_eTypeError. + * @exception rb_eIOTimeoutError The wait operation timed out. + * @retval Otherwise Always returns ::RUBY_IO_WRITABLE. */ int rb_io_maybe_wait_writable(int error, VALUE io, VALUE timeout); @@ -989,6 +1113,18 @@ int rb_io_read_pending(rb_io_t *fptr); */ VALUE rb_stat_new(const struct stat *st); +#if RUBY_USE_STATX +/** + * Constructs an instance of ::rb_cStat from the passed information. + * + * @param[in] st A stat. + * @return Allocated new instance of ::rb_cStat. + */ +VALUE rb_statx_new(const rb_io_stat_data *st); +#else +# define rb_statx_new rb_stat_new +#endif + /* gc.c */ RBIMPL_SYMBOL_EXPORT_END() diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h index 907fec20bb..e4d98bf051 100644 --- a/include/ruby/io/buffer.h +++ b/include/ruby/io/buffer.h @@ -1,5 +1,5 @@ -#ifndef RUBY_IO_BUFFER_T -#define RUBY_IO_BUFFER_T 1 +#ifndef RUBY_IO_BUFFER_H +#define RUBY_IO_BUFFER_H /** * @file * @author Samuel Williams @@ -21,10 +21,20 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() // WARNING: This entire interface is experimental and may change in the future! #define RB_IO_BUFFER_EXPERIMENTAL 1 +#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. @@ -35,6 +45,9 @@ enum rb_io_buffer_flags { // A non-private mapping is marked as external. RB_IO_BUFFER_MAPPED = 4, + // A mapped buffer that is also shared. + RB_IO_BUFFER_SHARED = 8, + // The buffer is locked and cannot be resized. // More specifically, it means we can't change the base address or size. // A buffer is typically locked before a system call that uses the data. @@ -44,35 +57,41 @@ 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 }; VALUE rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags); -VALUE rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags flags); +VALUE rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags); VALUE rb_io_buffer_lock(VALUE self); VALUE rb_io_buffer_unlock(VALUE self); 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); @@ -81,11 +100,11 @@ void rb_io_buffer_resize(VALUE self, size_t size); void rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length); // The length is the minimum required length. -VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t length); -VALUE rb_io_buffer_pread(VALUE self, VALUE io, size_t length, off_t offset); -VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t length); -VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, off_t offset); +VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset); +VALUE rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset); +VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset); +VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset); RBIMPL_SYMBOL_EXPORT_END() -#endif /* RUBY_IO_BUFFER_T */ +#endif /* RUBY_IO_BUFFER_H */ diff --git a/include/ruby/memory_view.h b/include/ruby/memory_view.h index 83931038a0..42309d5afc 100644 --- a/include/ruby/memory_view.h +++ b/include/ruby/memory_view.h @@ -16,7 +16,7 @@ # include <stddef.h> /* size_t */ #endif -#if HAVE_SYS_TYPES_H +#ifdef HAVE_SYS_TYPES_H # include <sys/types.h> /* ssize_t */ #endif @@ -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 a7ef59c7c8..9dcddee829 100644 --- a/include/ruby/onigmo.h +++ b/include/ruby/onigmo.h @@ -4,8 +4,8 @@ onigmo.h - Onigmo (Oniguruma-mod) (regular expression library) **********************************************************************/ /*- - * Copyright (c) 2002-2009 K.Kosako <sndgk393 AT ybb DOT ne DOT jp> - * Copyright (c) 2011-2017 K.Takata <kentkt AT csc DOT jp> + * Copyright (c) 2002-2016 K.Kosako <sndgk393 AT ybb DOT ne DOT jp> + * Copyright (c) 2011-2019 K.Takata <kentkt AT csc DOT jp> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,8 +38,8 @@ extern "C" { #endif #define ONIGMO_VERSION_MAJOR 6 -#define ONIGMO_VERSION_MINOR 1 -#define ONIGMO_VERSION_TEENY 3 +#define ONIGMO_VERSION_MINOR 2 +#define ONIGMO_VERSION_TEENY 0 #ifndef ONIG_EXTERN # ifdef RUBY_EXTERN @@ -356,9 +356,9 @@ int onigenc_ascii_only_case_map(OnigCaseFoldType* flagP, const OnigUChar** pp, c #define ONIGENC_PRECISE_MBC_ENC_LEN(enc,p,e) (enc)->precise_mbc_enc_len(p,e,enc) ONIG_EXTERN -int onigenc_mbclen_approximate(const OnigUChar* p,const OnigUChar* e, const struct OnigEncodingTypeST* enc); +int onigenc_mbclen(const OnigUChar* p,const OnigUChar* e, const struct OnigEncodingTypeST* enc); -#define ONIGENC_MBC_ENC_LEN(enc,p,e) onigenc_mbclen_approximate(p,e,enc) +#define ONIGENC_MBC_ENC_LEN(enc,p,e) onigenc_mbclen(p,e,enc) #define ONIGENC_MBC_MAXLEN(enc) ((enc)->max_enc_len) #define ONIGENC_MBC_MAXLEN_DIST(enc) ONIGENC_MBC_MAXLEN(enc) #define ONIGENC_MBC_MINLEN(enc) ((enc)->min_enc_len) @@ -636,6 +636,7 @@ ONIG_EXTERN const OnigSyntaxType* OnigDefaultSyntax; #define ONIGERR_PARSE_DEPTH_LIMIT_OVER -16 #define ONIGERR_DEFAULT_ENCODING_IS_NOT_SET -21 #define ONIGERR_SPECIFIED_ENCODING_CANT_CONVERT_TO_WIDE_CHAR -22 +#define ONIGERR_TIMEOUT -23 /* general error */ #define ONIGERR_INVALID_ARGUMENT -30 /* syntax error */ @@ -788,8 +789,8 @@ typedef struct re_pattern_buffer { unsigned char *exact; unsigned char *exact_end; unsigned char map[ONIG_CHAR_TABLE_SIZE]; /* used as BM skip or char-map */ - int *int_map; /* BM skip for exact_len > 255 */ - int *int_map_backward; /* BM skip for backward search */ + int *reserved1; + int *reserved2; OnigDistance dmin; /* min-distance of exact or map */ OnigDistance dmax; /* max-distance of exact or map */ @@ -844,6 +845,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); @@ -852,6 +855,8 @@ OnigPosition onig_search_gpos(OnigRegex, const OnigUChar* str, const OnigUChar* ONIG_EXTERN OnigPosition onig_match(OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* at, OnigRegion* region, OnigOptionType option); ONIG_EXTERN +int onig_check_linear_time(OnigRegex reg); +ONIG_EXTERN OnigRegion* onig_region_new(void); ONIG_EXTERN void onig_region_init(OnigRegion* region); diff --git a/include/ruby/ractor.h b/include/ruby/ractor.h index 7811616f6d..8cfca21621 100644 --- a/include/ruby/ractor.h +++ b/include/ruby/ractor.h @@ -217,7 +217,7 @@ VALUE rb_ractor_make_shareable(VALUE obj); * * @param[in] obj Arbitrary ruby object to duplicate. * @exception rb_eRactorError Ractors cannot share `obj` by nature. - * @return A deep copy of `obj` which is sharable among Ractors. + * @return A deep copy of `obj` which is shareable among Ractors. */ VALUE rb_ractor_make_shareable_copy(VALUE obj); @@ -261,4 +261,18 @@ rb_ractor_shareable_p(VALUE obj) } } +// TODO: optimize on interpreter core +#ifndef RB_OBJ_SET_SHAREABLE +VALUE rb_obj_set_shareable(VALUE obj); // ractor.c +#define RB_OBJ_SET_SHAREABLE(obj) rb_obj_set_shareable(obj) +#endif + +static inline VALUE +RB_OBJ_SET_FROZEN_SHAREABLE(VALUE obj) +{ + RB_OBJ_FREEZE(obj); + RB_OBJ_SET_SHAREABLE(obj); + return obj; +} + #endif /* RUBY_RACTOR_H */ diff --git a/include/ruby/random.h b/include/ruby/random.h index 657b37f034..740be6bdad 100644 --- a/include/ruby/random.h +++ b/include/ruby/random.h @@ -11,11 +11,31 @@ * * 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" +/* + * version + * 0: before versioning; deprecated + * 1: added version, flags and init_32bit function + */ +#define RUBY_RANDOM_INTERFACE_VERSION_MAJOR 1 +#define RUBY_RANDOM_INTERFACE_VERSION_MINOR 0 + +#define RUBY_RANDOM_PASTE_VERSION_SUFFIX(x, y, z) x##_##y##_##z +#define RUBY_RANDOM_WITH_VERSION_SUFFIX(name, major, minor) \ + RUBY_RANDOM_PASTE_VERSION_SUFFIX(name, major, minor) +#define rb_random_data_type \ + RUBY_RANDOM_WITH_VERSION_SUFFIX(rb_random_data_type, \ + RUBY_RANDOM_INTERFACE_VERSION_MAJOR, \ + RUBY_RANDOM_INTERFACE_VERSION_MINOR) +#define RUBY_RANDOM_INTERFACE_VERSION_INITIALIZER \ + {RUBY_RANDOM_INTERFACE_VERSION_MAJOR, RUBY_RANDOM_INTERFACE_VERSION_MINOR} +#define RUBY_RANDOM_INTERFACE_VERSION_MAJOR_MAX 0xff +#define RUBY_RANDOM_INTERFACE_VERSION_MINOR_MAX 0xff + RBIMPL_SYMBOL_EXPORT_BEGIN() /** @@ -48,6 +68,17 @@ typedef void rb_random_init_func(rb_random_t *rng, const uint32_t *buf, size_t l RBIMPL_ATTR_NONNULL(()) /** + * This is the type of functions called when your random object is initialised. + * Passed data is the seed integer. + * + * @param[out] rng Your random struct to fill in. + * @param[in] data Seed, single word. + * @post `rng` is initialised using the passed seeds. + */ +typedef void rb_random_init_int32_func(rb_random_t *rng, uint32_t data); + +RBIMPL_ATTR_NONNULL(()) +/** * This is the type of functions called from your object's `#rand` method. * * @param[out] rng Your random struct to extract an integer from. @@ -84,9 +115,24 @@ typedef struct { /** Number of bits of seed numbers. */ size_t default_seed_bits; - /** Initialiser function. */ + /** + * Major/minor versions of this interface + */ + struct { + uint8_t major, minor; + } version; + + /** + * Reserved flags + */ + uint16_t flags; + + /** Function to initialize from uint32_t array. */ rb_random_init_func *init; + /** Function to initialize from single uint32_t. */ + rb_random_init_int32_func *init_int32; + /** Function to obtain a random integer. */ rb_random_get_int32_func *get_int32; @@ -130,11 +176,12 @@ typedef struct { } rb_random_interface_t; /** - * This utility macro defines 3 functions named prefix_init, prefix_get_int32, - * prefix_get_bytes. + * This utility macro defines 4 functions named prefix_init, prefix_init_int32, + * prefix_get_int32, prefix_get_bytes. */ #define RB_RANDOM_INTERFACE_DECLARE(prefix) \ static void prefix##_init(rb_random_t *, const uint32_t *, size_t); \ + static void prefix##_init_int32(rb_random_t *, uint32_t); \ static unsigned int prefix##_get_int32(rb_random_t *); \ static void prefix##_get_bytes(rb_random_t *, void *, size_t) @@ -161,7 +208,9 @@ typedef struct { * ``` */ #define RB_RANDOM_INTERFACE_DEFINE(prefix) \ + RUBY_RANDOM_INTERFACE_VERSION_INITIALIZER, 0, \ prefix##_init, \ + prefix##_init_int32, \ prefix##_get_int32, \ prefix##_get_bytes @@ -173,6 +222,12 @@ typedef struct { RB_RANDOM_INTERFACE_DEFINE(prefix), \ prefix##_get_real +#define RB_RANDOM_DEFINE_INIT_INT32_FUNC(prefix) \ + static void prefix##_init_int32(rb_random_t *rnd, uint32_t data) \ + { \ + prefix##_init(rnd, &data, 1); \ + } + #if defined _WIN32 && !defined __CYGWIN__ typedef rb_data_type_t rb_random_data_type_t; # define RB_RANDOM_PARENT 0 @@ -189,7 +244,7 @@ typedef const rb_data_type_t rb_random_data_type_t; * 0, RB_RANDOM_INTERFACE_DEFINE(your), * }; * - * static inline constexpr your_prng = { + * static inline constexpr rb_random_data_type_t your_prng_type = { * "your PRNG", * { rb_random_mark, }, * RB_RANDOM_PARENT, // <<-- HERE @@ -277,7 +332,9 @@ RBIMPL_ATTR_PURE_UNLESS_DEBUG() static inline const rb_random_interface_t * rb_rand_if(VALUE obj) { + RBIMPL_ASSERT_OR_ASSUME(RB_TYPE_P(obj, T_DATA)); RBIMPL_ASSERT_OR_ASSUME(RTYPEDDATA_P(obj)); + RUBY_ASSERT(rb_typeddata_is_kind_of(obj, &rb_random_data_type)); const struct rb_data_type_struct *t = RTYPEDDATA_TYPE(obj); const void *ret = t->data; return RBIMPL_CAST((const rb_random_interface_t *)ret); 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 108127a93c..ca794bcaeb 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -43,7 +43,6 @@ #include "ruby/internal/method.h" #include "ruby/internal/module.h" #include "ruby/internal/newobj.h" -#include "ruby/internal/rgengc.h" #include "ruby/internal/scan_args.h" #include "ruby/internal/special_consts.h" #include "ruby/internal/symbol.h" @@ -98,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)) @@ -108,7 +109,7 @@ VALUE rb_get_path_no_checksafe(VALUE); # define rb_varargs_argc_check_runtime(argc, vargc) \ (((argc) <= (vargc)) ? (argc) : \ (rb_fatal("argc(%d) exceeds actual arguments(%d)", \ - argc, vargc), 0)) + argc, vargc), 0)) # define rb_varargs_argc_valid_p(argc, vargc) \ ((argc) == 0 ? (vargc) <= 1 : /* [ruby-core:85266] [Bug #14425] */ \ (argc) == (vargc)) @@ -117,16 +118,16 @@ VALUE rb_get_path_no_checksafe(VALUE); ERRORFUNC((" argument length doesn't match"), int rb_varargs_bad_length(int,int)); # else # define rb_varargs_bad_length(argc, vargc) \ - ((argc)/rb_varargs_argc_valid_p(argc, vargc)) + ((argc)/rb_varargs_argc_valid_p(argc, vargc)) # endif # define rb_varargs_argc_check(argc, vargc) \ __builtin_choose_expr(__builtin_constant_p(argc), \ - (rb_varargs_argc_valid_p(argc, vargc) ? (argc) : \ - rb_varargs_bad_length(argc, vargc)), \ - rb_varargs_argc_check_runtime(argc, vargc)) + (rb_varargs_argc_valid_p(argc, vargc) ? (argc) : \ + rb_varargs_bad_length(argc, vargc)), \ + rb_varargs_argc_check_runtime(argc, vargc)) # else # define rb_varargs_argc_check(argc, vargc) \ - rb_varargs_argc_check_runtime(argc, vargc) + rb_varargs_argc_check_runtime(argc, vargc) # endif #endif /** @endcond */ @@ -271,30 +272,148 @@ 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 */ #elif defined(__GNUC__) && defined(HAVE_VA_ARGS_MACRO) && defined(__OPTIMIZE__) # define rb_yield_values(argc, ...) \ __extension__({ \ - const int rb_yield_values_argc = (argc); \ - const VALUE rb_yield_values_args[] = {__VA_ARGS__}; \ - const int rb_yield_values_nargs = \ - (int)(sizeof(rb_yield_values_args) / sizeof(VALUE)); \ - rb_yield_values2( \ - rb_varargs_argc_check(rb_yield_values_argc, rb_yield_values_nargs), \ - rb_yield_values_nargs ? rb_yield_values_args : NULL); \ + const int rb_yield_values_argc = (argc); \ + const VALUE rb_yield_values_args[] = {__VA_ARGS__}; \ + const int rb_yield_values_nargs = \ + (int)(sizeof(rb_yield_values_args) / sizeof(VALUE)); \ + rb_yield_values2( \ + rb_varargs_argc_check(rb_yield_values_argc, rb_yield_values_nargs), \ + rb_yield_values_nargs ? rb_yield_values_args : NULL); \ }) # define rb_funcall(recv, mid, argc, ...) \ __extension__({ \ - const int rb_funcall_argc = (argc); \ - const VALUE rb_funcall_args[] = {__VA_ARGS__}; \ - const int rb_funcall_nargs = \ - (int)(sizeof(rb_funcall_args) / sizeof(VALUE)); \ + const int rb_funcall_argc = (argc); \ + const VALUE rb_funcall_args[] = {__VA_ARGS__}; \ + const int rb_funcall_nargs = \ + (int)(sizeof(rb_funcall_args) / sizeof(VALUE)); \ rb_funcallv(recv, mid, \ - rb_varargs_argc_check(rb_funcall_argc, rb_funcall_nargs), \ - rb_funcall_nargs ? rb_funcall_args : NULL); \ + rb_varargs_argc_check(rb_funcall_argc, rb_funcall_nargs), \ + rb_funcall_nargs ? rb_funcall_args : NULL); \ }) #endif /** @endcond */ @@ -307,6 +426,21 @@ __extension__({ \ # include "ruby/backward.h" #endif +#ifndef RUBY__ASAN_DEFAULT_OPTIONS +# define RUBY__ASAN_DEFAULT_OPTIONS +#endif + +#define RUBY_GLOBAL_SETUP \ + RUBY__ASAN_DEFAULT_OPTIONS \ + /* RUBY_GLOBAL_SETUP end */ + +#if defined(__wasm__) && !defined(__EMSCRIPTEN__) +int rb_wasm_rt_start(int (*)(int, char **), int, char **); +# define ruby_start_main rb_wasm_rt_start +#else +# define ruby_start_main(main, argc, argv) main(argc, argv) +#endif + RBIMPL_SYMBOL_EXPORT_END() #endif /* RUBY_RUBY_H */ diff --git a/include/ruby/st.h b/include/ruby/st.h index 1e4bb80686..f35ab43603 100644 --- a/include/ruby/st.h +++ b/include/ruby/st.h @@ -98,6 +98,8 @@ struct st_table { enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE, ST_CHECK, ST_REPLACE}; +size_t rb_st_table_size(const struct st_table *tbl); +#define st_table_size rb_st_table_size 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); diff --git a/include/ruby/thread.h b/include/ruby/thread.h index 299aa2d62e..2fa01229e2 100644 --- a/include/ruby/thread.h +++ b/include/ruby/thread.h @@ -59,6 +59,19 @@ */ #define RB_NOGVL_UBF_ASYNC_SAFE (0x2) +/** + * Passing this flag to rb_nogvl() indicates that the passed function + * is safe to offload to a background thread or work pool. In other words, the + * function is safe to run using a fiber scheduler's `blocking_operation_wait`. + * hook. + * + * If your function depends on thread-local storage, or thread-specific data + * operations/data structures, you should not set this flag, as + * these operations may behave differently (or fail) when run in a different + * thread/context (e.g. unlocking a mutex). + */ +#define RB_NOGVL_OFFLOAD_SAFE (0x4) + /** @} */ RBIMPL_SYMBOL_EXPORT_BEGIN() @@ -128,7 +141,7 @@ RBIMPL_ATTR_NONNULL((1)) * your code to see if it is actually worth releasing the GVL. */ void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1, - rb_unblock_function_t *ubf, void *data2); + rb_unblock_function_t *ubf, void *data2); RBIMPL_ATTR_NONNULL((1)) /** @@ -152,7 +165,7 @@ RBIMPL_ATTR_NONNULL((1)) * @return What `func` returned, or 0 in case `func` did not return. */ void *rb_thread_call_without_gvl2(void *(*func)(void *), void *data1, - rb_unblock_function_t *ubf, void *data2); + rb_unblock_function_t *ubf, void *data2); /* * XXX: unstable/unapproved - out-of-tree code should NOT not depend @@ -190,14 +203,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 +269,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 +286,60 @@ 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); + +/** + * Whether the current thread is holding the GVL. + * + * @return true if the current thread is holding the GVL, false otherwise. + */ +int ruby_thread_has_gvl_p(void); + RBIMPL_SYMBOL_EXPORT_END() #endif /* RUBY_THREAD_H */ diff --git a/include/ruby/thread_native.h b/include/ruby/thread_native.h index c23b15e133..8217a67514 100644 --- a/include/ruby/thread_native.h +++ b/include/ruby/thread_native.h @@ -28,6 +28,11 @@ typedef union rb_thread_lock_union { CRITICAL_SECTION crit; } rb_nativethread_lock_t; +struct rb_thread_cond_struct { + struct cond_event_entry *next; + struct cond_event_entry *prev; +}; + typedef struct rb_thread_cond_struct rb_nativethread_cond_t; #elif defined(HAVE_PTHREAD_H) diff --git a/include/ruby/util.h b/include/ruby/util.h index b2bc1a09f6..12e69c4b80 100644 --- a/include/ruby/util.h +++ b/include/ruby/util.h @@ -19,7 +19,7 @@ # include <stddef.h> /* size_t */ #endif -#if HAVE_SYS_TYPES_H +#ifdef HAVE_SYS_TYPES_H # include <sys/types.h> /* ssize_t */ #endif @@ -33,9 +33,20 @@ 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 */ +#define DECIMAL_SIZE_OF_BYTES(n) DECIMAL_SIZE_OF_BITS((n) * CHAR_BIT) + +/** + * An approximation of decimal representation size. `expr` may be a + * type name + */ +#define DECIMAL_SIZE_OF(expr) DECIMAL_SIZE_OF_BYTES(sizeof(expr)) + /** * Character to number mapping like `'a'` -> `10`, `'b'` -> `11` etc. For * punctuation etc., the value is -1. "36" terminology comes from the fact @@ -124,7 +135,7 @@ unsigned long ruby_scan_hex(const char *str, size_t len, size_t *ret); # define ruby_qsort qsort_r #else void ruby_qsort(void *, const size_t, const size_t, - int (*)(const void *, const void *, void *), void *); + int (*)(const void *, const void *, void *), void *); #endif RBIMPL_ATTR_NONNULL((1)) diff --git a/include/ruby/version.h b/include/ruby/version.h index 18b3abc8d7..5bb381cea2 100644 --- a/include/ruby/version.h +++ b/include/ruby/version.h @@ -61,13 +61,13 @@ * doesn't mean a total rewrite. Practically when it comes to API versioning, * major and minor version changes are equally catastrophic. */ -#define RUBY_API_VERSION_MAJOR 3 +#define RUBY_API_VERSION_MAJOR 4 /** * 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 2 +#define RUBY_API_VERSION_MINOR 1 /** * 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 a3f466d627..ae11a61481 100644 --- a/include/ruby/win32.h +++ b/include/ruby/win32.h @@ -19,11 +19,6 @@ RUBY_SYMBOL_EXPORT_BEGIN */ /* - * Definitions for NT port of Perl - */ - - -/* * Ok now we can include the normal include files. */ @@ -35,14 +30,10 @@ extern "C++" { /* template without extern "C++" */ #if !defined(_WIN64) && !defined(WIN32) #define WIN32 #endif -#if defined(_MSC_VER) && _MSC_VER <= 1200 -#include <windows.h> -#endif #include <winsock2.h> #include <ws2tcpip.h> -#if !defined(_MSC_VER) || _MSC_VER >= 1400 +#include <mswsock.h> #include <iphlpapi.h> -#endif #if defined(__cplusplus) && defined(_MSC_VER) } #endif @@ -63,13 +54,7 @@ extern "C++" { /* template without extern "C++" */ #include <direct.h> #include <process.h> #include <time.h> -#if defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER == 1200 -extern "C++" { /* template without extern "C++" */ -#endif #include <math.h> -#if defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER == 1200 -} -#endif #include <signal.h> #include <sys/stat.h> #include <sys/types.h> @@ -130,8 +115,30 @@ typedef unsigned int uintptr_t; #define O_SHARE_DELETE 0x20000000 /* for rb_w32_open(), rb_w32_wopen() */ typedef int clockid_t; -#define CLOCK_REALTIME 0 -#define CLOCK_MONOTONIC 1 + +/* + * Since we use our versions in win32/win32.c, not to depend on yet + * another DLL, prefix our versions not to conflict with inline + * versions provided in time.h. + */ +#define clock_gettime rb_w32_clock_gettime +#define clock_getres rb_w32_clock_getres + +#ifndef CLOCK_REALTIME +# define CLOCK_REALTIME 0 +#endif +#ifndef CLOCK_MONOTONIC +# define CLOCK_MONOTONIC 1 +#endif +#ifndef CLOCK_PROCESS_CPUTIME_ID +# define CLOCK_PROCESS_CPUTIME_ID 2 +#endif +#ifndef CLOCK_THREAD_CPUTIME_ID +# define CLOCK_THREAD_CPUTIME_ID 3 +#endif +#ifndef CLOCK_REALTIME_COARSE +# define CLOCK_REALTIME_COARSE 4 +#endif #undef utime #undef lseek @@ -152,8 +159,10 @@ typedef int clockid_t; #define open rb_w32_uopen #define close(h) rb_w32_close(h) #define fclose(f) rb_w32_fclose(f) -#define read(f, b, s) rb_w32_read(f, b, s) -#define write(f, b, s) rb_w32_write(f, b, s) +#define read(f, b, s) rb_w32_read(f, b, s) +#define write(f, b, s) rb_w32_write(f, b, s) +#define pread(f, b, s, o) rb_w32_pread(f, b, s, o) +#define pwrite(f, b, s, o) rb_w32_pwrite(f, b, s, o) #define getpid() rb_w32_getpid() #undef HAVE_GETPPID #define HAVE_GETPPID 1 @@ -195,7 +204,6 @@ struct stati128 { long st_ctimensec; }; -#define off_t __int64 #define stat stati128 #undef SIZEOF_STRUCT_STAT_ST_INO #define SIZEOF_STRUCT_STAT_ST_INO sizeof(unsigned __int64) @@ -254,7 +262,6 @@ struct ifaddrs { #endif extern void rb_w32_sysinit(int *, char ***); -extern DWORD rb_w32_osid(void); extern int flock(int fd, int oper); extern int rb_w32_io_cancelable_p(int); extern int rb_w32_is_socket(int); @@ -298,12 +305,15 @@ extern void rb_w32_free_environ(char **); extern int rb_w32_map_errno(DWORD); extern const char *WSAAPI rb_w32_inet_ntop(int,const void *,char *,size_t); extern int WSAAPI rb_w32_inet_pton(int,const char *,void *); -extern DWORD rb_w32_osver(void); + +RBIMPL_ATTR_DEPRECATED(("as Windows 9x is not supported already")) +static inline DWORD rb_w32_osid(void) {return VER_PLATFORM_WIN32_NT;} +RBIMPL_ATTR_DEPRECATED(("by Windows Version Helper APIs")) +extern DWORD rb_w32_osver(void); extern int rb_w32_uchown(const char *, int, int); extern int rb_w32_ulink(const char *, const char *); extern ssize_t rb_w32_ureadlink(const char *, char *, size_t); -extern ssize_t rb_w32_wreadlink(const WCHAR *, WCHAR *, size_t); extern int rb_w32_usymlink(const char *src, const char *link); extern int gettimeofday(struct timeval *, struct timezone *); extern int clock_gettime(clockid_t, struct timespec *); @@ -335,7 +345,7 @@ extern int rb_w32_dup2(int, int); #include <float.h> -#if defined _MSC_VER && _MSC_VER >= 1800 && defined INFINITY +#if defined _MSC_VER && defined INFINITY #pragma warning(push) #pragma warning(disable:4756) static inline float @@ -394,6 +404,7 @@ scalb(double a, long b) #endif #define S_IFLNK 0xa000 +#define S_IFSOCK 0xc000 /* * define this so we can do inplace editing @@ -401,9 +412,9 @@ scalb(double a, long b) #define SUFFIX -extern int rb_w32_ftruncate(int fd, off_t length); -extern int rb_w32_truncate(const char *path, off_t length); -extern int rb_w32_utruncate(const char *path, off_t length); +extern int rb_w32_ftruncate(int fd, rb_off_t length); +extern int rb_w32_truncate(const char *path, rb_off_t length); +extern int rb_w32_utruncate(const char *path, rb_off_t length); #undef HAVE_FTRUNCATE #define HAVE_FTRUNCATE 1 @@ -417,11 +428,6 @@ extern int rb_w32_utruncate(const char *path, off_t length); #define HAVE_TRUNCATE 1 #define truncate rb_w32_utruncate -#if defined(_MSC_VER) && _MSC_VER >= 1400 && _MSC_VER < 1800 -#define strtoll _strtoi64 -#define strtoull _strtoui64 -#endif - /* * stubs */ @@ -702,10 +708,10 @@ extern char *rb_w32_strerror(int); #endif struct tms { - long tms_utime; - long tms_stime; - long tms_cutime; - long tms_cstime; + long tms_utime; + long tms_stime; + long tms_cutime; + long tms_cstime; }; int rb_w32_times(struct tms *); @@ -722,7 +728,9 @@ int rb_w32_fclose(FILE*); int rb_w32_pipe(int[2]); ssize_t rb_w32_read(int, void *, size_t); ssize_t rb_w32_write(int, const void *, size_t); -off_t rb_w32_lseek(int, off_t, int); +ssize_t rb_w32_pread(int, void *, size_t, rb_off_t offset); +ssize_t rb_w32_pwrite(int, const void *, size_t, rb_off_t offset); +rb_off_t rb_w32_lseek(int, rb_off_t, int); int rb_w32_uutime(const char *, const struct utimbuf *); int rb_w32_uutimes(const char *, const struct timeval *); int rb_w32_uutimensat(int /* must be AT_FDCWD */, const char *, const struct timespec *, int /* must be 0 */); @@ -815,7 +823,7 @@ double rb_w32_pow(double x, double y); #define MAP_ANON 0x1000 #define MAP_ANONYMOUS MAP_ANON -extern void *rb_w32_mmap(void *, size_t, int, int, int, off_t); +extern void *rb_w32_mmap(void *, size_t, int, int, int, rb_off_t); extern int rb_w32_munmap(void *, size_t); extern int rb_w32_mprotect(void *, size_t, int); |
