Expand description
A more ergonomic and more flexible form of thread local storage.
Inspired by the parameters feature from Racket.
The general idea is the following. Many applications have “context” variables that are needed by almost every module in the application. It is extremely tedious to pass down these values through every every function in the program. The obvious temptation is to use a global variable instead, but global variables have a bunch of widely known downsides:
-
They lack thread safety.
-
They create a hidden side channel between modules in your application that can create “spooky action at a distance.”
-
Because there is only one instance of a global variable modules in the program can fight over what they want the value of it to be.
Threadstacks are a middle ground. Essentially instead of having a
global variable, you keep a thread local stack of values. You can
only refer to the value at the top of the stack, and the borrow
checker will guarantee that your reference goes away before the
value is popped. You can push new values on the stack, but they
automatically expire when the lexical scope containing your push
ends. Values on the threadstack are immutable unless you go out of
your way to use a type with interior mutability like Cell
or
RefCell
, so code that wants to customize the value typically
will do so by pushing on onto the stack rather than clobbering the
existing value as would normally occur with a global variable.
This gives you the effect of a global variable that you can temporarily override. Functions that before would have referenced a global variable instead reference the top of the stack, and by pushing a value on the stack before calling said functions you can affect their behavior. However you are unable to affect the behavior when your caller calls those functions because by the time control returns to your caller the lexical scope containing your push will have ended and the value you pushed will have automatically been popped from the stack. This limits the degree to which different modules can step on each other.
Because the provided let_ref_thread_stack_value!
creates
references that have a special lifetime tied to the current stack
frame, it is not necessary to wrap all code using thread stack
values inside a call to something like my_local_key.with(|data| {...})
like you would have to with the standard thread_local!
TLS implementation.
Macros§
- declare_
thread_ stacks - Macro used to declare one or more thread stacks. The syntax pretty
closely mirrors thread_local! from the standard library, except
that the
static
key word is not used. - let_
ref_ thread_ stack_ value - Create a local reference to the value at the top of the
threadstack. Even though the top value may have been pushed at a
much higher layer in the call stack, the reference has a
conservative lifetime to guarantee safety – the same lifetime as
a local variable created on the stack where the macro is invoked.
If you don’t want to have to worry about lifetimes consider using
clone_thread_stack_value
instead. - push_
thread_ stack_ value - Push a new value on the top of the threadstack. This value becomes
the value that will be returned by
clone_thread_stack_value
and thatlet_ref_thread_stack_value!
will create a reference to. Can only be invoked inside a function, and the effect will last until the end of the current scope. Pushing a new value onto the threadstack will never panic. The assumption is that threadstacks are mostly used for infrequently set context data, or configuration settings that would otherwise be global variables.
Structs§
- Thread
Stack - The container for the underlying array used to implement the stack
of values when the stack is not given an initial value (for that
see
ThreadStackWithInitialValue
). Generally you will only ever see this type wrapped inside ofstd::thread:LocalKey
, and there is never any reason really to use it directly. Instead usedeclare_thread_stacks!
,let_ref_thread_stack_value!
,push_thread_stack_value!
andclone_thread_stack_value
. - Thread
Stack With Initial Value - The container for the underlying array used to implement the stack
of values, when in the declaration and initial value for the stack
was specified. This is only a separate type from
ThreadStack
so that a branch and possible panic can be omitted for slightly better performance and smaller generated code. Generally you will only ever see this type wrapped inside ofstd::thread:LocalKey
, and there is never any reason really to use it directly. Instead usedeclare_thread_stacks!
,let_ref_thread_stack_value!
,push_thread_stack_value!
andclone_thread_stack_value
.
Functions§
- clone_
thread_ stack_ value - Clone the value currently at the top of threadstack. This lets you avoid worrying about lifetimes but does require a clone to be made. This can panic only if nothing has been pushed onto the threadstack and it was created without an initial value.