ThreadLocal passing to thread

I want to pass an integer into a ThreadLocal into my thread.

https://docs.rs/thread_local/1.1.3/thread_local/

        let tls: ThreadLocal<u32> = ThreadLocal::new();

            children.push(thread::spawn(move || {
                        shard_provider_file(&archive_copy, &filename, &pg_urls,virtual_nodes, &log_directory, tls).unwrap(); }
                   ));

The compiler will complain about tls getting moved. Can't clone it either? So how does ThreadLocal actually work for something this simple and practical?

Put it in an Arc.

Thank worked! Thanks.

        let mut tls: ThreadLocal<HashMap<String,u32>> = ThreadLocal::new();
        let mut tls_arc = Arc::new(tls);

So far so good..managed to pass into my thread, but then I am not sure how to access the Hashmap inside the thread so I can insert into it. Can't get the mutability right.

How would I pass the above tls_arch into a thread and add an item to that HashMap?

You can wrap it in a RefCell so you have the type:

Arc<ThreadLocal<RefCell<HashMap<String, u32>>>>

This will let you access it via the get_or_default method, which inserts an empty HashMap if this thread has not yet accessed the ThreadLocal. Of course, each thread has its own hash map when doing it this way.

2 Likes

Ok,almost there!
let mut unpacked = tls.get().unwrap().borrow();

unpacked.contains_key("Les Misérables");
unpacked.remove("f");

I am unpacking from Arc<ThreadLocal<RefCell<HashMap<String, u32>>>>. But one last problem. I am more or less new to Rust so wanting to learn how to deal with ThreadLocal. Have a background in C/C++ for years -- so bear with me as I find the Rust idiomatic way to do things.

The "remove" on the HashMap complains of | ^^^^^^^^ cannot borrow as mutable.

You need to use borrow_mut to get a mutable reference to the contents of a RefCell.

That worked! Thanks.

Can you explain to me the levels of wrapping and what each is doing. I understand ThreadLocal's function. I think RefCell is basically letting you get a safe reference to what is in it. But Arc is. a bit confusing.
Arc<ThreadLocal<RefCell<HashMap<String, u32>>>>

Sure:

  1. The Arc is an atomic reference counted container. It allows each thread to hold shared ownership of the inner value, and the value is destroyed once the last clone of the Arc goes out of scope. An alternative would be to store it in a global.
  2. The ThreadLocal can be thought of a HashMap<ThreadId, InnerType>.
  3. The RefCell lets you mutate through an immutable reference.

A RefCell is not Sync, so it may not be used from multiple threads at the same time, but the ThreadLocal ensures that this doesn't happen, since each thread has a separate RefCell.

Arc is the thread-safe alternative to Rc, so Arc and Rc are mostly the same functionality-wise and API-wise. (Rc can be more performant, you’ll use it when you don’t need the thread-safety).

There’s a chapter in the Rust book covering Rc and RefCell:

Smart Pointers - The Rust Programming Language

Thanks. That all makes sense. I have one last problem.
let mut tls: ThreadLocal<RefCell<HashMap<String,u32>>> = ThreadLocal::new();

let mut tls_arc = Arc::new(tls);

I am able to pass tls_arc to my thread. But when I got to use it..I get a None panic.
In thread...
let mut unpacked = tls.get().unwrap().borrow_mut();

Where does the actual HashMap<String,u32> get created? I feel like I am missing something when I create the ThreadLocal. Or perhaps I need to create it somehow using tls.get_or() ??

You have to call get_or_default or similar to ask the ThreadLocal to create it if missing.

Yes, you should use get_or whenever accessing the thread local, or at least everywhere that could be the first time the thread local is accessed in the current thread.

Edit: right.. get_or_default works, too. At least when an empty hashmap is what you want to start out with for each new local instance. And in that case it's less typing ^^

Ok all of my Thread Locals worked in my threads. Now I want to unpack them back in the parent. A bit stuck now. How would I unwrap the ThreadLocal above back in the parent thread?

let tls_unwrap = Arc::try_unwrap(tls_arc).unwrap();
let mut stats = tls_unwrap.iter(); // this errors ^^^^ RefCell<HashMap<std::string::String, u32>> cannot be shared between threads safely

Figured it out!!