Thread-safety analysis for your projects, as an extension to RuboCop.
Add this line to your application's Gemfile (using require: false as it's a standalone tool):
gem 'rubocop-thread_safety', require: falseInstall it with Bundler by invoking:
$ bundle
Add this line to your application's .rubocop.yml:
plugins: rubocop-thread_safety
Now you can run rubocop and it will automatically load the RuboCop
Thread-Safety cops together with the standard cops.
Note
The plugin system is supported in RuboCop 1.72+. In earlier versions, use require instead of plugins.
Install the gem:
$ gem install rubocop-thread_safety
Scan the application for just thread-safety issues:
$ rubocop --plugin rubocop-thread_safety --only ThreadSafety,Style/GlobalVars,Style/ClassVars,Style/MutableConstant
There are some added configuration options that can be tweaked to modify the behaviour of these thread-safety cops.
There are a few ways to improve thread-safety that stem around avoiding unsynchronized mutation of state that is shared between multiple threads.
State shared between threads may take various forms, including:
- Class variables (
@@name). Note: these affect child classes too. - Class instance variables (
@namein class context or class methods) - Constants (
NAME). Ruby will warn if a constant is re-assigned to a new value but will allow it. Mutable objects can still be mutated (e.g. push to an array) even if they are assigned to a constant. - Globals (
$name), with the possible exception of some special globals provided by ruby that are documented as thread-local like regular expression results. - Variables in the scope of created threads (where
Thread.newis called).
Improvements that would make shared state thread-safe include:
freezeobjects to protect against mutation. Note:freezeis shallow, i.e. freezing an array will not also freeze its elements.- Use data structures or concurrency abstractions from concurrent-ruby, e.g.
Concurrent::Map - Use a
Mutexor similar tosynchronizeaccess. - Use
ActiveSupport::CurrentAttributes - Use
RequestStore - Use
Thread.current[:name]
Certain system calls, such as chdir, affect the entire process. To avoid potential thread-safety issues, it's preferable to use (if possible) the chdir option in methods like Kernel.system and IO.popen rather than relying on Dir.chdir.
- Intro to thread-safety in Ruby by Socketry: https://socketry.github.io/async/guides/thread-safety/index.html
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/rubocop/rubocop-thread_safety.
Portions Copyright (c) 2016-2023 Michael Gee and contributors. Portions Copyright (c) 2016-2023 CoverMyMeds.
See LICENSE.txt for further details.