[][src]Crate threadstack

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 that let_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. Note that pushing new values will panic if you go beyond the compile time configured maximum threadstack size. The assumption is that threadstacks are mostly used for infrequently set context data, or configuration settings that would otherwise be global variables.

Structs

ThreadStack

The container for the underlying array used to implement the stack of values. Generally you will only ever see this type wrapped inside of std::thread:LocalKey, and there is never any reason really to use it directly. Instead use declare_thread_stacks!, let_ref_thread_stack_value!, push_thread_stack_value! and clone_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.