summaryrefslogtreecommitdiff
path: root/include/ruby/internal/core/rtypeddata.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/ruby/internal/core/rtypeddata.h')
-rw-r--r--include/ruby/internal/core/rtypeddata.h475
1 files changed, 466 insertions, 9 deletions
diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h
index c038e6f2b8..6c19576c20 100644
--- a/include/ruby/internal/core/rtypeddata.h
+++ b/include/ruby/internal/core/rtypeddata.h
@@ -17,7 +17,7 @@
* 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.
+ * extension libraries. They could be written in C++98.
* @brief Defines struct ::RTypedData.
*/
#include "ruby/internal/config.h"
@@ -28,6 +28,8 @@
#include "ruby/internal/assume.h"
#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/flag_enum.h"
+#include "ruby/internal/attr/nonnull.h"
#include "ruby/internal/attr/pure.h"
#include "ruby/internal/cast.h"
#include "ruby/internal/core/rbasic.h"
@@ -38,13 +40,68 @@
#include "ruby/internal/stdbool.h"
#include "ruby/internal/value_type.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.
+ */
#define HAVE_TYPE_RB_DATA_TYPE_T 1
+
+/**
+ * @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_DATA_TYPE_T_FUNCTION 1
+
+/**
+ * @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_DATA_TYPE_T_PARENT 1
+
+/**
+ * This is a value you can set to ::rb_data_type_struct::dfree. Setting this
+ * means the data was allocated using ::ruby_xmalloc() (or variants), and shall
+ * be freed using ::ruby_xfree().
+ *
+ * @warning Do not use this if you want to use system malloc, because the
+ * system and Ruby might or might not share the same malloc
+ * implementation.
+ */
#define RUBY_TYPED_DEFAULT_FREE RUBY_DEFAULT_FREE
+
+/**
+ * This is a value you can set to ::rb_data_type_struct::dfree. Setting this
+ * means the data is managed by someone else, like, statically allocated. Of
+ * course you are on your own then.
+ */
#define RUBY_TYPED_NEVER_FREE RUBY_NEVER_FREE
+
+/**
+ * Convenient casting macro.
+ *
+ * @param obj An object, which is in fact an ::RTypedData.
+ * @return The passed object casted to ::RTypedData.
+ */
#define RTYPEDDATA(obj) RBIMPL_CAST((struct RTypedData *)(obj))
+
+/**
+ * Convenient getter macro.
+ *
+ * @param v An object, which is in fact an ::RTypedData.
+ * @return The passed object's ::RTypedData::data field.
+ */
#define RTYPEDDATA_DATA(v) (RTYPEDDATA(v)->data)
+
+/** @old{rb_check_typeddata} */
#define Check_TypedStruct(v, t) \
rb_check_typeddata(RBIMPL_CAST((VALUE)(v)), (t))
@@ -57,55 +114,373 @@
#define RUBY_TYPED_PROMOTED1 RUBY_TYPED_PROMOTED1
/** @endcond */
-/* bits for rb_data_type_struct::flags */
-enum rbimpl_typeddata_flags {
+#define TYPED_DATA_EMBEDDED 2
+
+/**
+ * @private
+ *
+ * Bits for rb_data_type_struct::flags.
+ */
+enum
+RBIMPL_ATTR_FLAG_ENUM()
+rbimpl_typeddata_flags {
+ /**
+ * This flag has something to do with Ruby's global interpreter lock. For
+ * maximum safety, Ruby locks the entire VM during GC. However your
+ * callback functions could unintentionally unlock it, for instance when
+ * they try to flush an IO buffer. Such operations are dangerous (threads
+ * then run alongside of GC). By default, to prevent those scenario,
+ * callbacks are deferred until the GC engine is 100% sure threads can run.
+ * This flag skips that; structs with it are deallocated during the sweep
+ * phase.
+ *
+ * Using this flag needs deep understanding of both GC and threads. You
+ * would better leave it unspecified.
+ */
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
+ * dangerous, disabled by default. This flag is used to bypass that
+ * restriction. but setting it is not enough. In addition to do so, an
+ * object also has to be frozen, and be passed to
+ * rb_ractor_make_shareable() before being actually shareable. Of course,
+ * you have to manually prevent race conditions then.
+ *
+ * Using this flag needs deep understanding of multithreaded programming.
+ * You would better leave it unspecified.
+ */
RUBY_TYPED_FROZEN_SHAREABLE = RUBY_FL_SHAREABLE,
+
+ /**
+ * 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 basically works.
+ * But there is one tweak that has to be exercised. When an elder object
+ * has reference(s) to younger one(s), that referenced objects must not
+ * die. In order to detect additions of such references, old generations
+ * are protected by write barriers. It is a very difficult hack to
+ * appropriately insert write barriers everywhere. This mechanism is
+ * disabled by default for 3rd party extensions (they never get aged). By
+ * specifying this flag you can enable the generational feature to your
+ * data structure. Of course, you have to manually insert write barriers
+ * then.
+ *
+ * Using this flag needs deep understanding of GC internals, often at the
+ * level of source code. You would better leave it unspecified.
+ */
RUBY_TYPED_WB_PROTECTED = RUBY_FL_WB_PROTECTED, /* THIS FLAG DEPENDS ON Ruby version */
- RUBY_TYPED_PROMOTED1 = RUBY_FL_PROMOTED1 /* THIS FLAG DEPENDS ON Ruby version */
+
+ /**
+ * This flag no longer in use
+ */
+ RUBY_TYPED_UNUSED = RUBY_FL_UNUSED6,
+
+ /**
+ * 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
};
+/**
+ * This is the struct that holds necessary info for a struct. It roughly
+ * resembles a Ruby level class; multiple objects can share a ::rb_data_type_t
+ * instance.
+ */
typedef struct rb_data_type_struct rb_data_type_t;
+/** @copydoc rb_data_type_t */
struct rb_data_type_struct {
+
+ /**
+ * Name of structs of this kind. This is used for diagnostic purposes.
+ * This has to be unique in the process, but doesn't has to be a valid
+ * C/Ruby identifier.
+ */
const char *wrap_struct_name;
+
+ /** Function pointers. Resembles C++ `vtbl`.*/
struct {
+
+ /**
+ * This function is called when the object is experiencing GC marks.
+ * If it contains references to other Ruby objects, you need to mark
+ * them also. Otherwise GC will smash your data.
+ *
+ * @see rb_gc_mark()
+ * @warning This is called during GC runs. Object allocations are
+ * impossible at that moment (that is why GC runs).
+ */
RUBY_DATA_FUNC dmark;
+
+ /**
+ * This function is called when the object is no longer used. You need
+ * to do whatever necessary to avoid memory leaks.
+ *
+ * @warning This is called during GC runs. Object allocations are
+ * impossible at that moment (that is why GC runs).
+ */
RUBY_DATA_FUNC dfree;
+
+ /**
+ * This function is to query the size of the underlying memory regions.
+ *
+ * @internal
+ *
+ * This function has only one usage, which is form inside of
+ * `ext/objspace`.
+ */
size_t (*dsize)(const void *);
+
+ /**
+ * This function is called when the object is relocated. Like
+ * ::rb_data_type_struct::dmark, you need to update references to Ruby
+ * objects inside of your structs.
+ *
+ * @see rb_gc_location()
+ * @warning This is called during GC runs. Object allocations are
+ * impossible at that moment (that is why GC runs).
+ */
RUBY_DATA_FUNC dcompact;
+
+ /**
+ * This field is reserved for future extension. For now, it must be
+ * filled with zeros.
+ */
void *reserved[1]; /* For future extension.
This array *must* be filled with ZERO. */
} function;
+
+ /**
+ * Parent of this class. Sometimes C structs have inheritance-like
+ * relationships. An example is `struct sockaddr` and its family. If you
+ * design such things, make ::rb_data_type_t for each of them and connect
+ * using this field. Ruby can then transparently cast your data back and
+ * forth when you call #TypedData_Get_Struct().
+ *
+ * ```CXX
+ * struct parent { };
+ * static inline const rb_data_type_t parent_type = {
+ * .wrap_struct_name = "parent",
+ * };
+ *
+ * struct child: public parent { };
+ * static inline const rb_data_type_t child_type = {
+ * .wrap_struct_name = "child",
+ * .parent = &parent_type,
+ * };
+ *
+ * // This function can take both parent_class and child_class.
+ * static inline struct parent *
+ * get_parent(VALUE v)
+ * {
+ * struct parent *p;
+ * TypedData_Get_Struct(v, parent_type, struct parent, p);
+ * return p;
+ * }
+ * ```
+ */
const rb_data_type_t *parent;
+
+ /**
+ * Type-specific static data. This area can be used for any purpose by a
+ * programmer who define the type. Ruby does not manage this at all.
+ */
void *data; /* This area can be used for any purpose
by a programmer who define the type. */
+
+ /**
+ * Type-specific behavioural characteristics. This is a bitfield. It is
+ * an EXTREMELY WISE IDEA to leave this field blank. It is designed so
+ * that setting zero is the safest thing to do. If you risk to set any
+ * bits on, you have to know exactly what you are doing.
+ *
+ * @internal
+ *
+ * Why it has to be a ::VALUE? @shyouhei doesn't understand the design.
+ */
VALUE flags; /* RUBY_FL_WB_PROTECTED */
};
+/**
+ * "Typed" user data. By using this, extension libraries can wrap a C struct
+ * to make it visible from Ruby. For instance if you have a `struct timeval`,
+ * and you want users to use it,
+ *
+ * ```CXX
+ * static inline const rb_data_type_t timeval_type = {
+ * // Note that unspecified fields are 0-filled by default.
+ * .wrap_struct_name = "timeval",
+ * .function = {
+ * .dmark = nullptr, // no need to mark
+ * .dfree = RUBY_TYPED_DEFAULT_FREE, // use ruby_xfree()
+ * .dsize = [](auto) {
+ * return sizeof(struct timeval);
+ * },
+ * },
+ * };
+ *
+ * extern "C" void
+ * Init_timeval(void)
+ * {
+ * auto klass = rb_define_class("YourName", rb_cObject);
+ *
+ * rb_define_alloc_func(klass, [](auto klass) {
+ * struct timeval *t;
+ * auto ret = TypedData_Make_Struct(
+ * klass, struct timeval, &timeval_type, t);
+ *
+ * if (auto i = gettimeofday(t, nullptr); i == -1) {
+ * rb_sys_fail("gettimeofday(3)");
+ * }
+ * else {
+ * return ret;
+ * }
+ * });
+ * }
+ * ```
+ */
struct RTypedData {
+
+ /** The part that all ruby objects have in common. */
struct RBasic basic;
- const rb_data_type_t *type;
- VALUE typed_flag; /* 1 or not */
+
+ /**
+ * 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 *const type;
+
+ /**
+ * This has to be always 1.
+ *
+ * @internal
+ */
+ const VALUE typed_flag;
+
+ /** Pointer to the actual C level struct that you want to wrap. */
void *data;
};
RBIMPL_SYMBOL_EXPORT_BEGIN()
-VALUE rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *);
+RBIMPL_ATTR_NONNULL((3))
+/**
+ * This is the primitive way to wrap an existing C struct into ::RTypedData.
+ *
+ * @param[in] klass Ruby level class of the returning object.
+ * @param[in] datap Pointer to the target C struct.
+ * @param[in] type The characteristics of the passed data.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return An allocated object that wraps `datap`.
+ */
+VALUE rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type);
+
+/**
+ * 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
+ * using ruby_calloc(). Hence it makes no sense for `type->function.dfree` to
+ * be anything other than ::RUBY_TYPED_DEFAULT_FREE.
+ *
+ * @param[in] klass Ruby level class of the returning object.
+ * @param[in] size Requested size of memory to allocate.
+ * @param[in] type The characteristics of the passed data.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return An allocated object that wraps a new `size` byte region.
+ */
VALUE rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t *type);
+
+/**
+ * Checks for the domestic relationship between the two.
+ *
+ * @param[in] child A data type supposed to be a child of `parent`.
+ * @param[in] parent A data type supposed to be a parent of `child`.
+ * @retval true `child` is a descendent of `parent`.
+ * @retval false Otherwise.
+ *
+ * @internal
+ *
+ * You can path NULL to both arguments, don't know what that means though.
+ */
int rb_typeddata_inherited_p(const rb_data_type_t *child, const rb_data_type_t *parent);
+
+/**
+ * Checks if the given object is of given kind.
+ *
+ * @param[in] obj An instance of ::RTypedData.
+ * @param[in] data_type Expected data type of `obj`.
+ * @retval true `obj` is of `data_type`.
+ * @retval false Otherwise.
+ */
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type);
+
+/**
+ * Identical to rb_typeddata_is_kind_of(), except it raises exceptions instead
+ * of returning false.
+ *
+ * @param[in] obj An instance of ::RTypedData.
+ * @param[in] data_type Expected data type of `obj`.
+ * @exception rb_eTypeError obj is not of `data_type`.
+ * @return Unwrapped C struct that `obj` holds.
+ * @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_SYMBOL_EXPORT_END()
+/**
+ * Converts sval, a pointer to your struct, into a Ruby object.
+ *
+ * @param klass A ruby level class.
+ * @param data_type The type of `sval`.
+ * @param sval A pointer to your struct.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return A created Ruby object.
+ */
#define TypedData_Wrap_Struct(klass,data_type,sval)\
rb_data_typed_object_wrap((klass),(sval),(data_type))
+/**
+ * @private
+ *
+ * This is an implementation detail of #TypedData_Make_Struct. People don't
+ * use it directly.
+ *
+ * @param result Variable name of created Ruby object.
+ * @param klass Ruby level class of the object.
+ * @param type Type name of the C struct.
+ * @param size Size of the C struct.
+ * @param data_type The data type describing `type`.
+ * @param sval Variable name of created C struct.
+ */
#define TypedData_Make_Struct0(result, klass, type, size, data_type, sval) \
VALUE result = rb_data_typed_object_zalloc(klass, size, data_type); \
- (sval) = RBIMPL_CAST((type *)RTYPEDDATA_DATA(result)); \
+ (sval) = (type *)RTYPEDDATA_GET_DATA(result); \
RBIMPL_CAST(/*suppress unused variable warnings*/(void)(sval))
+/**
+ * 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.
+ *
+ * @param klass Ruby level class of the object.
+ * @param type Type name of the C struct.
+ * @param data_type The data type describing `type`.
+ * @param sval Variable name of created C struct.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return A created Ruby object.
+ */
#ifdef HAVE_STMT_AND_DECL_IN_EXPR
#define TypedData_Make_Struct(klass, type, data_type, sval) \
RB_GNUC_EXTENSION({ \
@@ -127,19 +502,79 @@ 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
+RTYPEDDATA_EMBEDDED_P(VALUE obj)
+{
+#if RUBY_DEBUG
+ if (RB_UNLIKELY(!RB_TYPE_P(obj, RUBY_T_DATA))) {
+ Check_Type(obj, RUBY_T_DATA);
+ RBIMPL_UNREACHABLE_RETURN(false);
+ }
+#endif
+
+ return RTYPEDDATA(obj)->typed_flag & TYPED_DATA_EMBEDDED;
+}
+
+static inline void *
+RTYPEDDATA_GET_DATA(VALUE obj)
+{
+#if RUBY_DEBUG
+ if (RB_UNLIKELY(!RB_TYPE_P(obj, RUBY_T_DATA))) {
+ Check_Type(obj, RUBY_T_DATA);
+ RBIMPL_UNREACHABLE_RETURN(false);
+ }
+#endif
+
+ /* We reuse the data pointer in embedded TypedData. We can't use offsetof
+ * since RTypedData a non-POD type in C++. */
+ const size_t embedded_typed_data_size = sizeof(struct RTypedData) - sizeof(void *);
+
+ return RTYPEDDATA_EMBEDDED_P(obj) ? (char *)obj + embedded_typed_data_size : RTYPEDDATA(obj)->data;
+}
+
RBIMPL_ATTR_PURE()
RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * @private
+ *
+ * This is an implementation detail of Check_Type(). People don't use it
+ * directly.
+ *
+ * @param[in] obj Object in question
+ * @retval true `obj` is an instance of ::RTypedData.
+ * @retval false `obj` is an instance of ::RData.
+ * @pre `obj` must be a Ruby object of ::RUBY_T_DATA.
+ */
static inline bool
rbimpl_rtypeddata_p(VALUE obj)
{
- return RTYPEDDATA(obj)->typed_flag == 1;
+ VALUE typed_flag = RTYPEDDATA(obj)->typed_flag;
+ return typed_flag != 0 && typed_flag <= 3;
}
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Checks whether the passed object is ::RTypedData or ::RData.
+ *
+ * @param[in] obj Object in question
+ * @retval true `obj` is an instance of ::RTypedData.
+ * @retval false `obj` is an instance of ::RData.
+ * @pre `obj` must be a Ruby object of ::RUBY_T_DATA.
+ */
static inline bool
RTYPEDDATA_P(VALUE obj)
{
@@ -156,6 +591,13 @@ RTYPEDDATA_P(VALUE obj)
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/* :TODO: can this function be __attribute__((returns_nonnull)) or not? */
+/**
+ * Queries for the type of given object.
+ *
+ * @param[in] obj Object in question
+ * @return Data type struct that corresponds to `obj`.
+ * @pre `obj` must be an instance of ::RTypedData.
+ */
static inline const struct rb_data_type_struct *
RTYPEDDATA_TYPE(VALUE obj)
{
@@ -169,6 +611,20 @@ RTYPEDDATA_TYPE(VALUE obj)
return RTYPEDDATA(obj)->type;
}
+/**
+ * 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
+ * this one.
+ *
+ * @param[in] klass Ruby level class of the returning object.
+ * @param[in] type The data type
+ * @param[out] datap Return pointer.
+ * @param[in] size Size of the C struct.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return A created Ruby object.
+ * @post `*datap` points to the C struct wrapped by the returned object.
+ */
static inline VALUE
rb_data_typed_object_make(VALUE klass, const rb_data_type_t *type, void **datap, size_t size)
{
@@ -177,6 +633,7 @@ rb_data_typed_object_make(VALUE klass, const rb_data_type_t *type, void **datap,
}
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)
{