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:

  1. 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.
  2. Install the gem.

  3. 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
    
  4. Run your code with thread_safety in the command line.

    For example, if you had the following script in test.rb with the callback from above:

    obj = []
    Thread.new do
      obj << 1
    end.join
    

    Then 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 created object.
  • created_thread: The Thread object that created object.
  • access_thread: The Thread object that wrote into object.
  • access_fiber: The Fiber object that wrote into object.