thread_cell 0.2.2

Safe, Send + Sync access to !Send/!Sync data by isolating it on a dedicated thread and interacting with it through message passing. Perfect for Rc, RefCell, and other single-threaded types.
Documentation
# thread_cell

[<img alt="github" src="https://img.shields.io/badge/github-mcmah309/thread_cell-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/mcmah309/thread_cell)
[<img alt="crates.io" src="https://img.shields.io/crates/v/thread_cell.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/thread_cell)
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-thread_cell-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/thread_cell)
[<img alt="test status" src="https://img.shields.io/github/actions/workflow/status/mcmah309/thread_cell/rust.yml?branch=master&style=for-the-badge" height="20">](https://github.com/mcmah309/thread_cell/actions/workflows/rust.yml)

**thread_cell** is a Rust crate that gives you **safe, `Send`/`Sync` access to non-`Send`/`Sync` data** by isolating it on a dedicated thread and interacting with it through message passing.

Unlike `Arc<Mutex<T>>`, `ThreadCell<T>` does not require `T: Send + Sync`, yet you can still share it across threads. This makes it perfect for types like `Rc`, `RefCell`, or FFI handles that are not `Send`.

---

## Why?

Sometimes you have data that **must stay on a single thread** (e.g. `Rc`, `RefCell`, or certain graphics/audio/DB handles).
But you still want to send it around safely — possibly to other threads — and run operations on it in a serialized manner.

### Example: making `Rc` safely shareable across threads

```rust
use std::rc::Rc;
use std::cell::RefCell;

fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}

struct NonSendSync {
    parent: Rc<RefCell<NonSendSync>>,
    children: Vec<Rc<RefCell<NonSendSync>>>,
}

use thread_cell::ThreadCell;

// ✅ Compiles — ThreadCell makes `NonSendSync` shareable across threads
assert_send::<ThreadCell<NonSendSync>>();
assert_sync::<ThreadCell<NonSendSync>>();

// ❌ Does not compile — Arc<Mutex<T>> requires T: Send
// assert_send::<Arc<std::sync::Mutex<NonSendSync>>>();
// assert_sync::<Arc<std::sync::Mutex<NonSendSync>>>();
```

## Example

```rust
use std::rc::Rc;
use std::cell::RefCell;
use thread_cell::ThreadCell;

#[derive(Debug)]
struct GameState {
    score: usize,
}

fn main() {
    // Rc<RefCell<_>> is !Send and !Sync
    let shared = ThreadCell::new_with(|| Rc::new(RefCell::new(GameState { score: 0 })));

    // synchronous access
    shared.run_blocking(|state| {
        state.borrow_mut().score += 10;
        println!("Score after sync update: {}", state.borrow().score);
    });

    // async access - `tokio` feature flag
    let rt = tokio::runtime::Runtime::new().unwrap();
    rt.block_on({
        let shared = shared.clone();
        async move {
            let a = shared.clone();
            let b = shared;

            let t1 = tokio::spawn(async move {
                a.run(|state| {
                    state.borrow_mut().score += 5;
                    state.borrow().score
                })
                .await
            });

            let t2 = tokio::spawn(async move {
                b.run(|state| {
                    state.borrow_mut().score += 20;
                    state.borrow().score
                })
                .await
            });

            let (s1, s2) = tokio::join!(t1, t2);
            println!("Task 1 score: {}", s1.unwrap());
            println!("Task 2 score: {}", s2.unwrap());
        }
    });

    let final_score = shared.run_blocking(|state| state.borrow().score);
    println!("Final score: {final_score}");
}
```
```console
Score after sync update: 10
Task 1 score: 15
Task 2 score: 35
Final score: 35
```

## When to Use ThreadCell

✅ You have a **non-`Send` type** but still want to share and mutate it safely across threads.
✅ You want **serialized access** without locks (no risk of deadlocks).
✅ You need **async-friendly** access to such data.

❌ Use `Arc<Mutex<T>>` if `T: Send + Sync` and you just want a regular concurrent lock.

---

## Performance Notes

* Each call involves **message passing** to a background thread. This may be faster than a contended mutex for many workloads, but also may be slower for very fine-grained access patterns.

---

## Safety

`ThreadCell<T>` guarantees:

* All access to `T` happens on the same thread.
* No data races, even if `T` is `!Send` or `!Sync`.