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...
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.
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:
Then from now on, each callback will get the context from the above structure.
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.
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).