Description
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.
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?