HTTPS clone URL
Subversion checkout URL
Thread safety notes
- 0.17.0 Prerelease
- Actor lifecycle
- Asking for Help
- Basic usage
- DEPRECATION WARNING
- Elsewhere on the Internet
- Fault Tolerance
- Fiber Stack Errors
- Finite State Machines
- Frequently Asked Questions
- GSoC Ideas
- Overhaul Plan
- Pipelining and execution modes
- Projects Using Celluloid
- Protocol Interaction
- Structuring Programs
- Supervision Groups
- Thread safety notes
- Triage Process
Clone this wiki locally
Ruby actually has a pretty good story when it comes to thread safety. The best strategy for thread safety is to share as little state as possible, and if you do share state, you should never mutate it. The worry of anyone stepping into a thread safe world is that you're using a bunch of legacy libraries with dubious thread safety. Who knows what those crazy library authors were doing?
Relax people. You're using a language where somebody can change what the '+' operator does to numbers. So why aren't we afraid to add numbers? Who knows what those crazy library authors may have done! Instead of freaking out, we can learn some telltale signs of things that will cause thread safety problems in Ruby programs so we can identify potential problem libraries just from how their APIs behave.
The #1 thread safety issue to look out for in a Ruby library is if it provides some sort of singleton access to a particular object through a class method, e.g MyClass.zomgobject, as opposed to asking you to do MyClass.new. If you aren't allocating the object, it isn't yours, it's somebody else's, and you better damn well make sure you can share nice, or you shouldn't play with it at all.
How do we share nicely? Let's find out by first looking at a thread-unsafe version of a singleton method:
class Foo def self.current @foo ||= Foo.new end end
Seems bad. All threads will share access to the same Foo object, and there's also a secondary bug here which means when the object is first being allocated and memoized as @foo. The first thread that tries to allocate it may get a different version than all the other threads because the memo value it set got clobbered by another thread because it's unsynchronized.
What else can we do? It depends on why the library is memoizing. Perhaps the Foo object has some kind of setup cost, such as making a network connection, and we want to keep it around instead of setting it up and tearing it down every time. If that's the case, the simplest thing we can do to make this code thread safe is to create a thread-specific memo of the object:
class Foo def self.current Thread.current[:foo] ||= Foo.new end end
Keep in mind that this will require N Foo objects for N threads. If each object is wrapping a network connection, this might be a concern. That said, if you see this pattern employed in the singleton methods of a library, it's most likely thread safe, provided that Foo doesn't do other wonky things.
A more efficient approach is to use a connection pool. Threads can share a group of connections and return them to the pool when they're not in use, rather than each making their own connections that may or may not stay idle most of the time. Many gems already provide a connection pool (e.g. net-http-persistent), however if you find a library that doesn't and want to create a connection pool with it, check out the connection_pool gem.