Confused about async and crossbeam-epoch

I'm currently working with tokio to build a service (as an attempt to replace parts of an aging C++ monolith) that itself holds a lot of data. We built a structure that utilizes crossbeam-epoch for reclamation (since in our specific use case, we can't rely on locks and too much spinning). Now, since EBR relies on threads being pinned and unpinned, will it work with async since here we're dealing with tasks and no explicit threads (...hence, no threads are actually unpinned?).

The service, atleast on a conceptual level has an async fn main() (annotated with #[tokio::main]) which accepts UDP requests in a loop, and then uses the data structure to read/write any relevant data and perform any computations. Will destructor calls for the data structure (which uses CB epoch) actually never be evaluated because of the async task situation I described above? Or am I thinking incorrectly?

Any help is appreciated!

Tokio gives you a choice between single-threaded and multi-threaded runtime. If you spawn futures on the multi-threaded, obviously you won't control which thread gets it.

If you have to use a multi-threaded runtime, you can either use LocalSet to spawn limited futures on the current thread, or create a separate additional single-threaded tokio runtime, keep its Handle, and carefully use it to spawn the futures depending on the specific thread.

However, note that futures can change threads when they're spawned and then only at .await points. Code running in between .await is completely synchronous, and doesn't get any pre-emptive interruption. If you'd be careful to keep pin Guard only between awaits, it might work? Although I'm not that familiar with crossbeam.

1 Like

Note that Guard is !Send, so you won't be able to keep it across .await points of tasks spawned with tokio::task::spawn

Thanks for the information. As you have correctly guessed, Iā€™m using the multi-threaded runtime.

In that case, will EBR collections trigger at all?

Yes, EBR will trigger. If you call guard.pin(); op(); drop(guard);, it doesn't matter whether you're running within the context of an async task, crossbeam-epoch will pin/unpin the current thread using thread-local storage. Because you don't hold the guard across an await point, the operation is performed synchronously and there's no difference from crossbeam-epoch's perspective. When the async task on that same thread is replaced, it's as if you continued to perform operations from the same thread.

1 Like

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.