« All posts

01.31.2022 - Deno / Non-blocking FFI Calls

Sometimes, we want to call an FFI function that is CPU-bound. This would block the main thread. For example, in this sleep_and_scream method from Rust, we use std::thread::sleep to hang the function for a while, then print out some text:

use std::{thread, time::Duration};
 
#[no_mangle]
pub fn sleep_and_scream(millis: u64) {
    thread::sleep(Duration::from_millis(millis));
    println!("WAAAAAAAAAAAA!!!!!!!!!!!");
}

Import and call this function in Deno would cause the program to hang for a few seconds before everything can be printed out:

const libName = './target/debug/libdeno_blocking_demo.dylib';
 
const lib = Deno.dlopen(libName, {
    sleep_and_scream: {
        parameters: ["u64"],
        result: "void",
    }
});
 
lib.symbols.sleep_and_scream(2000);
console.log("It's Deno!!!!");

The output:

<hang for 2 seconds>
WAAAAAAAAAAAA!!!!!!!!!!!
It's Deno!!!!

From version 1.15, Deno supports marking an FFI function as nonblocking, which will make the function run on a dedicated blocking thread and return a Promise.

const libName = './target/debug/libdeno_blocking_demo.dylib';
 
const lib = Deno.dlopen(libName, {
    sleep_and_scream: {
        parameters: ["u64"],
        result: "void",
        nonblocking: true // <-- add this
    }
});
 
lib.symbols.sleep_and_scream(2000)
   .then(() => console.log("Finished!")); // <-- It's a promise now
console.log("It's Deno!!!!");

With this updated program, the output would be:

It's Deno!!!!
<hang for 2 seconds>
WAAAAAAAAAAAA!!!!!!!!!!!
Finished!

Read more