gpt4 book ai didi

node.js - 使 Node.js 退出,无论 native 模块异步调用如何挂起

转载 作者:太空宇宙 更新时间:2023-11-04 01:23:52 25 4
gpt4 key购买 nike

我正在写一个Napi基于 Node.js 的模块。

该模块使用 WINAPI WaitForSingleObject(pid)。阻塞 WINAPI 调用包装在 Napi::AsyncWorker 中。

问题

异步调用会阻止 Node.js 退出。我希望 Node.js 在无事可做时退出,就像 child_process.unref() 那样。所以我想取消引用 Node.js 事件循环中的异步调用。

最佳答案

我没有时间制作它的 NPM 包,但这是我的解决方案。该解决方案对于所有阻塞系统调用都有效(将调用线程置于等待状态,如 Sleep() 所做的那样)。

这个想法是:

  1. 使用std::thread用于阻止 WINAPI 调用。
  2. 使用napi_threadsafe_function从新线程安全地回调到 Javascript。
  3. 使用napi_unref_threadsafe_function从 Node.js 事件循环中取消引用异步操作。

更多详细信息:

  • 为了安全地同时调用,请使用线程特定的数据创建一个新的 void* 上下文,并将其传递(受 N-API 支持)。
  • 对于未引用的 napi_threadsafe_function,支持终结器。在此处停止等待线程,以防止 Node.js 退出时出现崩溃消息。
  • 我使用 C N-API和 C++ node-addon-api可以互换。不幸的是,在撰写本文时纯 C++ 解决方案是不可能的,因为 napi_unref_threadsafe_function 只能通过 C API 获得。
  • addon.cc:

        #include <napi.h>
    #include <node_api.h>
    #include <windows.h>
    #include <tlhelp32.h>
    #include <sstream>
    #include <string>
    #include <iostream>
    #include <thread>

    using namespace std;
    using namespace Napi;

    struct my_context {
    std::thread nativeThread;
    HANDLE hStopEvent;
    napi_threadsafe_function tsfn;
    };

    std::string WaitForPid(int pid, my_context* context, bool* stopped) {
    HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid);
    if (process == NULL)
    {
    std::stringstream stream;
    stream << "OpenProcess failed: " << (int)GetLastError();
    return stream.str();
    }
    context->hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (process == NULL)
    {
    std::stringstream stream;
    stream << "CreateEvent failed: " << (int)GetLastError();
    return stream.str();
    }
    HANDLE hEvents[2] = {process, context->hStopEvent};
    DWORD waitResult = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
    if (waitResult == WAIT_FAILED)
    {
    std::stringstream stream;
    stream << "WaitForSingleObject failed: " << (int)GetLastError();
    return stream.str();
    } else if (waitResult == WAIT_OBJECT_0 + 1) {
    *stopped = true;
    }
    return std::string();
    }

    Value WaitForProcessToExit(CallbackInfo &info)
    {
    Env env = info.Env();

    if (info.Length() < 3) {
    TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException();
    return env.Null();
    }

    if (!info[0].IsNumber() || !info[1].IsBoolean() || !info[2].IsFunction()) {
    TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
    return env.Null();
    }

    int pid = info[0].As<Number>().Int32Value();
    bool unref = info[1].As<Boolean>();
    Function func = info[2].As<Function>();

    // Do async stuff

    my_context* context = new my_context();

    NAPI_THROW_IF_FAILED(env, napi_create_threadsafe_function(
    (napi_env) env,
    (napi_value) func,
    NULL,
    (napi_value) String::New(env, "WaitForProcessToExit"),
    0,
    1,
    NULL,
    [](napi_env env, void* finalize_data, void* finalize_hint /* Context attached to the TSFN */) {
    SetEvent(((my_context*)finalize_hint)->hStopEvent);
    ((my_context*)finalize_hint)->nativeThread.join();
    delete finalize_hint;
    },
    context, // Context attached to the TSFN
    [](napi_env env, napi_value js_callback, void* context, void* data) {
    std::string* error = (std::string*)data;
    // Transform native data into JS data, passing it to the provided `js_callback` (the TSFN's JavaScript function)
    napi_status status = napi_ok;

    napi_value global;
    status = napi_get_global(env, &global);
    if (status != napi_ok)
    {
    std::cout << "napi_get_global failed" << std::endl;
    }

    napi_value result;
    if (error->empty()) {
    status = napi_call_function(env, global, js_callback, 0, NULL, &result);
    } else {
    napi_value values[] = { (napi_value)Error::New(env, *error).Value() };
    status = napi_call_function(env, global, js_callback, 1, values, &result);
    }
    delete data;
    if (status != napi_ok)
    {
    std::cout << "napi_call_function failed" << std::endl;
    }
    status = napi_release_threadsafe_function(((my_context*)context)->tsfn, napi_tsfn_release);
    if (status != napi_ok)
    {
    std::cout << "napi_release_threadsafe_function failed" << std::endl;
    }
    },
    &(context->tsfn)), env.Undefined());

    context->nativeThread = std::thread([pid, context] {
    bool stopped = false;
    std::string error = WaitForPid(pid, context, &stopped);

    if (stopped) {
    return;
    }

    napi_status status = napi_call_threadsafe_function(context->tsfn, new std::string(error), napi_tsfn_blocking);
    if (status != napi_ok)
    {
    std::cout << "napi_call_threadsafe_function failed" << std::endl;
    }
    });

    if (unref) {
    NAPI_THROW_IF_FAILED(env, napi_unref_threadsafe_function((napi_env) env, context->tsfn), env.Undefined());
    }

    return env.Undefined();
    }

    Object Init(Env env, Object exports)
    {
    exports.Set(String::New(env, "WaitForProcessToExit"), Function::New(env, Winapi::WaitForProcessToExit));
    return exports;
    }

    NODE_API_MODULE(hello, Init)

    关于node.js - 使 Node.js 退出,无论 native 模块异步调用如何挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58241265/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com