Project

General

Profile

Bug #22195

Updated by himura467 (Akito Shitara) 3 months ago

Reading from an `IO::Buffer` after calling `#free` does not raise. It behaves like an empty buffer: buffer, and `#valid?` still returns `true`: 

 ```ruby 
 buffer = IO::Buffer.for("Hello World") 
 buffer.free 
 buffer.valid? # => true 
 buffer.get_string # => "" (no error) 
 ``` 

 ## Why this looks like an unintended regression 

 Originally (https://github.com/ruby/ruby/commit/e30920354f, Ruby 3.1–3.3), accessing a freed buffer raised `IO::Buffer::AllocationError` ("The buffer is not allocated!"). 

 https://github.com/ruby/ruby/commit/c5cf4d4e12 made zero-length buffer operations succeed instead of raising, for [Bug #19542] and [Bug #18805]. Since a freed buffer also has `base == NULL, size == 0`, it was caught in the same code path and stopped raising too. Neither ticket mentions freed buffers, so this appears to be an accidental side effect rather than a decision. 
 Note that the rdoc of `#free` still promises that access after free raises. 

 ## Proposal 

 How about marking a buffer internally as freed invalidated when its memory it is released, explicitly freed (`#free` / `rb_io_buffer_free`), so that: 

 * `#valid?` returns `false`. 
 * Any byte access raises `IO::Buffer::AllocationError` again, restoring the pre-3.4 documented behavior. `IO::Buffer::InvalidatedError` or `IO::Buffer::AllocationError`. 
 * `#resize` re-allocates and clears the mark, as `#free`'s rdoc promises ("You can resize a freed buffer to re-allocate it"). 
 * The zero-length buffer semantics from https://github.com/ruby/ruby/pull/9532 are preserved. preserved: `IO::Buffer.new(0)`, `resize(0)` and zero-length slices remain valid and readable. 

 I'm not sure which exception class is preferable: `IO::Buffer::InvalidatedError` is consistent with how invalidated slices already behave and with `#valid?` returning `false`, while `IO::Buffer::AllocationError` restores the pre-3.4 behavior.

Back