Skip to content

Segfault when using node-addon-api Objects during AsyncWorker::Execute #281

Closed
@webern

Description

@webern

In the following project:

https://github.com/bitflip-software/xlsx-util/tree/feature/async-worker
branch feature/async-worker
f3b31420281e5f5939ad082c6835f024c14cd728

When I run the project's test with npm run test, I get a segfault.

I've been chasing this down for days, and today I built Node from source (v9.8) so that I could debug.

It appears that all of the objects my code is operating on are being destroyed when uv__fs_done is called, which calls the suspicious looking node::InternalCallbackScope::Close()

But my code is still running on another thread, which ends in tears.

image

My AsyncWorker looks like this

#include "AsyncReader.h"
#include "XlsxReaderFunctions.h"

namespace xlsx
{
    AsyncReader::AsyncReader( Napi::HandleScope scope, const std::string& filename, bool hasHeaders, const Napi::Function& headerTransform, const Napi::Function& callback )
    : Napi::AsyncWorker{ callback }
    , myScope{ scope }
    , myFilename{ filename }
    , myHasHeaders{ hasHeaders }
    , myHeaderTransform{ headerTransform }
    , myResult{}
    {

    }


    void
    AsyncReader::Execute()
    {
        myResult = extractAllData( myScope, myFilename, myHasHeaders, myHeaderTransform );
    }


    void
    AsyncReader::OnOK()
    {
        Callback().MakeCallback( Receiver().Value(), { myScope.Env().Null(), myResult } );
    }


    void
    AsyncReader::OnError( const Napi::Error& e )
    {
        Callback().MakeCallback( Receiver().Value(), { e.Value(), myScope.Env().Undefined() } );
    }
}

The AsyncReader is instantiated and queued like this:

#include "napi.h"
#include "readXlsxAsync.h"
#include "AsyncReader.h"
#include "AsyncError.h"

namespace xlsx
{
    void readXlsxAsync( const Napi::CallbackInfo& info )
    {
        Napi::Env env = info.Env();
        Napi::HandleScope scope{ env };

        // we need to check for the presence of a client callback function
        // and if we do not have one then we must raise a synchronous error
        if( info.Length() != 4 )
        {
            Napi::TypeError::New(env, "invalid argument count").ThrowAsJavaScriptException();
            return;
        }

        if( !info[3].IsFunction() )
        {
            Napi::TypeError::New(env, "invalid callback function [arg3]").ThrowAsJavaScriptException();
            return;
        }

        Napi::Function cb = info[3].As<Napi::Function>();

        if( !info[0].IsString() )
        {
            auto err = new AsyncError{ cb, "first argument must be a string [filename]" };
            err->Queue();
            return;
        }

        if( !info[1].IsBoolean() )
        {
            auto err = new AsyncError{ cb, "second argument must be a bool [hasHeaders]" };
            err->Queue();
            return;
        }

        if( !info[2].IsFunction() )
        {
            auto err = new AsyncError{ cb, "third argument must be a function [headerTransform]" };
            err->Queue();
            return;
        }

        const std::string filename = info[0].As<Napi::String>().Utf8Value();
        const bool hasHeaders = info[1].As<Napi::Boolean>();
        Napi::Function headerTransform = info[2].As<Napi::Function>();
        AsyncReader* reader = new AsyncReader{ scope, filename, hasHeaders, headerTransform, cb };
        reader->Queue();
    }
}

xlsx::extractAllData runs synchronously on a single thread (as far as I understand, though it does create Napi::Objects.

Bottom line, I can't understand why my code segfaults because the lifetime management of these libraries is very complicated and I'm new to this (and Napi:: doesn't seem to have much documentation).

When does an AsyncWorker destroy itself exactly? Can anyone tell me why my code segfaults?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions