Thread-local storage

Migo·2024년 10월 10일

CS

목록 보기
6/7

Context

When a thread is executing, its context is typically stored in the stack memory. The key elements stored in the stack for thread context usually include:

  • Local variables: Variables defined within functions that are currently in the call stack.
  • Function parameters: Arguments passed to functions in the current call stack.
  • Return addresses: Memory addresses where execution should resume after a function call completes.
  • Saved register values: The state of CPU registers that need to be preserved across function calls.
  • Stack frame pointers: References to previous stack frames, allowing for proper unwinding of the call stack.
  • Exception handling information: Data structures used for managing exceptions, if applicable.
  • Thread-specific data: Small amounts of thread-local storage, depending on the implementation.

But that begs this question :What if the context references to data defined in calling function?

There comes, Dangling pointer and Undefined behaviour. When the data being referenced literally hits the end of its lifetime, every other variables that hold the reference to it will point to null. And it boils down to the common synchronization problem.

Thread local storage

So how can we circumvent the race condition or undefiend behaviour issues? One way to do it is to use thread-local storage.

Variables stored in thread-local storage mean the following:

  • Any thread can access this variable
  • Although it seems to access the same variable, each thread takes its own variable

The following example shows how you can declare thread-local resource and what happens when those values are accessed by multiple threads.

use std::cell::Cell;
use std::thread;

thread_local! {
    static COUNTER: Cell<u32> = Cell::new(0);
}

fn increment_counter() {
    COUNTER.with(|c| {
        let value = c.get();
        c.set(value + 1);
        println!(
            "Thread {:?}: counter value = {}",
            thread::current().id(),
            c.get()
        );
    });
}

#[test]
fn t() {
    // Spawn multiple threads
    let handles: Vec<_> = (0..3)
        .map(|_| {
            thread::spawn(|| {
                for _ in 0..5 {
                    increment_counter();
                }
            })
        })
        .collect();

    // Wait for all threads to complete
    for handle in handles {
        handle.join().unwrap();
    }

    // Check the counter in the main thread
    COUNTER.with(|c| {
        println!("Main thread: counter value = {}", c.get());
    });
}




Thread ThreadId(3): counter value = 1
Thread ThreadId(3): counter value = 2
Thread ThreadId(3): counter value = 3
Thread ThreadId(3): counter value = 4
Thread ThreadId(3): counter value = 5
Thread ThreadId(4): counter value = 1
Thread ThreadId(4): counter value = 2
Thread ThreadId(4): counter value = 3
Thread ThreadId(4): counter value = 4
Thread ThreadId(4): counter value = 5
Thread ThreadId(5): counter value = 1
Thread ThreadId(5): counter value = 2
Thread ThreadId(5): counter value = 3
Thread ThreadId(5): counter value = 4
Thread ThreadId(5): counter value = 5
profile
Dude with existential crisis

0개의 댓글