How to kill a thread

I have a program where I execute third-party JavaScript code using deno. The objective is to execute the JavaScript code for 1 second max and if the code takes too long to execute, stop executing it and return.

I have been searching how to do that.

I am using the following code to do so

  • Create a Mutex that will contain the JS result with a condition variable
  • Create a thread that execute the JS code
  • Wait on the condition variable until the 1000 ms delay expires
  • If the JS code is executed, post on the condition variable to release the thread
  • drop the create thread for the JS execution

The objective is to make sure the JS code that execute the infinite loop is killed. And that is done with drop.

Is the solution sound? Or should I proceed differently?

    let pair = Arc::new((Mutex::new(None), Condvar::new()));
    let pair2 = Arc::clone(&pair);

    let thr = thread::spawn(move || {
        let (lock, cvar) = &*pair2;

        yield_now();
        let res = execute_javascript_code(code);
        let mut started = lock.lock().unwrap();
        *started = Some(res);
        cvar.notify_one();
    });

    let (lock, cvar) = &*pair;
    let started = lock.lock().unwrap();

    let cond_result = cvar.wait_timeout(started, Duration::from_millis(1000));

    drop(thr);
    match cond_result {
        Ok(v) => {
            if v.1.timed_out() {
            }
        }
  }

No. Dropping JoinHandle returned by thread::spawn() makes the thread keep running in background. There's no reliable way to kill running thread externally. But you can kill external processes. Why don't you invoke deno as a separate process and kill it on timeout?

3 Likes

If your not going down the external process route then you have to dig into how deno works.
terminate_execution might be what you want. Either would need someone with knowledge of how to get there and/or spend time reading the code.

1 Like

The problem with the external process route is the associated cost. I am running thousands time this routine and do not want to start thousands of processes. I will investigate the terminate_execution route.

The problem with the terminate_execution is that I need to share a mutable JsRuntime between threads. The JsRuntime also uses raw pointers and when I try to have a program and I use the runtime in the thread, I get

*const c_void` cannot be sent between threads safely

Crates like fragile only share non-mutable reference.

Any idea about how to share mutable reference of a value that has raw pointers between thread?

I'm a bit confused, where's JsRuntime coming from? The link goes to IsolateHandle, which is the thing you would hand out to kill the execution, and that's from an Isolate, which should live forever on one thread. So if you want to hand out the handle to terminate execution you need to do something like:

let (tx, rx) = channel();
spawn(|| {
  let isolate = Isolate::new(...);
  tx.send(isolate.thread_safe_handle());
  ...
});
let handle = rx.recv();
...
handle.terminate_execution();

channel here is probably the thing you're missing: channel in std::sync::mpsc - Rust

2 Likes

Thank you for your answer and thank you so much for your suggestion! You are correct about the JsRuntime. It was declared in the execute_javascript_code function and I need to bring it back outside of the function.

I will try what you say and report if it works here!

1 Like

Thank you so much for your help, it worked!