Skip to content
105 changes: 102 additions & 3 deletions ext/-test-/io_buffer/io_buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ io_buffer_raise(VALUE buffer, VALUE argument)
rb_raise(rb_eRuntimeError, "interrupted");
}

static VALUE
io_buffer_locked_p(VALUE buffer, VALUE argument)
{
(void)argument;
return rb_funcall(buffer, rb_intern("locked?"), 0);
}

static VALUE
io_buffer_for_reading_get_string(VALUE self, VALUE object)
{
Expand All @@ -63,10 +70,15 @@ io_buffer_for_reading_object_id(VALUE self, VALUE object)
}

static VALUE
io_buffer_for_reading_raise(VALUE self, VALUE string)
io_buffer_for_reading_raise(VALUE self, VALUE object)
{
StringValue(string);
return rb_io_buffer_for_reading(string, io_buffer_raise, Qnil);
return rb_io_buffer_for_reading(object, io_buffer_raise, Qnil);
}

static VALUE
io_buffer_for_reading_locked_p(VALUE self, VALUE object)
{
return rb_io_buffer_for_reading(object, io_buffer_locked_p, Qnil);
}

static VALUE
Expand All @@ -89,6 +101,86 @@ io_buffer_for_writing_modify_string(VALUE self, VALUE string)
return rb_io_buffer_for_writing(string, io_buffer_modify_string, string);
}

static VALUE
io_buffer_for_writing_raise(VALUE self, VALUE object)
{
return rb_io_buffer_for_writing(object, io_buffer_raise, Qnil);
}

static VALUE
io_buffer_for_writing_locked_p(VALUE self, VALUE object)
{
return rb_io_buffer_for_writing(object, io_buffer_locked_p, Qnil);
}

static VALUE
io_buffer_locked_for_reading_callback(const void *base, size_t size, VALUE buffer)
{
VALUE result = rb_ary_new_capa(3);

rb_ary_push(result, rb_funcall(buffer, rb_intern("locked?"), 0));
rb_ary_push(result, SIZET2NUM(size));
rb_ary_push(result, size > 0 ? INT2FIX(*(const unsigned char *)base) : Qnil);

return result;
}

static VALUE
io_buffer_locked_for_reading(VALUE self, VALUE buffer)
{
return rb_io_buffer_locked_for_reading(buffer, io_buffer_locked_for_reading_callback, buffer);
}

static VALUE
io_buffer_locked_for_reading_raise_callback(const void *base, size_t size, VALUE argument)
{
(void)base;
(void)size;
(void)argument;

rb_raise(rb_eRuntimeError, "interrupted");
}

static VALUE
io_buffer_locked_for_reading_raise(VALUE self, VALUE buffer)
{
return rb_io_buffer_locked_for_reading(buffer, io_buffer_locked_for_reading_raise_callback, Qnil);
}

static VALUE
io_buffer_locked_for_writing_callback(void *base, size_t size, VALUE buffer)
{
VALUE locked = rb_funcall(buffer, rb_intern("locked?"), 0);

if (size > 0) {
*(unsigned char *)base = 'x';
}

return locked;
}

static VALUE
io_buffer_locked_for_writing(VALUE self, VALUE buffer)
{
return rb_io_buffer_locked_for_writing(buffer, io_buffer_locked_for_writing_callback, buffer);
}

static VALUE
io_buffer_locked_for_writing_raise_callback(void *base, size_t size, VALUE argument)
{
(void)base;
(void)size;
(void)argument;

rb_raise(rb_eRuntimeError, "interrupted");
}

static VALUE
io_buffer_locked_for_writing_raise(VALUE self, VALUE buffer)
{
return rb_io_buffer_locked_for_writing(buffer, io_buffer_locked_for_writing_raise_callback, Qnil);
}

static VALUE
io_buffer_lock(VALUE self, VALUE buffer)
{
Expand Down Expand Up @@ -123,9 +215,16 @@ Init_io_buffer(void)
rb_define_singleton_method(mIOBuffer, "for_reading_readonly?", io_buffer_for_reading_readonly_p, 1);
rb_define_singleton_method(mIOBuffer, "for_reading_object_id", io_buffer_for_reading_object_id, 1);
rb_define_singleton_method(mIOBuffer, "for_reading_raise", io_buffer_for_reading_raise, 1);
rb_define_singleton_method(mIOBuffer, "for_reading_locked?", io_buffer_for_reading_locked_p, 1);
rb_define_singleton_method(mIOBuffer, "for_writing_set_string", io_buffer_for_writing_set_string, 2);
rb_define_singleton_method(mIOBuffer, "for_writing_readonly?", io_buffer_for_writing_readonly_p, 1);
rb_define_singleton_method(mIOBuffer, "for_writing_modify_string", io_buffer_for_writing_modify_string, 1);
rb_define_singleton_method(mIOBuffer, "for_writing_raise", io_buffer_for_writing_raise, 1);
rb_define_singleton_method(mIOBuffer, "for_writing_locked?", io_buffer_for_writing_locked_p, 1);
rb_define_singleton_method(mIOBuffer, "locked_for_reading", io_buffer_locked_for_reading, 1);
rb_define_singleton_method(mIOBuffer, "locked_for_reading_raise", io_buffer_locked_for_reading_raise, 1);
rb_define_singleton_method(mIOBuffer, "locked_for_writing", io_buffer_locked_for_writing, 1);
rb_define_singleton_method(mIOBuffer, "locked_for_writing_raise", io_buffer_locked_for_writing_raise, 1);
rb_define_singleton_method(mIOBuffer, "lock", io_buffer_lock, 1);
rb_define_singleton_method(mIOBuffer, "unlock", io_buffer_unlock, 1);
rb_define_singleton_method(mIOBuffer, "new_locked", io_buffer_new_locked, 1);
Expand Down
4 changes: 2 additions & 2 deletions gems/bundled_gems
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ net-imap 0.6.6 https://github.com/ruby/net-imap
net-smtp 0.5.1 https://github.com/ruby/net-smtp
matrix 0.4.3 https://github.com/ruby/matrix
prime 0.1.4 https://github.com/ruby/prime
rbs 4.0.3 https://github.com/ruby/rbs
typeprof 0.32.0 https://github.com/ruby/typeprof
rbs 4.2.0 https://github.com/ruby/rbs
typeprof 0.32.0 https://github.com/ruby/typeprof b950f910c6b6e4736988637f46a1ae256384c9f7
debug 1.11.1 https://github.com/ruby/debug 6510cfbc7496c55ebbefa437a25c17ca58f7c5eb
racc 1.8.1 https://github.com/ruby/racc
mutex_m 0.3.0 https://github.com/ruby/mutex_m
Expand Down
12 changes: 12 additions & 0 deletions include/ruby/io/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,18 @@ enum rb_io_buffer_flags rb_io_buffer_get_bytes(VALUE self, void **base, size_t *
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);

// Lock the backing allocation, invoke the callback with its readable bytes,
// and automatically unlock it when the callback returns or raises. The bytes
// are only valid for the duration of the callback. This protects the lifetime
// of the allocation; it does not provide synchronization for its contents.
VALUE rb_io_buffer_locked_for_reading(VALUE self, VALUE (*callback)(const void *base, size_t size, VALUE argument), VALUE argument);

// Lock the backing allocation, invoke the callback with its writable bytes,
// and automatically unlock it when the callback returns or raises. The bytes
// are only valid for the duration of the callback. This protects the lifetime
// of the allocation; it does not provide synchronization for its contents.
VALUE rb_io_buffer_locked_for_writing(VALUE self, VALUE (*callback)(void *base, size_t size, VALUE argument), VALUE argument);

VALUE rb_io_buffer_transfer(VALUE self);
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);
Expand Down
16 changes: 11 additions & 5 deletions internal/io_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ RUBY_SYMBOL_EXPORT_BEGIN
/**
* Wrap string_or_buffer as a read-only IO::Buffer view and invoke callback(buffer, argument).
*
* - IO::Buffer: callback is called directly with no wrapping.
* The resulting buffer's backing allocation is locked for the duration of the
* callback and automatically unlocked when the callback returns or raises.
*
* - IO::Buffer: locked and passed directly to the callback.
* - String: locked to prevent GC compaction from moving the backing memory,
* wrapped in a read-only IO::Buffer, callback called inside rb_ensure, buffer
* freed and string unlocked on exit.
* wrapped in a locked read-only IO::Buffer, callback called inside
* rb_ensure, buffer freed and string unlocked on exit.
* - Other: TypeError raised.
*/
VALUE rb_io_buffer_for_reading(VALUE string_or_buffer, VALUE (*callback)(VALUE buffer, VALUE argument), VALUE argument);
Expand All @@ -20,9 +23,12 @@ VALUE rb_io_buffer_for_reading(VALUE string_or_buffer, VALUE (*callback)(VALUE b
* Wrap string_or_buffer as a writable IO::Buffer view and invoke callback(buffer, argument).
*
* - Read-only IO::Buffer: ArgumentError raised.
* - IO::Buffer: callback is called directly with no wrapping.
* The resulting buffer's backing allocation is locked for the duration of the
* callback and automatically unlocked when the callback returns or raises.
*
* - IO::Buffer: locked and passed directly to the callback.
* - String: locked, wrapped in a writable IO::Buffer, callback called inside
* rb_ensure, buffer freed and string unlocked on exit.
* rb_ensure, buffer unlocked and freed, and string unlocked on exit.
* - Other: TypeError raised.
*/
VALUE rb_io_buffer_for_writing(VALUE string_or_buffer, VALUE (*callback)(VALUE buffer, VALUE argument), VALUE argument);
Expand Down
Loading