Lifetimes and passing between threads

Hi folks. I'm writing a multithreaded program and I want to have a thread reading notifications from a Postgres connection while other threads make database calls on the same connection. I'm using the postgres crate, and the Notifications struct appears to have a lifetime parameter that's tied to the connection's lifetime.

Here's a minimal example of what I'd like to do:

use postgres as pg;

struct Foo<'n>(pg::Notifications<'n>);

fn main() {
	let mut conn: pg::Client = None.unwrap();
	let mut f = Foo(conn.notifications());
	std::thread::spawn(move || {
		f.0.blocking_iter();
	});
}

The error message doesn't actually mention the closure or thread::spawn, but when I remove that call it compiles, so clearly the problem is moving the Notifications struct between threads. My guess is the right way to work around this is to send a raw pointer to the Notifications struct, which I gather would require implementing Send/Sync manually. I also came across the fragile crate, which I thought might be useful? What's the best way to make this work?

The right way is to use a scoped thread, like from a crossbeam::scope.

2 Likes

The std::thread::spawn() function requires its arguments to be 'static. That means the closure you pass in can't have any references to state from the caller's stack frame. In this case, conn.notifications() holds a reference to conn.

As an alternative to using scoped threads, I would put the pg::Client behind an Arc and pass a copy of that Arc<pg::Client> into std::thread::spawn(). From there you can call conn.notifications() to get a stream of notifications. This works because an Arc<pg::Client> moves the pg::Client to the heap so it is no longer tied to the calling stack frame.

Using shared pointers means your other threads can also be given access to the pg::Client, then whenever the last Arc<pg::Client> is dropped it'll automatically free the pg::Client and do any cleanup logic.

2 Likes

Okay, thanks all for the assistance. I think the reason I wasn't able to figure out how to make this work originally was that the compiler wasn't letting me move my pg::Client, which I had wrapped in Arc and RwLock, between threads. Still not sure why this would be, but Arc and Mutex seem to make the compiler happy, so I can move ahead.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.