Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert NIO objects to TypedData API #310

Merged
merged 3 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
74 changes: 46 additions & 28 deletions ext/nio4r/bytebuffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ static VALUE cNIO_ByteBuffer_MarkUnsetError = Qnil;

/* Allocator/deallocator */
static VALUE NIO_ByteBuffer_allocate(VALUE klass);
static void NIO_ByteBuffer_gc_mark(struct NIO_ByteBuffer *byteBuffer);
static void NIO_ByteBuffer_free(struct NIO_ByteBuffer *byteBuffer);
static void NIO_ByteBuffer_free(void *data);
static size_t NIO_ByteBuffer_memsize(const void *data);

/* Methods */
static VALUE NIO_ByteBuffer_initialize(VALUE self, VALUE capacity);
Expand Down Expand Up @@ -92,28 +92,46 @@ void Init_NIO_ByteBuffer()
rb_define_method(cNIO_ByteBuffer, "inspect", NIO_ByteBuffer_inspect, 0);
}

static const rb_data_type_t NIO_ByteBuffer_type = {
"NIO::ByteBuffer",
{
NULL, // Nothing to mark
NIO_ByteBuffer_free,
NIO_ByteBuffer_memsize,
},
0,
0,
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};

static VALUE NIO_ByteBuffer_allocate(VALUE klass)
{
struct NIO_ByteBuffer *bytebuffer = (struct NIO_ByteBuffer *)xmalloc(sizeof(struct NIO_ByteBuffer));
bytebuffer->buffer = NULL;
return Data_Wrap_Struct(klass, NIO_ByteBuffer_gc_mark, NIO_ByteBuffer_free, bytebuffer);
return TypedData_Wrap_Struct(klass, &NIO_ByteBuffer_type, bytebuffer);
}

static void NIO_ByteBuffer_gc_mark(struct NIO_ByteBuffer *buffer)
static void NIO_ByteBuffer_free(void *data)
{
struct NIO_ByteBuffer *buffer = (struct NIO_ByteBuffer *)data;
if (buffer->buffer)
xfree(buffer->buffer);
xfree(buffer);
}

static void NIO_ByteBuffer_free(struct NIO_ByteBuffer *buffer)
static size_t NIO_ByteBuffer_memsize(const void *data)
{
const struct NIO_ByteBuffer *buffer = (const struct NIO_ByteBuffer *)data;
size_t memsize = sizeof(struct NIO_ByteBuffer);
if (buffer->buffer)
xfree(buffer->buffer);
xfree(buffer);
memsize += buffer->capacity;
return memsize;
}

static VALUE NIO_ByteBuffer_initialize(VALUE self, VALUE capacity)
{
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

buffer->capacity = NUM2INT(capacity);
buffer->buffer = xmalloc(buffer->capacity);
Expand All @@ -126,7 +144,7 @@ static VALUE NIO_ByteBuffer_initialize(VALUE self, VALUE capacity)
static VALUE NIO_ByteBuffer_clear(VALUE self)
{
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

memset(buffer->buffer, 0, buffer->capacity);

Expand All @@ -140,7 +158,7 @@ static VALUE NIO_ByteBuffer_clear(VALUE self)
static VALUE NIO_ByteBuffer_get_position(VALUE self)
{
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

return INT2NUM(buffer->position);
}
Expand All @@ -149,7 +167,7 @@ static VALUE NIO_ByteBuffer_set_position(VALUE self, VALUE new_position)
{
int pos;
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

pos = NUM2INT(new_position);

Expand All @@ -173,7 +191,7 @@ static VALUE NIO_ByteBuffer_set_position(VALUE self, VALUE new_position)
static VALUE NIO_ByteBuffer_get_limit(VALUE self)
{
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

return INT2NUM(buffer->limit);
}
Expand All @@ -182,7 +200,7 @@ static VALUE NIO_ByteBuffer_set_limit(VALUE self, VALUE new_limit)
{
int lim;
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

lim = NUM2INT(new_limit);

Expand Down Expand Up @@ -210,23 +228,23 @@ static VALUE NIO_ByteBuffer_set_limit(VALUE self, VALUE new_limit)
static VALUE NIO_ByteBuffer_capacity(VALUE self)
{
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

return INT2NUM(buffer->capacity);
}

static VALUE NIO_ByteBuffer_remaining(VALUE self)
{
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

return INT2NUM(buffer->limit - buffer->position);
}

static VALUE NIO_ByteBuffer_full(VALUE self)
{
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

return buffer->position == buffer->limit ? Qtrue : Qfalse;
}
Expand All @@ -236,7 +254,7 @@ static VALUE NIO_ByteBuffer_get(int argc, VALUE *argv, VALUE self)
int len;
VALUE length, result;
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

rb_scan_args(argc, argv, "01", &length);

Expand Down Expand Up @@ -264,7 +282,7 @@ static VALUE NIO_ByteBuffer_fetch(VALUE self, VALUE index)
{
int i;
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

i = NUM2INT(index);

Expand All @@ -283,7 +301,7 @@ static VALUE NIO_ByteBuffer_put(VALUE self, VALUE string)
{
long length;
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

StringValue(string);
length = RSTRING_LEN(string);
Expand All @@ -303,7 +321,7 @@ static VALUE NIO_ByteBuffer_read_from(VALUE self, VALUE io)
struct NIO_ByteBuffer *buffer;
ssize_t nbytes, bytes_read;

Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

io = rb_convert_type(io, T_FILE, "IO", "to_io");
io_set_nonblock(io);
Expand Down Expand Up @@ -333,7 +351,7 @@ static VALUE NIO_ByteBuffer_write_to(VALUE self, VALUE io)
struct NIO_ByteBuffer *buffer;
ssize_t nbytes, bytes_written;

Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);
io = rb_convert_type(io, T_FILE, "IO", "to_io");
io_set_nonblock(io);

Expand All @@ -360,7 +378,7 @@ static VALUE NIO_ByteBuffer_write_to(VALUE self, VALUE io)
static VALUE NIO_ByteBuffer_flip(VALUE self)
{
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

buffer->limit = buffer->position;
buffer->position = 0;
Expand All @@ -372,7 +390,7 @@ static VALUE NIO_ByteBuffer_flip(VALUE self)
static VALUE NIO_ByteBuffer_rewind(VALUE self)
{
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

buffer->position = 0;
buffer->mark = MARK_UNSET;
Expand All @@ -383,7 +401,7 @@ static VALUE NIO_ByteBuffer_rewind(VALUE self)
static VALUE NIO_ByteBuffer_mark(VALUE self)
{
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

buffer->mark = buffer->position;
return self;
Expand All @@ -392,7 +410,7 @@ static VALUE NIO_ByteBuffer_mark(VALUE self)
static VALUE NIO_ByteBuffer_reset(VALUE self)
{
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

if (buffer->mark < 0) {
rb_raise(cNIO_ByteBuffer_MarkUnsetError, "mark has not been set");
Expand All @@ -406,7 +424,7 @@ static VALUE NIO_ByteBuffer_reset(VALUE self)
static VALUE NIO_ByteBuffer_compact(VALUE self)
{
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

memmove(buffer->buffer, buffer->buffer + buffer->position, buffer->limit - buffer->position);
buffer->position = buffer->limit - buffer->position;
Expand All @@ -419,7 +437,7 @@ static VALUE NIO_ByteBuffer_each(VALUE self)
{
int i;
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

if (rb_block_given_p()) {
for (i = 0; i < buffer->limit; i++) {
Expand All @@ -435,7 +453,7 @@ static VALUE NIO_ByteBuffer_each(VALUE self)
static VALUE NIO_ByteBuffer_inspect(VALUE self)
{
struct NIO_ByteBuffer *buffer;
Data_Get_Struct(self, struct NIO_ByteBuffer, buffer);
TypedData_Get_Struct(self, struct NIO_ByteBuffer, &NIO_ByteBuffer_type, buffer);

return rb_sprintf(
"#<%s:%p @position=%d @limit=%d @capacity=%d>",
Expand Down
48 changes: 31 additions & 17 deletions ext/nio4r/monitor.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ static VALUE cNIO_Monitor = Qnil;

/* Allocator/deallocator */
static VALUE NIO_Monitor_allocate(VALUE klass);
static void NIO_Monitor_mark(struct NIO_Monitor *monitor);
static void NIO_Monitor_free(struct NIO_Monitor *monitor);
static void NIO_Monitor_mark(void *data);
static size_t NIO_Monitor_memsize(const void *data);

/* Methods */
static VALUE NIO_Monitor_initialize(VALUE self, VALUE selector, VALUE io, VALUE interests);
Expand Down Expand Up @@ -70,22 +70,36 @@ void Init_NIO_Monitor()
rb_define_method(cNIO_Monitor, "writeable?", NIO_Monitor_is_writable, 0);
}

static const rb_data_type_t NIO_Monitor_type = {
"NIO::Monitor",
{
NIO_Monitor_mark,
RUBY_TYPED_DEFAULT_FREE,
NIO_Monitor_memsize,
},
0,
0,
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
};

static VALUE NIO_Monitor_allocate(VALUE klass)
{
struct NIO_Monitor *monitor = (struct NIO_Monitor *)xmalloc(sizeof(struct NIO_Monitor));
assert(monitor);
*monitor = (struct NIO_Monitor){.self = Qnil};
return Data_Wrap_Struct(klass, NIO_Monitor_mark, NIO_Monitor_free, monitor);
return TypedData_Wrap_Struct(klass, &NIO_Monitor_type, monitor);
}

static void NIO_Monitor_mark(struct NIO_Monitor *monitor)
static void NIO_Monitor_mark(void *data)
{
struct NIO_Monitor *monitor = (struct NIO_Monitor *)data;
rb_gc_mark(monitor->self);
}

static void NIO_Monitor_free(struct NIO_Monitor *monitor)
static size_t NIO_Monitor_memsize(const void *data)
{
xfree(monitor);
const struct NIO_Monitor *monitor = (const struct NIO_Monitor *)data;
return sizeof(*monitor);
}

static VALUE NIO_Monitor_initialize(VALUE self, VALUE io, VALUE interests, VALUE selector_obj)
Expand All @@ -96,7 +110,7 @@ static VALUE NIO_Monitor_initialize(VALUE self, VALUE io, VALUE interests, VALUE

interests_id = SYM2ID(interests);

Data_Get_Struct(self, struct NIO_Monitor, monitor);
TypedData_Get_Struct(self, struct NIO_Monitor, &NIO_Monitor_type, monitor);

if (interests_id == rb_intern("r")) {
monitor->interests = EV_READ;
Expand All @@ -115,9 +129,9 @@ static VALUE NIO_Monitor_initialize(VALUE self, VALUE io, VALUE interests, VALUE
rb_ivar_set(self, rb_intern("interests"), interests);
rb_ivar_set(self, rb_intern("selector"), selector_obj);

Data_Get_Struct(selector_obj, struct NIO_Selector, selector);
selector = NIO_Selector_unwrap(selector_obj);

monitor->self = self;
RB_OBJ_WRITE(self, &monitor->self, self);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for my own edification, what is the nature of RB_OBJ_WRITE and when should it be used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a helper to assign a reference and trigger write barriers, it is required to allow Monitor objects to be marked as WB_PROTECTED which is what allow them to reach the old generation.

In short, when an object get to the old generation, it's no longer marked during minor GC. So if you create a reference from an old to a young object, the GC won't see it and mayincorrectly assume the young object is no longer referenced by anyone.

Write barriers are a way to tell the GC: "Hey, this old object created a new reference, you should check it".

In this specific instance, it's actually not really required because it's an object creating a reference to itself, so from a GC point of view, it's a bit useless. Similarly when you assign to an immediate like Qnil, it's not a big deal if you forget it.

But I like to always do it as I think consistency here help avoiding future patches to forget it.

monitor->ev_io.data = (void *)monitor;

/* We can safely hang onto this as we also hang onto a reference to the
Expand All @@ -135,7 +149,7 @@ static VALUE NIO_Monitor_close(int argc, VALUE *argv, VALUE self)
{
VALUE deregister, selector;
struct NIO_Monitor *monitor;
Data_Get_Struct(self, struct NIO_Monitor, monitor);
TypedData_Get_Struct(self, struct NIO_Monitor, &NIO_Monitor_type, monitor);

rb_scan_args(argc, argv, "01", &deregister);
selector = rb_ivar_get(self, rb_intern("selector"));
Expand All @@ -161,7 +175,7 @@ static VALUE NIO_Monitor_close(int argc, VALUE *argv, VALUE self)
static VALUE NIO_Monitor_is_closed(VALUE self)
{
struct NIO_Monitor *monitor;
Data_Get_Struct(self, struct NIO_Monitor, monitor);
TypedData_Get_Struct(self, struct NIO_Monitor, &NIO_Monitor_type, monitor);

return monitor->selector == 0 ? Qtrue : Qfalse;
}
Expand Down Expand Up @@ -190,7 +204,7 @@ static VALUE NIO_Monitor_set_interests(VALUE self, VALUE interests)
static VALUE NIO_Monitor_add_interest(VALUE self, VALUE interest)
{
struct NIO_Monitor *monitor;
Data_Get_Struct(self, struct NIO_Monitor, monitor);
TypedData_Get_Struct(self, struct NIO_Monitor, &NIO_Monitor_type, monitor);

interest = monitor->interests | NIO_Monitor_symbol2interest(interest);
NIO_Monitor_update_interests(self, (int)interest);
Expand All @@ -201,7 +215,7 @@ static VALUE NIO_Monitor_add_interest(VALUE self, VALUE interest)
static VALUE NIO_Monitor_remove_interest(VALUE self, VALUE interest)
{
struct NIO_Monitor *monitor;
Data_Get_Struct(self, struct NIO_Monitor, monitor);
TypedData_Get_Struct(self, struct NIO_Monitor, &NIO_Monitor_type, monitor);

interest = monitor->interests & ~NIO_Monitor_symbol2interest(interest);
NIO_Monitor_update_interests(self, (int)interest);
Expand All @@ -227,7 +241,7 @@ static VALUE NIO_Monitor_set_value(VALUE self, VALUE obj)
static VALUE NIO_Monitor_readiness(VALUE self)
{
struct NIO_Monitor *monitor;
Data_Get_Struct(self, struct NIO_Monitor, monitor);
TypedData_Get_Struct(self, struct NIO_Monitor, &NIO_Monitor_type, monitor);

if ((monitor->revents & (EV_READ | EV_WRITE)) == (EV_READ | EV_WRITE)) {
return ID2SYM(rb_intern("rw"));
Expand All @@ -243,7 +257,7 @@ static VALUE NIO_Monitor_readiness(VALUE self)
static VALUE NIO_Monitor_is_readable(VALUE self)
{
struct NIO_Monitor *monitor;
Data_Get_Struct(self, struct NIO_Monitor, monitor);
TypedData_Get_Struct(self, struct NIO_Monitor, &NIO_Monitor_type, monitor);

if (monitor->revents & EV_READ) {
return Qtrue;
Expand All @@ -255,7 +269,7 @@ static VALUE NIO_Monitor_is_readable(VALUE self)
static VALUE NIO_Monitor_is_writable(VALUE self)
{
struct NIO_Monitor *monitor;
Data_Get_Struct(self, struct NIO_Monitor, monitor);
TypedData_Get_Struct(self, struct NIO_Monitor, &NIO_Monitor_type, monitor);

if (monitor->revents & EV_WRITE) {
return Qtrue;
Expand Down Expand Up @@ -286,7 +300,7 @@ static void NIO_Monitor_update_interests(VALUE self, int interests)
{
ID interests_id;
struct NIO_Monitor *monitor;
Data_Get_Struct(self, struct NIO_Monitor, monitor);
TypedData_Get_Struct(self, struct NIO_Monitor, &NIO_Monitor_type, monitor);

if (NIO_Monitor_is_closed(self) == Qtrue) {
rb_raise(rb_eEOFError, "monitor is closed");
Expand Down
2 changes: 2 additions & 0 deletions ext/nio4r/nio4r.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ struct NIO_ByteBuffer {
int position, limit, capacity, mark;
};

struct NIO_Selector *NIO_Selector_unwrap(VALUE selector);

/* Thunk between libev callbacks in NIO::Monitors and NIO::Selectors */
void NIO_Selector_monitor_callback(struct ev_loop *ev_loop, struct ev_io *io, int revents);

Expand Down