Cast a AtomicPtr to *raw::c_void for FFI

I am trying to pass an AtomicPtr to a FFI.

The FFI only get as input a pointer to void, *void.

Is there any way to convert and AtomicPtr to a raw::c_void ?

It is always so hard to pass stuff to FFI, we should really fix this...

You'd want to load the value of the AtomicPtr, then cast that.

Hummm, why would I want that?

Inside the FFI the pointer must be atomic, or at least protected by a lock. If I first load it and then apply the cast I would lose the atomicity, wouldn't I?

I don't understand what you're asking, or what you're trying to do. You can't just pass an AtomicPtr into C and have it magically become atomic in the C code. If all pointer accesses were unavoidably atomic, you wouldn't need AtomicPtr as its own type.

Atomicity affects how the compiler generates code that interacts with a value. It's not something you can impose on external code that was compiled without taking atomicity into account. If the C code is asking for a pointer, you need to give it a pointer, and pointers aren't automatically atomic.

The FFI is asking for a raw pointer, so it isn't asking for an atomic pointer. The callee gets to decide if the pointer is atomic, not the caller. Although, I don't know what your bindings should be if the function asks for an _Atomic pointer.

1 Like

Box and into_raw. You would need to manage ownership since probably would not be using Arc with FFI.
The problem is turning it back to an atomic in the foreign code. You would need to match how rusts atomics operate; i.e. reading source code.

Alternatively: expose some functions keeping the atomic in rust but only the item pointed to transferred over as argument.

@jonh I believe is more complex than just that.

Let me try to explain the whole problem and maybe we can work something out.

I have a series of callbacks, each callback takes as input some predefined parameters plus a *void pointer that can be used to communicate some sort of context.

The trick is that the context pointer must be defined in advance inside a structure.

So the flow is something like this:

  1. Define the structure including a pointer to some context.

Then from now on, each callback will get the context from the above structure.

  1. Set the context (get the lock)
  2. Call the callbacks
  3. Unset the context (release the lock)

This can happen in a multithreaded environment, so on step 2 I should get a lock and on step 4 I should release it.

Everything is complicated by the fact that we are talking mostly with C code, those callbacks and structures are defined via FFI

I'm still unclear as to what's going on here. Assuming that the control flow goes something like: Rust → C → Rust, where the void * is just a pointer to whatever context value you choose (that is, it's used to pass a pointer to an arbitrary thing from your code, through the library, then back to your code again), then it seems like what you actually want to do is define a structure for the context, and pass around a const pointer to that. If that contains an AtomicPtr, then your code is free to access it like it normally would. But an AtomicPtr is just a pointer that can be updated atomically, it's not a synchronisation mechanism for the thing it points to.

1 Like

Yes, exactly, I need to figure out what kind of container use for doing all these passages.

An idea was to just use an AtomicPtr to point to the context structure.

But maybe other structures are better suited...

My point was that an AtomicPtr doesn't help you here. What's atomic is the actual pointer itself, not the thing it points to. The safest thing to do is treat the context void * as a *const Context where you define struct Context. You'll also want to ensure that Context implements Send and Sync, which you should be able to do with a test. Together, that should ensure any operations you perform on the contents of Context are safe... I mean, assuming you also take care of managing the lifetime of the allocation, however you're meant to do that with the C library.

I believe, that the more I think about the less AtomicPtr seems like a sensible solution.

Unfortunately the Context is defined and used in Rust, I don't think that going managing it into the C library is a good solution.

I'd say this is because FFI is kinda hard to do correctly. Regardless of the language. You're essentially forced to do things like you would in C, leaving you without all the nice things that rustc does to ensure correct code.

You may want to have a look at the chapter in my FFI guide about exposing "objects" (i.e. complex data types with methods) across the FFI boundary. The callbacks chapter isn't written yet, but the long term goal is to have something similar to The Nomicon for FFI.

It sounds like what you're actually wanting is a way to synchronize access to an object (the void* context object). Why not make sure all callbacks do synchronization internally? That way C can call your callbacks from whatever thread it wants and you don't have to care because your object is thread-safe internally.

As several people have mentioned before, AtomicPointer doesn't actually synchronize all accesses to an object, it just allows you to swap out raw pointers atomically (e.g. think of a thread-safe linked list).

2 Likes