IO::Buffer#locked: Release lock even when the block raises (#16180)

IO::Buffer#locked: Release lock even when the block raises/breaks

Previously, `IO::Buffer#locked` leaks the lock when the block raises
an exception, or breaks.

Fixes: [Bug #21882]
This commit is contained in:
Kasumi Hanazuki 2026-02-20 10:56:50 +09:00 committed by GitHub
parent 126b657bd1
commit b5ccab2093
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
Notes: git 2026-02-20 01:57:18 +00:00
Merged-By: ioquatix <samuel@codeotaku.com>
2 changed files with 42 additions and 5 deletions

View File

@ -1424,6 +1424,17 @@ rb_io_buffer_try_unlock(VALUE self)
return 0;
}
static VALUE
rb_io_buffer_locked_ensure(VALUE self)
{
struct rb_io_buffer *buffer = NULL;
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
buffer->flags &= ~RB_IO_BUFFER_LOCKED;
return Qnil;
}
/*
* call-seq: locked { ... }
*
@ -1466,11 +1477,7 @@ rb_io_buffer_locked(VALUE self)
buffer->flags |= RB_IO_BUFFER_LOCKED;
VALUE result = rb_yield(self);
buffer->flags &= ~RB_IO_BUFFER_LOCKED;
return result;
return rb_ensure(rb_yield, self, rb_io_buffer_locked_ensure, self);
}
/*

View File

@ -930,4 +930,34 @@ class TestIOBuffer < Test::Unit::TestCase
assert_equal value, round_trip_value, "#{le_type}/#{be_type}: double-swap should restore original value"
end
end
class Bug21882 < RuntimeError; end
def test_locked_exception
buf = IO::Buffer.new(10)
assert_raise(Bug21882, '#locked should propagate exception') do
buf.locked { raise Bug21882 }
end
# should be unlocked now and can be locked again
refute_predicate buf, :locked?
buf.locked { }
end
def test_locked_break
buf = IO::Buffer.new(10)
assert_equal :ok, (buf.locked { break :ok })
# should be unlocked now and can be locked again
refute_predicate buf, :locked?
buf.locked { }
end
def test_locked_throw
buf = IO::Buffer.new(10)
assert_equal :ok, (catch(:bug21882) { buf.locked { throw :bug21882, :ok } })
# should be unlocked now and can be locked again
refute_predicate buf, :locked?
buf.locked { }
end
end