[forwarded to users.rust-lang] Requiring T: !Sync

I'm currently trying to get per-thread-object to work for my program, which works with a scoped function like .with(FnOnce(&T) -> R) -> R, allowing &T to "not escape" the thread bounds.

However, I require &T to live for longer than that, see the following simple bit of code;

    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = TupleOfBytes> + 'a> {
        let guard: &'a Connection = self.engine.iter_reader();

        self.iter_with_guard(guard)
    }

This is part of implementing a trait, so rewriting it isn't feasable.

However, we'd still like thread-locals, to conserve resources on initiating and maintaining multiple connections;

pub struct Engine {
    writer: Mutex<Connection>,

    // rusqlite::Connection: Send + !Sync
    read_conn_tls: ThreadLocal<Connection>,
    read_iterator_conn_tls: ThreadLocal<Connection>,

    ...

}

std::thread_local and thread-local have already been considered and rejected for different reasons (std doesn't free all objects on program exit, which is required for some durability. And thread-local never frees TLS objects after their threads die.), so that brings me to per-thread-object, with a following small modification;

// This borrows a TLS with a lifetime only as long as the TLS itself,
// this assures it does not outlive the TLS (or the structure its borrowed from),
//
// SAFETY: ThreadLocal assures that the value is never dropped as long as it or the thread lives.
//
// However, that means that T needs to be !Sync, there is no stable way of conveying this,
// so extra caution needs to be taken on the caller's side to signal this.
unsafe fn robbin_hood_tls<'tls, T: Send, I: FnOnce() -> T>(
    tls: &'tls ThreadLocal<T>,
    init: I,
) -> &'tls T {
    tls.with_or(
        |b| unsafe {
            let borrow: &'tls T = std::mem::transmute(b);

            return borrow;
        },
        init,
    )
}

This unsafe function has a massive "gotcha"; manually checking if T: !Sync, else the borrowed value can have a use-after-free (if it gets sent across threads, and the original thread dies).

It is only used in the following bit of code in Engine;


    fn iter_reader<'a>(&'a self) -> &'a Connection {
        let init = || Self::prepare_conn(&self.path, self.cache_size_per_thread).unwrap();

        // This is required to coerce a "good" lifetime for iterators,
        // which need a borrow to the connection to stay alive as long as they are.
        //
        // Unfortunately, we're giving away ownership of those iterators to the caller,
        // and so with a normal ThreadLocal we can't scope with `with()`.
        //
        // SAFETY: Connection is !Sync, so the borrow is !Send,
        // which assures it stays in the same thread, preventing use-after-free.
        unsafe { robbin_hood_tls(&self.read_iterator_conn_tls, init) }
    }

...but, understandably, i'd be more happier if this didn't require so many hazmat gloves, and i could let the compiler check itself if T: !Sync for this required "contract".


So my question is; Is there any way, existing in stable, existing in nightly, or up-and-coming, that could let me require that T: !Sync on robbin_hood_tls?

Oh apologies, i thought i was on users.rust-lang.org :man_facepalming:

I've posted this issue over at Requiring T: !Sync - help - The Rust Programming Language Forum, this thread can be closed/ignored.

1 Like