gorules / zen

Open-source Business Rules Engine for your Rust, NodeJS, Python or Go applications.

Home Page:https://gorules.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Issue evaluating inside an async fn with + Send

iman38 opened this issue · comments

Hi @stefan-gorules ,

I found that I cannot evaluate inside an async fn having + Send trait (for thread safety). Below is a simple snippet to reproduce the issue. It's complaining about QuickJS runtime. I tried forking and changing the runtime (and context) to AsyncRuntime & AsyncContext, but found the problem on bumpalo as well. I would like to know if I miss anything I cannot find on the doc to do this properly, or if this is not supported yet.

p.s. please ignore the fact that the JSON file is not attached. However, this should reproduce a compile error.

use async_trait::async_trait;
use serde_json::json;
use std::env;
use std::fs::File;
use std::io::BufReader;
use welfares_common::AirlineError;
use zen_engine::model::DecisionContent;
use zen_engine::DecisionEngine;

#[async_trait]
pub trait AirlineConnector {
    async fn search(&self) -> Result<bool, AirlineError>;
}

pub struct TestController {
    pub id: String,
}

impl TestController {
    pub fn new(id: &str) -> TestController {
        TestController { id: id.to_string() }
    }
}
#[async_trait]
impl AirlineConnector for TestController {
    async fn search(&self) -> Result<bool, AirlineError> {
        let wf_env = env::var("WF_ENV").expect("Environment variable WF_ENV not found");
        let file = File::open(format!("config_{}/test_rule.json", wf_env))
            .expect("file commission_rule.json should be able to be open read only");
        let reader = BufReader::new(file);

        let search_decision_content: DecisionContent = serde_json::from_reader(reader).unwrap();
        let engine = DecisionEngine::default();
        let comm_decision = engine.create_decision(search_decision_content.into());
        let commission_param = json!({});
        let result = comm_decision.evaluate(&commission_param).await;
        Ok(true)
    }
}

#[tokio::main]
async fn main() {
    let c = TestController::new("new controller");
    c.search().await.expect("TODO: panic message");
}

Some errors I saw:

error[E0277]: `Rc<rquickjs_core::safe_ref::Mut<rquickjs_core::runtime::raw::RawRuntime>>` cannot be sent between threads safely
   --> src/bin/ts_zen.rs:26:58
    |
26  |       async fn search(&self) -> Result<bool, AirlineError> {
    |  __________________________________________________________^
27  | |         let wf_env = env::var("WF_ENV").expect("Environment variable WF_ENV not found");
28  | |         let file = File::open(format!("config_{}/test_rule.json", wf_env))
29  | |             .expect("file commission_rule.json should be able to be open read only");
...   |
38  | |         Ok(true)
39  | |     }
    | |     ^
    | |     |
    | |_____`Rc<rquickjs_core::safe_ref::Mut<rquickjs_core::runtime::raw::RawRuntime>>` cannot be sent between threads safely
    |       within this `{async block@src/bin/ts_zen.rs:26:58: 39:6}`
    |
    = help: within `{async block@src/bin/ts_zen.rs:26:58: 39:6}`, the trait `Send` is not implemented for `Rc<rquickjs_core::safe_ref::Mut<rquickjs_core::runtime::raw::RawRuntime>>`, which is required by `{async block@src/bin/ts_zen.rs:26:58: 39:6}: Send`
note: required because it appears within the type `rquickjs_core::runtime::base::Runtime`

*mut bumpalo::Bumpcannot be sent between threads safely [E0277] Help: within{async block@src/bin/ts_zen.rs:26:58: 38:6}, the trait std::marker::Sendis not implemented for*mut bumpalo::Bump, which is required by {async block@src/bin/ts_zen.rs:26:58: 38:6}: std::marker::SendHelp: the traitstd::marker::Sendis implemented forbumpalo::BumpNote: required because it appears within the typezen_expression::arena::UnsafeArena<'>Note: required because it appears within the typezen_expression::isolate::Isolate<'>Note: required because it appears within the typezen_engine::handler::table::zen::DecisionTableHandler<'_>Note: required because it's used within thisasyncfn body Note: required because it's used within thisasyncfn body Note: required because it's used within thisasyncfn body Note: required because it's used within thisasyncblock Note: required for the cast fromstd::pin::Pin<std::boxed::Box<{async block@src/bin/ts_zen.rs:26:58: 38:6}>>toPin<Box<dyn Future<Output = Result<bool, AirlineError>> + Send>>`

Hi @iman38, you are getting error above because it's not safe for async to be running across multiple threads. You need to use a function such as block_in_place. See #112.

@iman38 Closing issue for now, feel free to re-open if problem still persists.