rust


Drop a Rust void pointer stored in an FFI


I'm wrapping a C API which allows the caller to set/get an arbitrary pointer via function calls. In this way, the C API allows a caller to associate arbitrary data with one of the C API objects. This data is not used in any callbacks, it's just a pointer that a user can stash away and get at later.
My wrapper struct implements the Drop trait for the C object that contains this pointer. What I'd like to be able to do, but am not sure it's possible, is have the data dropped correctly if the pointer is not null when the wrapper struct drops. I'm not sure how I would recover the correct type though from a raw c_void pointer.
Two alternatives I'm thinking of are
Implement the behavior of these two calls in the wrapper. Don't make any calls to the C API.
Don't attempt to offer any kind of safer interface to these functions. Document that the pointer must be managed by the caller of the wrapper.
Is what I want to do possible? If not, is there a generally accepted practice for these kinds of situations?
A naive + fully automatic approach is NOT possible for the following reasons:
freeing memory does not call drop/deconstructors/...: the C API can be used from languages which can have objects which should be deconstructed properly, e.g. C++ or Rust itself. So when you only store a memory pointer you do not know you to call the proper function (you neither know which function not how the calling conventions look like).
which memory allocator?: memory allocation and deallocation isn't a trivial thing. your program needs to request memory from the OS and then manage this resources in an intelligent way to be efficient and correct. This is usually done by a library. In case of Rust, jemalloc is used (but can be changed). So even when you ask the API caller to only pass Plain Old Data (which should be easier to destruct) you still don't know which library function to call to deallocate memory. Just using libc::free won't work (it can but it could horrible fail).
Solutions:
dealloc callback: you can ask the API user to set an additional pointer to, let's say a void destruct(void* ptr) function. If this one is not NULL, you call that function during your drop. You could also use int as an return type to signal when the destruction went wrong. In that case you could for example panic!.
global callback: let's assume you requested your user to only pass POD (plain old data). To know which free function of the memory allocator to call, you could request the user to register a global void (*free)(void* ptr) pointer which is called during drop. You could also make that one optional.
Although I was able to follow the advice in this thread, I wasn't entirely satisfied with my results, so I asked the question on the Rust forums and found the answer I was really looking for. (play)
use std::any::Any;
static mut foreign_ptr: *mut () = 0 as *mut ();
unsafe fn api_set_fp(ptr: *mut ()) {
foreign_ptr = ptr;
}
unsafe fn api_get_fp() -> *mut() {
foreign_ptr
}
struct ApiWrapper {}
impl ApiWrapper {
fn set_foreign<T: Any>(&mut self, value: Box<T>) {
self.free_foreign();
unsafe {
let raw = Box::into_raw(Box::new(value as Box<Any>));
api_set_fp(raw as *mut ());
}
}
fn get_foreign_ref<T: Any>(&self) -> Option<&T> {
unsafe {
let raw = api_get_fp() as *const Box<Any>;
if !raw.is_null() {
let b: &Box<Any> = &*raw;
b.downcast_ref()
} else {
None
}
}
}
fn get_foreign_mut<T: Any>(&mut self) -> Option<&mut T> {
unsafe {
let raw = api_get_fp() as *mut Box<Any>;
if !raw.is_null() {
let b: &mut Box<Any> = &mut *raw;
b.downcast_mut()
} else {
None
}
}
}
fn free_foreign(&mut self) {
unsafe {
let raw = api_get_fp() as *mut Box<Any>;
if !raw.is_null() {
Box::from_raw(raw);
}
}
}
}
impl Drop for ApiWrapper {
fn drop(&mut self) {
self.free_foreign();
}
}
struct MyData {
i: i32,
}
impl Drop for MyData {
fn drop(&mut self) {
println!("Dropping MyData with value {}", self.i);
}
}
fn main() {
let p1 = Box::new(MyData {i: 1});
let mut api = ApiWrapper{};
api.set_foreign(p1);
{
let p2 = api.get_foreign_ref::<MyData>().unwrap();
println!("i is {}", p2.i);
}
api.set_foreign(Box::new("Hello!"));
{
let p3 = api.get_foreign_ref::<&'static str>().unwrap();
println!("payload is {}", p3);
}
}

Related Links

Callback to mutable self
Can't use the conrod library in my Rust project: can't find crate piston_window
Cannot borrow variable when borrower scope ends
Rust RAM limited LRU cache? [closed]
How to determine messages in .rs files generated from rust-protobuf included by include! macro
Replacing a borrowed Arc<RwLock>
How to concatenate a literal string with a const string?
Replacing a borrowed variable
Type hinting on Rust function calls
Nesting an iterator's loops
Do literal integral values have a specific type in Rust?
What value does the variable in the following code snippet have?
How do you import macros in submodules in Rust?
Local let like in caml
E0309: Constraining a generic type parameter's lifetime
Are nested matches a bad practice in idiomatic Rust?

Categories

HOME
ionic2
svn
azure-functions
opencart
paypal-ipn
applescript
biztalk
timber
swarm
nexus3
responsivevoice
bundler
salt-cloud
liferay-6.2
backendless
vuforia
ratio
dragula
nurbs
raima
tf-idf
jackrabbit-oak
basex
unmarshalling
jsonresult
windowsiot
wampsharp
universal
rspec-rails
overwrite
xmlunit
apptentive
restful-architecture
autoencoder
netstat
bluemixtools
testlink
mv
klee
get-event-store
access-denied
yoast
httphandler
sendinput
ipp-protocol
realstudio
image-registration
case-when
nitrousio
mifos
scheduledexecutorservice
multi-touch
bigdecimal
xmlbeans
gcloud-node
maven-release-plugin
bbc-micro
meld
mathnet
jeditable
distributed-cache
utf-16
mousehover
openshift-cartridge
mixins
enaml
libz
ssmtp
findersync
kognitio-wx2
fasterxml
hp-idol-ondemand
windows-rt
lmax
oracle-adf-mobile
prism.js
refit
tabletools
boolean-algebra
jquery-transit
eager-loading
proc-open
session-0-isolation
navigationcontroller
mmc3
mongrel2
ninject-interception
resharper-5.1
3-tier
ixmldomdocument
eventaggregator
eai
autobench

Resources

Database Users
RDBMS discuss
Database Dev&Adm
javascript
java
csharp
php
android
javascript
java
csharp
php
python
android
jquery
ruby
ios
html
Mobile App
Mobile App
Mobile App