6.22.2 Thread-Local Variables

Sometimes you want to establish a variable binding that is only valid for a given thread: a “thread-local variable”.

You would think that fluids or parameters would be Guile’s answer for thread-local variables, since establishing a new fluid binding doesn’t affect bindings in other threads. See Fluids and Dynamic States, or See Parameters. However, new threads inherit the fluid bindings that were in place in their creator threads. In this way, a binding established using a fluid (or a parameter) in a thread can escape to other threads, which might not be what you want. Or, it might escape via explicit reification via current-dynamic-state.

Of course, this dynamic scoping might be exactly what you want; that’s why fluids and parameters work this way, and is what you want for for many common parameters such as the current input and output ports, the current locale conversion parameters, and the like. Perhaps this is the case for most parameters, even. If your use case for thread-local bindings comes from a desire to isolate a binding from its setting in unrelated threads, then fluids and parameters apply nicely.

On the other hand, if your use case is to prevent concurrent access to a value from multiple threads, then using vanilla fluids or parameters is not appropriate. For this purpose, Guile has thread-local fluids. A fluid created with make-thread-local-fluid won’t be captured by current-dynamic-state and won’t be propagated to new threads.

Scheme Procedure: make-thread-local-fluid [dflt]
C Function: scm_make_thread_local_fluid (dflt)

Return a newly created fluid, whose initial value is dflt, or #f if dflt is not given. Unlike fluids made with make-fluid, thread local fluids are not captured by make-dynamic-state. Similarly, a newly spawned child thread does not inherit thread-local fluid values from the parent thread.

Scheme Procedure: fluid-thread-local? fluid
C Function: scm_fluid_thread_local_p (fluid)

Return #t if the fluid fluid is is thread-local, or #f otherwise.

For example:

(define %thread-local (make-thread-local-fluid))

(with-fluids ((%thread-local (compute-data)))
  ... (fluid-ref %thread-local) ...)

You can also make a thread-local parameter out of a thread-local fluid using the normal fluid->parameter:

(define param (fluid->parameter (make-thread-local-fluid)))

(parameterize ((param (compute-data)))
  ... (param) ...)