metacall / core

MetaCall: The ultimate polyglot programming experience.
https://metacall.io
Apache License 2.0
1.55k stars 160 forks source link

Segmentation fault (core dumped): Potential deadlock detected in function_node_interface_invoke #496

Closed hulxv closed 4 months ago

hulxv commented 6 months ago

🐛 Bug Report

During do some benchmarks to decide what the Rust web framework I will use as a HTTP server for the SSR framework project. I made a simple HTTP server using different frameworks and loading this ts script:

import React, { useState, ReactNode } from 'react';
import { renderToString } from 'react-dom/server';

export function Hello(text: string, children: ReactNode) {
        return renderToString(
                <div>
                        <script src="https://cdn.tailwindcss.com"></script>
                        <h1 className="text-4xl font-bold">Hello {text}, </h1>
                </div>
        );
}

using this rust code (for example):

use axum::{response::Html, routing::get, Router};
use metacall;
#[tokio::main]
async fn main() {
    match metacall::initialize() {
        Err(e) => {
            println!("{}", e);
            panic!();
        }
        _ => println!("MetaCall initialized"),
    }

    let scripts = ["App.tsx".to_string()];

    if let Err(e) = metacall::load_from_file("ts", &scripts) {
        println!("{}", e);
        panic!();
    }
    println!("scripts loaded");

    let app = Router::new().route("/", get(root));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:8082").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

// basic handler that responds with a static string
async fn root() -> Html<String> {
    match metacall::metacall("Hello", &[metacall::Any::Str("Metacall!".to_string())]) {
        Err(e) => {
            println!("{}", e);
            panic!();
        }
        Ok(ret) => match ret {
            metacall::Any::Str(message) => Html(message),
            _ => Html("<h1>Not a Valid HTML</h1>".to_string()),
        },
    }
}

I use wrk to do an HTTP benchmark, If I do this benchmark with multiple threads and multiple connections using this command

$ wrk
wrk -t4 -c100 -d3s http://localhost:8082

For some reason the HTTP server crashed and got this errors:

Error: Potential deadlock detected in function_node_interface_invoke, the call has not been executed in order to avoid the deadlock
Error: Potential deadlock detected in function_node_interface_invoke, the call has not been executed in order to avoid the deadlock
Error: Potential deadlock detected in function_node_interface_invoke, the call has not been executed in order to avoid the deadlock
Error: Potential deadlock detected in function_node_interface_invoke, the call has not been executed in order to avoid the deadlock
Error: Potential deadlock detected in function_node_interface_invoke, the call has not been executed in order to avoid the deadlock
[1]    36255 segmentation fault (core dumped)  cargo run --bin axum

The benchmark done successfully just if I used a single thread and one connection

wrk -t1 -c1 -d3s http://localhost:8082

Expected Behavior

Works fine without crashing

Current Behavior

the program crashes

Steps to Reproduce

  1. Run Previous Rust code with script
  2. make an HTTP benchmark using this command:
    $wrk -t4 -c100 -d3s http://localhost:8082

Context (Environment)

OS: Arch Linux x86_64 
Kernel: 6.6.16-1-lts 
CPU: 13th Gen Intel i5-13500H (16) @ 4.7GHz [48.0°on] 
GPU: Intel Raptor Lake-P [UHD Graphics] 
Memory: 7569MiB 
Rust version: 1.76.0 
Metacall-rs (lib): 0.3.0

Detailed Description

check the source code of this experiment if you want

viferga commented 5 months ago

@Hulxv in order to make MetaCall work properly, the metacall_initialize should be called in the main thread. I am not sure how the macro tokio::main modifies this behavior, it may be one reason why this is failing.

Another thing we can do is to use async calls instead of sync ones, this may also help to solve that issue. It would be cool to make an example like this that works out of the box, but I am a bit busy in other parts.

Do you know if it's possible to initialize tokio without that macro, and initialize metacall on the very beginning of the application?

viferga commented 4 months ago

This is partially solved, it needs to be properly refactored and extended to all the loader implementation.. but the workaround is done: https://github.com/metacall/core/pull/502