← All posts

Threads, Fibers and local variables

· 3 min read · V. Dombrovskiy

Did you know that if you store something in thread-local storage, it won't be available in child fibers?

01 Thread.current[] is fiber-local

Although the name Thread.current[] gives us a clue that it stores our data in a thread, it actually stores it in fiber-local variables.

So, if you do something like this:

thread_current.rbRB
Thread.current[:foo] = 1
Fiber.new do
  p Thread.current[:foo]
end.resume

# => nil

It will print nil.

02 Truly thread-local storage

If you really want to store something that is truly thread-local, you must use Thread.current.thread_variable_get and Thread.current.thread_variable_set.

03 Two separate storage APIs

To make things even more interesting, Thread.current[] and Fiber[] are not the same storage.

If you set a value using Thread.current[], it won't be available through Fiber[], and vice versa.

04 Fiber Storage inheritance

One more caveat about fibers: Fiber Storage supports inheritance of data from the parent fiber. This makes it different from Thread.current[], whose fiber-local variables are always brand new in a newly created fiber.

However, Fiber Storage inheritance is shallow. If you do something like this:

fiber_storage.rbRB
Fiber[:foo] = { a: 1, b: 2 }

Ruby copies the Fiber Storage itself, but the hash stored under :foo is not duplicated. Instead, both the parent and child fibers hold a reference to the same hash.

Mutating the hash in either the parent or the child fiber will be visible to the other.

Working through something similar?

Start a project