[#108552] [Ruby master Bug#18782] Race conditions in autoload when loading the same feature with multiple threads. — "ioquatix (Samuel Williams)" <noreply@...>
Issue #18782 has been reported by ioquatix (Samuel Williams).
11 messages
2022/05/14
[ruby-core:108736] [Ruby master Bug#18465] Make `IO#write` atomic.
From:
"mame (Yusuke Endoh)" <noreply@...>
Date:
2022-05-30 03:19:31 UTC
List:
ruby-core #108736
Issue #18465 has been updated by mame (Yusuke Endoh).
I cannot understand this change well. Could you show me a code example to demonstrate this change?
I tried the following code in Ruby 3.1, but I couldn't observe the behavior that `A` and `B` are interleaved.
```
a = ["A" * 100_000] * 1000
b = ["B" * 100_000] * 1000
open("output.txt", "w") do |f|
th = Thread.new { f.write(*a) }
f.write(*b)
th.join
end
p File.read("t").gsub(/(.)\1*/) { $1 } #=> "BA" or "AB"
```
----------------------------------------
Bug #18465: Make `IO#write` atomic.
https://bugs.ruby-lang.org/issues/18465#change-97791
* Author: ioquatix (Samuel Williams)
* Status: Closed
* Priority: Normal
* Assignee: ioquatix (Samuel Williams)
* Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
Right now, `IO#write` including everything that calls it including `IO#puts`, has a poorly specified behaviour w.r.t. other fibers/threads that call `IO#write` at the same time.
Internally, we have a write lock, however it's only used to lock against individual writes rather than the whole operation. From a user point of view, there is some kind of atomicity, but it's not clearly defined and depends on many factors, e.g. whether `write` or `writev` is used internally.
We propose to make `IO#write` an atomic operation, that is, `IO#write` on a synchronous/buffered IO will always perform the write operation using a lock around the entire operation.
In theory, this should actually be more efficient than the current approach which may acquire and release the lock several times per operation, however in practice I'm sure it's almost unnoticeable.
Where it does matter, is when interleaved operations invoke the fiber scheduler. By using a single lock around the entire operation, rather than one or more locks around the system calls, the entire operation is more predictable and behaves more robustly.
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>