Multiple JsRuntimes crashe if first is not dropped before a second is created and ran

In my application, I want an op2 to take in a function which will be stored in a struct as a Global<v8::Function>. Later this function will be called. This issue is the application crashes with Memory Access Violation. However, this only occurs on the second instance of the JsRuntime. Additionally this doesn't happen for the first few call, which leads me to believe it is something with the handle be garbage collected.
OC
Outer Cloud52d ago
Here's a simplified source with [...] to show omitted code:
struct RuntimeState {
[...]
advance_function: Option<Global<v8::Function>>,
}

struct ScriptRuntime {
js_runtime: deno_core::JsRuntime,
state: Arc<Mutex<RuntimeState>>,
}

impl ScriptRuntime {
pub fn new() -> ScriptRuntime {
let state = Arc::new(Mutex::new(RuntimeState {
[...]
advance_function: None,
}));

let state_arc = state.clone();

let runtime_extension = Extension::builder("runtime_extension")
.ops(vec![op_register_advance::DECL, [...]])
.state(|extension_state| {
extension_state.put::<Arc<Mutex<RuntimeState>>>(state_arc);
})
.build();

let js_runtime = deno_core::JsRuntime::new(deno_core::RuntimeOptions {
module_loader: Some(Rc::new(TsModuleLoader)),
extensions: vec![runtime_extension],
..Default::default()
});

ScriptRuntime { js_runtime, state }
}

pub fn initialize(&mut self, script: &String) {
self.js_runtime
.execute_script("vector-engine/runtime.ts", deno_core::FastString::from(transpile_ts(String::from(include_str!("./runtime.ts")))))
.unwrap();

self.js_runtime.execute_script("project/clip.ts", deno_core::FastString::from(transpile_ts(script.clone()))).unwrap();

let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();

runtime.block_on(self.js_runtime.run_event_loop(false)).unwrap();
}

// The crash happens when calling this function
pub fn advance(&mut self) {
let state = self.state.lock().unwrap();

let advance = state.advance_function.clone();

if advance.is_none() {
return;
}

let advance = advance.unwrap();

drop(state);
struct RuntimeState {
[...]
advance_function: Option<Global<v8::Function>>,
}

struct ScriptRuntime {
js_runtime: deno_core::JsRuntime,
state: Arc<Mutex<RuntimeState>>,
}

impl ScriptRuntime {
pub fn new() -> ScriptRuntime {
let state = Arc::new(Mutex::new(RuntimeState {
[...]
advance_function: None,
}));

let state_arc = state.clone();

let runtime_extension = Extension::builder("runtime_extension")
.ops(vec![op_register_advance::DECL, [...]])
.state(|extension_state| {
extension_state.put::<Arc<Mutex<RuntimeState>>>(state_arc);
})
.build();

let js_runtime = deno_core::JsRuntime::new(deno_core::RuntimeOptions {
module_loader: Some(Rc::new(TsModuleLoader)),
extensions: vec![runtime_extension],
..Default::default()
});

ScriptRuntime { js_runtime, state }
}

pub fn initialize(&mut self, script: &String) {
self.js_runtime
.execute_script("vector-engine/runtime.ts", deno_core::FastString::from(transpile_ts(String::from(include_str!("./runtime.ts")))))
.unwrap();

self.js_runtime.execute_script("project/clip.ts", deno_core::FastString::from(transpile_ts(script.clone()))).unwrap();

let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();

runtime.block_on(self.js_runtime.run_event_loop(false)).unwrap();
}

// The crash happens when calling this function
pub fn advance(&mut self) {
let state = self.state.lock().unwrap();

let advance = state.advance_function.clone();

if advance.is_none() {
return;
}

let advance = advance.unwrap();

drop(state);
let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();

runtime.block_on(self.js_runtime.call_and_await(&advance)).unwrap();
}
}

[...]

#[op2]
fn op_register_advance(state: &mut OpState, scope: &mut v8::HandleScope, #[global] value: v8::Global<v8::Function>) -> Result<(), AnyError> {
let state_mutex = state.borrow_mut::<Arc<Mutex<ClipRuntimeState>>>();
let mut state = state_mutex.lock().unwrap();

state.advance_function = Some(value);

Ok(())
}

[...]
let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();

runtime.block_on(self.js_runtime.call_and_await(&advance)).unwrap();
}
}

[...]

#[op2]
fn op_register_advance(state: &mut OpState, scope: &mut v8::HandleScope, #[global] value: v8::Global<v8::Function>) -> Result<(), AnyError> {
let state_mutex = state.borrow_mut::<Arc<Mutex<ClipRuntimeState>>>();
let mut state = state_mutex.lock().unwrap();

state.advance_function = Some(value);

Ok(())
}

[...]
(had to split it up due to message length limit)
DAH
Deno AI Helper52d ago
I wasn't able to find the answer to your question in the official Deno documentation. However, based on the information you've provided, it seems like there might be an issue with the way the Global<v8::Function> handle is being managed across different instances of JsRuntime. Without seeing the actual code, it's difficult to provide a precise solution, but here are a couple of general points you might want to consider: 1. Ensure that you are properly managing the lifetime of the Global<v8::Function> handle. If it's being garbage collected prematurely, you might need to look into how you're maintaining references to it. 2. Check if you are correctly creating and disposing of JsRuntime instances. Improper handling of these instances could lead to memory access violations. Please note that this advice is not based on the Deno documentation, and I would advise you to double-check the accuracy of the advice given. It might also be helpful to review the V8 embedding documentation or seek assistance from the community if the problem persists.
OC
Outer Cloud52d ago
After updating the call to instead get the function from the global scope instead of storing it, it still crashes.
pub fn advance(&mut self) {
let binding = self.js_runtime.main_context();

let context = binding.open(&mut self.js_runtime.v8_isolate());

let mut scope = self.js_runtime.handle_scope();

let global = context.global(&mut scope);

let key = v8::String::new(&mut scope, "advance").unwrap();

let advance = global.get(&mut scope, key.into()).unwrap();

let advance = v8::Local::<v8::Function>::try_from(advance).unwrap();

let this = v8::undefined(&mut scope);

advance.call(&mut scope, this.into(), &[]);
}
pub fn advance(&mut self) {
let binding = self.js_runtime.main_context();

let context = binding.open(&mut self.js_runtime.v8_isolate());

let mut scope = self.js_runtime.handle_scope();

let global = context.global(&mut scope);

let key = v8::String::new(&mut scope, "advance").unwrap();

let advance = global.get(&mut scope, key.into()).unwrap();

let advance = v8::Local::<v8::Function>::try_from(advance).unwrap();

let this = v8::undefined(&mut scope);

advance.call(&mut scope, this.into(), &[]);
}
Maybe it has to do with creating multiple Js runtimes...
OC
Outer Cloud52d ago
It does seem like it could be the isolate
No description
OC
Outer Cloud52d ago
No description
OC
Outer Cloud51d ago
I found a fix but I don't understand why it fixes it. If I manually drop first runtime before creating a second, There is no longer a crash. If anyone has any incite into why this happens, it would be helpful
More Posts
Structure microservicesHi, how would you structure a Deno microservice repository with a good developer experience (good dGet image from automated downloadI have this page which displays a render using threejs and renders it into a png and downloads it. iGet image data from automated downloadI have this page which displays a render using threejs and renders it into a png and downloads it. iWait until Deno.Command process has exitedHow would i stop code execution until the process has exited? since its not a promise i cant just doWhy JSX is not working ?// @deno-types="@types/react" import React from "react"; const A = (): React.ReactElement => <div />Running with --cached-only not workingFirst I cache all dependencies of my typescript file: `deno cache --lock=deno.lock --lock-write serSubhosting DeploymentsI've been playing around with deno subhosting. I get the concept of a project and deployments in a pThe new and updated version for using socket.io handler with Deno.serve()Hey there! am getting a error when I pass socket.io handler to Deno.serve(), Here is what am doing :Sorting tailwindcss class names with prettier plugin?When developing in Fresh, I would like to have my class names automatically sorted with the [officiaHow to validate types in API Requests and Responses?Hello, I'm wondering how to validate and handle the types in my app's API. I'll use the example founDeno LSP + React + TypeScript + Vite (--node-modules-dir)Does the Deno LSP works with the --node-modules-dir option? did ```sh deno run --allow-env --allow-deno_runtime NpmResolver like the CLI?As far as I can tell, if I want to embed Deno in my Rust application using deno_core and deno_runtimIs it possible to share the same privateLink in Deno Subhosting?Recently we are trying to implement a "custom js function" module in Deno Subhosting, our concern isSource generation for dependenciesHello, I'm working on a source gen for a library (Node, Deno, Bun) and currently my idea was to overFetch errors outFailed to serve connection: hyper::Error(BodyWrite, Os { code: 55, kind: Uncategorized, message: "NoDeno Jupyter Notebook - monorepo supportHi all! We have a typescript-based monorepo that uses Turbo/Pnpm/etc Our primary reason for coming workspace importI have a vsc root-level settings.json file ```json { "deno.enable": true, "deno.enablePathsHow Enable HTTPS localhost DENO?I want to run Deno Oak over Https in local. I use OpenSsl/mkcert to create cert and key. But no workDeny env permissions silently (without throwing)Hi. An `npm` module I'm using tries to read, or rather check for an existence of an `env` variable.HTTPS Oak DenoI want to run Deno Oak over Https in local. I use OpenSsl to create cert and key. But no works.