Skip to content

No rb_str_locktmp: IO::Buffer.for(str) { } does not lock str against modification #671

Open

Description

@tannevaled

What

rbgo has no equivalent of MRI's rb_str_locktmp / rb_str_unlocktmp — a String flag, distinct from frozen, that makes every mutation raise

RuntimeError: can't modify string; temporarily locked

for as long as something holds the String's bytes.

IO::Buffer.for(string) { … } is the case that needs it. io_buffer_for_yield_instance (io_buffer.c) does

arguments->instance = io_buffer_for_make_instance(arguments->klass, arguments->string, arguments->flags);
rb_str_locktmp(arguments->string);
return rb_yield(arguments->instance);

and io_buffer_for_yield_instance_ensure unlocks it. The buffer aliases the String's bytes, so mutating the String while the block runs could reallocate them under the buffer — hence the lock rather than a copy.

s = +"test"
IO::Buffer.for(s) { s << "x" }
# MRI 4.0.5: RuntimeError: can't modify string; temporarily locked
# rbgo:      succeeds, s == "testx"   (the buffer now aliases freed bytes)

Measured cost

core/io/buffer/for_spec.rb    rbgo = 6    MRI 4.0.5 = 7

The one remaining example is "locks the original string to prevent modification". Every other file under core/io/buffer/ is at its ceiling after #668.

It is one example, but the flag is not IO::Buffer-specific: MRI uses it anywhere C code holds a String's buffer across a Ruby callback, so String#unpack with a block, IO#read into a buffer and the pack/unpack1 family would all want it.

Where

internal/object (a flag beside Frozen on object.String) plus every mutator that checks Frozen today — string_*.go. Both outside the file set of #668, which is why this is filed rather than fixed there. internal/vm/io_buffer.go then takes and releases the lock around the .for and .string block forms.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions