aboutsummaryrefslogtreecommitdiff
path: root/worker/src/compute_core.rs
blob: aec2a9dcdefad94983f59dc03be61bf6f18342d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
use std::io;
use std::path::Path;
use std::ffi::c_void;
use libloading::{Library, Symbol};
use static_assertions::assert_eq_size;
use dd_utils::error::*;

pub const CORE_API_VERSION: i32 = 1;

pub struct ComputeCore {
    #[used]
    library: Library,
    // init: unsafe extern "C" fn(i32) -> i32,
    run_job: unsafe extern "C" fn(u64, *mut c_void, *mut u64, *mut *mut c_void) -> i32,
    free_outdata: unsafe extern "C" fn(u64, *mut c_void),
}

impl ComputeCore {
    pub fn load<P: AsRef<Path>>(lib_path: P) -> io::Result<Self> {
        let lib_path = lib_path.as_ref();
        let lib_path = lib_path.canonicalize().map_err(|e| {
            format!("Library '{}' does not exist", lib_path.display()).perror(e)
        })?;

        let library = libloading::Library::new(&lib_path).map_err(|e| {
            format!("Cannot load library at '{}'", lib_path.display()).perror(e)
        })?;

        macro_rules! load_symbol {
            ($name:expr, $symname:expr, $typ:ty) => {{
                let sym: io::Result<Symbol<$typ>> = library.get($symname).map_err(|e| {
                    format!("Failed to load symbol '{}' from library '{}'", $name, lib_path.display()).perror(e)
                });
                sym.map(|s| std::mem::transmute::<*mut c_void, $typ>(s.into_raw().into_raw()))
            }}
        }

        let init =         unsafe { load_symbol!("worker_init", b"worker_init\0", unsafe extern "C" fn(i32) -> i32)? };
        let run_job =      unsafe { load_symbol!("worker_run_job",b"worker_run_job\0", unsafe extern "C" fn(u64, *mut c_void, *mut u64, *mut *mut c_void) -> i32)? };
        let free_outdata = unsafe { load_symbol!("worker_free_outdata", b"worker_free_outdata\0", unsafe extern "C" fn(u64, *mut c_void))? };

        let ret = unsafe {
            init(CORE_API_VERSION)
        };
        if ret != 0 {
            return Err(format!("Library refused initialisation at version {}: '{}'", CORE_API_VERSION, lib_path.display()).ioerr());
        }

        Ok(ComputeCore { library, /*init,*/ run_job, free_outdata })
    }

    /// Will block on this thread, so if you don't want to block, spawn a new thread.
    /// This does not syntactically need mutability, but since one can only run one job at a time
    /// on a core, semantic mutability seems warranted.
    pub fn run_job(&mut self, input_data: &mut [u8]) -> (i32, Vec<u8>) {
        let mut outsize = 0u64;
        let mut outdataptr: *mut c_void = std::ptr::null_mut();

        assert_eq_size!(u64, usize);

        let ret = unsafe {
            (self.run_job)(input_data.len() as u64, input_data.as_mut_ptr() as *mut c_void, &mut outsize, &mut outdataptr)
        };

        let mut result = (ret, Vec::new());

        if outsize != 0 && outdataptr != std::ptr::null_mut() {
            result.1.resize(outsize as usize, 0u8);
            unsafe {
                std::ptr::copy(outdataptr as *const u8, result.1.as_mut_ptr(), outsize as usize);
            }
        }

        unsafe {
            (self.free_outdata)(outsize, outdataptr);
        }

        result
    }
}