Workaround missing Send trait for the FFI

Hi! I am trying to use capstone crate, and they recently removed Send support for the Capstone type in dde063b commit. And I have the following code right now:


lazy_static! {
    pub static ref THUMB_DISASSEMBLER: Mutex<Capstone>
        = Mutex::new(Capstone::new()
                              .arm()
                              .mode(arch::arm::ArchMode::Thumb)
                              .build()
                              .expect("Failed to initialize THUMB_DISASSEMBLER"));
}

pub fn disas(insts: &Vec<u8>, mode: Mode, num_insts: usize) -> String {
    let arch = ARCHITECTURE.with_mode(mode);

    let cs = match arch {
        Arch::Arm(Mode::Thumb) => THUMB_DISASSEMBLER.lock().unwrap(),
        _ => panic!("not yet implemented"),
    }

Obviously, during compilation Rust throws an error "*mut std::ffi::c_void cannot be sent between threads safely".
What is the easiest way to workaround the restriction, preferably without modifying the capstone crate?

Wrapping it into Arc still leaves compiler complaining, since it is Mutex that requires Send trait.

It's not my day today ^^ I'm making stupid mistakes. Sorry for the noise :expressionless:

Is you program really multithreaded? If not, you could try to use thread_local instead of lazy_static.

Yes, it is really multithreaded.

If you are really sure that it is save to pass the pointer around you can build a new type, i.e. a wrapper around Capstone. And then implement Send yourself. Maybe you want some dereferencing, too.

struct MyWrapper(Capstone);

unsafe impl Send for MyWrapper {}

impl MyWrapper {
  fn inner(&self) -> &Capstone { &self.0 }
  fn inner_mut(&mut self) -> &mut Capstone { &mut self.0 }
}

However, ba a ware that there are reasons why raw-pointers are not Send! Once I did some FFI like that and was cought by suprise when I realized the C-library I used had some internal memory manageent that was NOT thread safe...

2 Likes

Even in a multithreaded environment, if Capstone is not multithread-safe, using a thread_local is the way to go: it will create a new instance per thread, which is the best you can do. Note that you can then downgrade the Mutex to a RefCell, and looking at Capstone's API, you shouldn't even need it if your builder already sets up the Capstone instance correctly:

thread_local! {
    pub
    static THUM_DISASSEMBLER: Capstone =
        Capstone::new()
            .arm()
            .mode(arch::arm::ArchMode::Thumb)
            .build()
            .expect("Failed to initialize THUMB_DISASSEMBLER")
    ;
}

But depending on you use-case, you might have been using a static to not be bothered with Capstone's annoying 'a everywhere. In that case you can get a &'static Capstone by telling Rust it will never be freed:

#[inline]
pub
fn thumb_disassembler () -> &'static Capstone
{
    thread_local! {
        static THUMB_DISASSEMBLER: &'static Capstone = Box::leak(Box::new(
            Capstone::new()
                .arm()
                .mode(arch::arm::ArchMode::Thumb)
                .build()
                .expect("Failed to initialize THUMB_DISASSEMBLER")
        ));
    }

    THUMB_DISASSEMBLER.with(|&x| x) // `Copy` the `&'static Capstone`
}
2 Likes