1

I pass a Napi::Promise::Deferred to a AsyncWorker, and call Promise::Defered.Resolve() in AynscWork.OnOK. but it always crash when I call Resolve()


// node addon function 

Napi::Promise FileMd5Async(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    auto filePath = info[0].As<Napi::String>().Utf8Value();
    Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
    HashAsyncWorker *hashAsyncWorker = new HashAsyncWorker(env, deferred, filePath);
    hashAsyncWorker->Queue();
    return deferred.Promise();
}

// HashAsyncWorker.h
#pragma once
#include "third_party/node-addon-api/napi.h"

class HashAsyncWorker : public Napi::AsyncWorker {
    
    public:
        HashAsyncWorker(Napi::Env &env, Napi::Promise::Deferred& deferred, std::string filePath);
        ~HashAsyncWorker() override {};

        void Execute() override;
        void OnOK() override;
        void OnError(Napi::Error const &error) override;

    private:
        Napi::Env &env;
        Napi::Promise::Deferred& deferred;
        std::string filePath;
        std::string hashResult;
};

// // HashAsyncWorker.cc
#include "HashAsyncWorker.H"
#include "hash_utils.h"

HashAsyncWorker::HashAsyncWorker(Napi::Env &env, Napi::Promise::Deferred& deferred, std::string filePath) : Napi::AsyncWorker(env), env(env), deferred(deferred), filePath(filePath), hashResult("") {

}

void HashAsyncWorker::Execute() {
    hashResult = hash_utils::FileMd5(filePath);
}

void HashAsyncWorker::OnOK() {
    // crash here  
    //[1]    37056 segmentation fault  
    deferred.Resolve(Napi::String::New(Env(), hashResult)); 
}

void HashAsyncWorker::OnError(Napi::Error const &error) {
    deferred.Reject(error.Value());
}


IS3NY
  • 43
  • 1
  • 5
QuinnChen
  • 670
  • 1
  • 8
  • 29

1 Answers1

1

Right. It crashes because created on the heap and used further by node-addon-api HashAsyncWorker outlives the Deferred object created on the stack of FileMd5Async function.

To prevent crashing you need to make sure deferred lives long enough for onOK function to access a valid value.

In fact, N-API objects, like napi_value, napi_deferred, etc. (of which node-addon-api is a thin wrapper) are pointers, meaning that they can be freely copied.

This way, if you just remove the references that you use to pass deferred around, you will prevent crashing from happening.

Removal of three ampersands in the constructor declaration, implementation and field declaration should be enough:

// HashAsyncWorker.h
#pragma once
#include "third_party/node-addon-api/napi.h"

class HashAsyncWorker : public Napi::AsyncWorker {
    
    public:
        HashAsyncWorker(Napi::Env &env, Napi::Promise::Deferred deferred, std::string filePath);
        ~HashAsyncWorker() override {};

        void Execute() override;
        void OnOK() override;
        void OnError(Napi::Error const &error) override;

    private:
        Napi::Env &env;
        Napi::Promise::Deferred deferred;
        std::string filePath;
        std::string hashResult;
};

// // HashAsyncWorker.cc
#include "HashAsyncWorker.H"
#include "hash_utils.h"

HashAsyncWorker::HashAsyncWorker(Napi::Env &env, Napi::Promise::Deferred deferred, std::string filePath) : Napi::AsyncWorker(env), env(env), deferred(deferred), filePath(filePath), hashResult("") {

}
IS3NY
  • 43
  • 1
  • 5