Thread Safety
This gem plugs into Ruby's Modular GC feature to detect potential thread-safety issues through reporting cross-thread and cross-fiber writes.
Usage
To use the Thread Safety gem, follow these steps:
-
Build Ruby with Modular GC enabled by following the "Building Ruby with Modular GC" guide.
- Since Modular GC is still an experimental feature, this gem does not yet support released versions of Ruby. You must use the development version of Ruby.
-
Install the gem.
-
Implement a callback to report when a thread-safety issue is detected:
ThreadSafety.callback = proc do |offense| puts "Offense: #{offense.object} #{offense.backtrace[0].path}:#{offense.backtrace[0].lineno}" end -
Run your code with
thread_safetyin the command line.For example, if you had the following script in
test.rbwith thecallbackfrom above:obj = [] Thread.new do obj << 1 end.joinThen you could run it as such:
$ thread_safety ruby test.rb Offense: [] test.rb:9
API
ThreadSafety.callback = cb
Sets the callback that is called when an offense is detected. cb must accept one argument offense, which is of type ThreadSafety::Offense.
Set cb to nil to turn Thread Safety off.
ThreadSafety.enabled = enabled
Sets Thread Safety to be enabled or disabled globally. Defaults to false.
ThreadSafety.suppress_warnings {}
Disables Thread Safety offense detection for the block. This is useful for silencing offenses that are false-positives.
For example:
obj = []
Thread.new do
obj << 1
ThreadSafety.suppress_warnings { obj << 2 }
obj << 3
end.join
Outputs:
$ thread_safety ruby script.rb
Offense: [] script.rb:9
Offense: [1, 2] script.rb:11
Thread#thread_safety_enabled = enabled and Fiber#thread_safety_enabled = enabled
Sets Thread Safety to be enabled or disabled for a particular Thread or Fiber. This value will ignore the global value set by ThreadSafety.enabled=, so you can use this to implement allowlists or denylists.
ThreadSafety::Offense
Class for a warning. Contains the following fields:
object: The object that is being written to.backtrace: An array containing the Ruby backtrace of the write.created_fiber: The Fiber object that createdobject.created_thread: The Thread object that createdobject.access_thread: The Thread object that wrote intoobject.access_fiber: The Fiber object that wrote intoobject.