Skip to content

Commit b5ccab2

Browse files
author
Kasumi Hanazuki
authored
IO::Buffer#locked: Release lock even when the block raises (ruby#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]
1 parent 126b657 commit b5ccab2

2 files changed

Lines changed: 42 additions & 5 deletions

File tree

io_buffer.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,17 @@ rb_io_buffer_try_unlock(VALUE self)
14241424
return 0;
14251425
}
14261426

1427+
static VALUE
1428+
rb_io_buffer_locked_ensure(VALUE self)
1429+
{
1430+
struct rb_io_buffer *buffer = NULL;
1431+
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1432+
1433+
buffer->flags &= ~RB_IO_BUFFER_LOCKED;
1434+
1435+
return Qnil;
1436+
}
1437+
14271438
/*
14281439
* call-seq: locked { ... }
14291440
*
@@ -1466,11 +1477,7 @@ rb_io_buffer_locked(VALUE self)
14661477

14671478
buffer->flags |= RB_IO_BUFFER_LOCKED;
14681479

1469-
VALUE result = rb_yield(self);
1470-
1471-
buffer->flags &= ~RB_IO_BUFFER_LOCKED;
1472-
1473-
return result;
1480+
return rb_ensure(rb_yield, self, rb_io_buffer_locked_ensure, self);
14741481
}
14751482

14761483
/*

test/ruby/test_io_buffer.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,4 +930,34 @@ def test_integer_endianness_swapping
930930
assert_equal value, round_trip_value, "#{le_type}/#{be_type}: double-swap should restore original value"
931931
end
932932
end
933+
934+
class Bug21882 < RuntimeError; end
935+
def test_locked_exception
936+
buf = IO::Buffer.new(10)
937+
assert_raise(Bug21882, '#locked should propagate exception') do
938+
buf.locked { raise Bug21882 }
939+
end
940+
941+
# should be unlocked now and can be locked again
942+
refute_predicate buf, :locked?
943+
buf.locked { }
944+
end
945+
946+
def test_locked_break
947+
buf = IO::Buffer.new(10)
948+
assert_equal :ok, (buf.locked { break :ok })
949+
950+
# should be unlocked now and can be locked again
951+
refute_predicate buf, :locked?
952+
buf.locked { }
953+
end
954+
955+
def test_locked_throw
956+
buf = IO::Buffer.new(10)
957+
assert_equal :ok, (catch(:bug21882) { buf.locked { throw :bug21882, :ok } })
958+
959+
# should be unlocked now and can be locked again
960+
refute_predicate buf, :locked?
961+
buf.locked { }
962+
end
933963
end

0 commit comments

Comments
 (0)