From d094f8c546130b0cd85105f836de4101b6137eaa Mon Sep 17 00:00:00 2001 From: Tyler Ang-Wanek Date: Wed, 8 Apr 2020 08:49:11 -0700 Subject: [PATCH 1/2] Revert Napi changes --- binding.gyp | 38 ++-- includes/NSFW.h | 103 ++++++----- package.json | 2 +- src/NSFW.cpp | 465 +++++++++++++++++++++++++----------------------- yarn.lock | 10 +- 5 files changed, 321 insertions(+), 297 deletions(-) diff --git a/binding.gyp b/binding.gyp index 33eedc3d..36015fac 100644 --- a/binding.gyp +++ b/binding.gyp @@ -5,27 +5,22 @@ "sources": [ "src/NSFW.cpp", "src/Queue.cpp", - "src/NativeInterface.cpp" + "src/NativeInterface.cpp", + "includes/NSFW.h", + "includes/Queue.h", + "includes/NativeInterface.h" ], "include_dirs": [ - "includes", - " -#include -#include -#include -#include +#include "Queue.h" +#include "NativeInterface.h" +#include +#include #include +#include -#include "./Queue.h" -#include "./NativeInterface.h" - -class NSFW : public Napi::ObjectWrap { - private: - static Napi::FunctionReference constructor; - static std::size_t instanceCount; - static bool gcEnabled; +using namespace Nan; - uint32_t mDebounceMS; - Napi::ThreadSafeFunction mErrorCallback; - Napi::ThreadSafeFunction mEventCallback; - std::unique_ptr mInterface; - std::mutex mInterfaceLock; - std::shared_ptr mQueue; - std::string mPath; - std::thread mPollThread; - std::atomic mRunning; +class NSFW : public ObjectWrap { +public: + static NAN_MODULE_INIT(Init); - class StartWorker: public Napi::AsyncWorker { - public: - StartWorker(Napi::Env env, NSFW *nsfw); - void Execute(); - void OnOK(); - Napi::Promise RunJob(); + static void fireErrorCallback(uv_async_t *handle); + static void fireEventCallback(uv_async_t *handle); + static void pollForEvents(void *arg); - private: - enum JobStatus { STARTED, ALREADY_RUNNING, COULD_NOT_START, JOB_NOT_EXECUTED_YET }; - Napi::Promise::Deferred mDeferred; - NSFW *mNSFW; - JobStatus mStatus; - }; + Persistent mPersistentHandle; +private: + NSFW(uint32_t debounceMS, std::string path, Callback *eventCallback, Callback *errorCallback); + ~NSFW(); - Napi::Value Start(const Napi::CallbackInfo &info); + uint32_t mDebounceMS; + uv_async_t mErrorCallbackAsync; + uv_async_t mEventCallbackAsync; + Callback *mErrorCallback; + Callback *mEventCallback; + NativeInterface *mInterface; + uv_mutex_t mInterfaceLock; + bool mInterfaceLockValid; + std::string mPath; + uv_thread_t mPollThread; + std::atomic mRunning; + std::shared_ptr mQueue; - class StopWorker: public Napi::AsyncWorker { - public: - StopWorker(Napi::Env env, NSFW *nsfw); - void Execute(); - void OnOK(); - Napi::Promise RunJob(); + struct ErrorBaton { + NSFW *nsfw; + std::string error; + }; - private: - Napi::Promise::Deferred mDeferred; - bool mDidStopWatching; - NSFW *mNSFW; - }; + static NAN_METHOD(JSNew); - Napi::Value Stop(const Napi::CallbackInfo &info); + static NAN_METHOD(Start); + class StartWorker : public AsyncWorker { + public: + StartWorker(NSFW *nsfw, Callback *callback); + void Execute(); + void HandleOKCallback(); + private: + NSFW *mNSFW; + }; + static NAN_METHOD(Stop); + class StopWorker : public AsyncWorker { public: - static Napi::Object Init(Napi::Env, Napi::Object exports); - static Napi::Value InstanceCount(const Napi::CallbackInfo &info); - void pollForEvents(); + StopWorker(NSFW *nsfw, Callback *callback); + void Execute(); + void HandleOKCallback(); + private: + NSFW *mNSFW; + }; - NSFW(const Napi::CallbackInfo &info); - ~NSFW(); + static Persistent constructor; }; #endif diff --git a/package.json b/package.json index f155345f..e0a10e1b 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ ], "homepage": "https://github.com/axosoft/node-simple-file-watcher", "dependencies": { - "node-addon-api": "*" + "nan": "^2.0.0" }, "devDependencies": { "eslint": "^6.8.0", diff --git a/src/NSFW.cpp b/src/NSFW.cpp index c20b3776..b07fb0c0 100644 --- a/src/NSFW.cpp +++ b/src/NSFW.cpp @@ -1,284 +1,311 @@ #include "../includes/NSFW.h" -Napi::FunctionReference NSFW::constructor; -std::size_t NSFW::instanceCount = 0; -bool NSFW::gcEnabled = false; - -NSFW::NSFW(const Napi::CallbackInfo &info): - Napi::ObjectWrap(info), - mDebounceMS(0), - mInterface(nullptr), - mQueue(std::make_shared()), - mPath(""), - mRunning(false) -{ - if (gcEnabled) { - instanceCount++; +#if defined(_WIN32) +#include +#define sleep_for_ms(ms) Sleep(ms) +#else +#include +#define sleep_for_ms(ms) usleep(ms * 1000) +#endif + +#pragma unmanaged +Persistent NSFW::constructor; + +NSFW::NSFW(uint32_t debounceMS, std::string path, Callback *eventCallback, Callback *errorCallback): + mDebounceMS(debounceMS), + mErrorCallback(errorCallback), + mEventCallback(eventCallback), + mInterface(NULL), + mInterfaceLockValid(false), + mPath(path), + mRunning(false), + mQueue(std::make_shared()) + { + HandleScope scope; + v8::Local obj = New(); + mPersistentHandle.Reset(obj); + mInterfaceLockValid = uv_mutex_init(&mInterfaceLock) == 0; } - auto env = info.Env(); - if (info.Length() < 1 || !info[0].IsString()) { - throw Napi::TypeError::New(env, "Must pass a string path as the first argument to NSFW."); +NSFW::~NSFW() { + if (mInterface != NULL) { + delete mInterface; } + delete mEventCallback; + delete mErrorCallback; + + if (mInterfaceLockValid) { + uv_mutex_destroy(&mInterfaceLock); + } +} - mPath = info[0].ToString(); +void NSFW::fireErrorCallback(uv_async_t *handle) { + Nan::HandleScope scope; + ErrorBaton *baton = (ErrorBaton *)handle->data; + v8::Local argv[] = { + New(baton->error).ToLocalChecked() + }; + baton->nsfw->mErrorCallback->Call(1, argv); + delete baton; +} - if (info.Length() < 2 || !info[1].IsFunction()) { - throw Napi::TypeError::New(env, "Must pass an event callback as the second parameter to NSFW."); +void NSFW::fireEventCallback(uv_async_t *handle) { + Nan::HandleScope scope; + NSFW *nsfw = (NSFW *)handle->data; + auto events = nsfw->mQueue->dequeueAll(); + if (events == nullptr) { + return; } - mEventCallback = Napi::ThreadSafeFunction::New( - env, - info[1].As(), - "nsfw", - 0, - 1 - ); - - if (info.Length() >= 3) { - if (!info[2].IsObject()) { - throw Napi::TypeError::New(env, "If the third parameter to NSFW is provided, it must be an object."); - } + v8::Local eventArray = New((int)events->size()); - Napi::Object options = info[2].ToObject(); - Napi::Value maybeDebounceMS = options["debounceMS"]; - if (options.Has("debounceMS") && !maybeDebounceMS.IsNumber()) { - throw Napi::TypeError::New(env, "options.debounceMS must be a number."); - } + for (unsigned int i = 0; i < events->size(); ++i) { + v8::Local jsEvent = New(); - if (maybeDebounceMS.IsNumber()) { - Napi::Number temp = maybeDebounceMS.ToNumber(); - double bounds = temp.DoubleValue(); - if (bounds < 1 || bounds > 60000) { - throw Napi::TypeError::New(env, "options.debounceMS must be >= 1 and <= 60000."); - } - mDebounceMS = temp; - } + Nan::Set(jsEvent, Nan::New("action").ToLocalChecked(), Nan::New((*events)[i]->type)); + Nan::Set(jsEvent, Nan::New("directory").ToLocalChecked(), Nan::New((*events)[i]->fromDirectory).ToLocalChecked()); - Napi::Value maybeErrorCallback = options["errorCallback"]; - if (options.Has("errorCallback") && !maybeErrorCallback.IsFunction()) { - throw Napi::TypeError::New(env, "options.errorCallback must be a function."); + if ((*events)[i]->type == RENAMED) { + Nan::Set(jsEvent, Nan::New("oldFile").ToLocalChecked(), Nan::New((*events)[i]->fromFile).ToLocalChecked()); + Nan::Set(jsEvent, Nan::New("newDirectory").ToLocalChecked(), Nan::New((*events)[i]->toDirectory).ToLocalChecked()); + Nan::Set(jsEvent, Nan::New("newFile").ToLocalChecked(), Nan::New((*events)[i]->toFile).ToLocalChecked()); + } else { + Nan::Set(jsEvent, Nan::New("file").ToLocalChecked(), Nan::New((*events)[i]->fromFile).ToLocalChecked()); } - mErrorCallback = Napi::ThreadSafeFunction::New( - env, - maybeErrorCallback.IsFunction() - ? maybeErrorCallback.As() - : Napi::Function::New(env, [](const Napi::CallbackInfo &info) {}), - "nsfw", - 0, - 1 - ); + Nan::Set(eventArray, i, jsEvent); } + + v8::Local argv[] = { + eventArray + }; + + nsfw->mEventCallback->Call(1, argv); } -NSFW::~NSFW() { - mErrorCallback.Release(); - mEventCallback.Release(); +void NSFW::pollForEvents(void *arg) { + NSFW *nsfw = (NSFW *)arg; + while(nsfw->mRunning) { + uv_mutex_lock(&nsfw->mInterfaceLock); + + if (nsfw->mInterface->hasErrored()) { + ErrorBaton *baton = new ErrorBaton; + baton->nsfw = nsfw; + baton->error = nsfw->mInterface->getError(); + + nsfw->mErrorCallbackAsync.data = (void *)baton; + uv_async_send(&nsfw->mErrorCallbackAsync); + nsfw->mRunning = false; + uv_mutex_unlock(&nsfw->mInterfaceLock); + break; + } + + if (nsfw->mQueue->count() == 0) { + uv_mutex_unlock(&nsfw->mInterfaceLock); + sleep_for_ms(50); + continue; + } - if (gcEnabled) { - instanceCount--; + nsfw->mEventCallbackAsync.data = (void *)nsfw; + uv_async_send(&nsfw->mEventCallbackAsync); + + uv_mutex_unlock(&nsfw->mInterfaceLock); + + sleep_for_ms(nsfw->mDebounceMS); } } -NSFW::StartWorker::StartWorker(Napi::Env env, NSFW *nsfw): - Napi::AsyncWorker(env, "nsfw"), - mDeferred(Napi::Promise::Deferred::New(env)), - mNSFW(nsfw), - mStatus(JOB_NOT_EXECUTED_YET) -{} +NAN_MODULE_INIT(NSFW::Init) { + Nan::HandleScope scope; -Napi::Promise NSFW::StartWorker::RunJob() { - mNSFW->Ref(); - this->Queue(); + v8::Local tpl = New(JSNew); + tpl->SetClassName(New("NSFW").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); - return mDeferred.Promise(); -} + SetPrototypeMethod(tpl, "start", Start); + SetPrototypeMethod(tpl, "stop", Stop); -void NSFW::StartWorker::Execute() { - std::lock_guard lock(mNSFW->mInterfaceLock); + v8::Local context = Nan::GetCurrentContext(); + constructor.Reset(tpl->GetFunction(context).ToLocalChecked()); + Set(target, New("NSFW").ToLocalChecked(), tpl->GetFunction(context).ToLocalChecked()); +} - if (mNSFW->mInterface) { - mStatus = ALREADY_RUNNING; +NAN_METHOD(NSFW::JSNew) { + if (!info.IsConstructCall()) { + const int argc = 4; + v8::Isolate *isolate = info.GetIsolate(); + v8::Local argv[argc] = {info[0], info[1], info[2], info[3]}; + v8::Local context = isolate->GetCurrentContext(); + v8::Local cons = v8::Local::New(isolate, constructor); + info.GetReturnValue().Set(cons->NewInstance(context, argc, argv).ToLocalChecked()); return; } - mNSFW->mQueue->clear(); - mNSFW->mInterface.reset(new NativeInterface(mNSFW->mPath, mNSFW->mQueue)); - - if (mNSFW->mInterface->isWatching()) { - mStatus = STARTED; - mNSFW->mRunning = true; - mNSFW->mErrorCallback.Acquire(); - mNSFW->mEventCallback.Acquire(); - mNSFW->mPollThread = std::thread([] (NSFW *nsfw) { nsfw->pollForEvents(); }, mNSFW); - } else { - mStatus = COULD_NOT_START; - mNSFW->mInterface.reset(nullptr); + if (info.Length() < 1 || !info[0]->IsUint32()) { + return ThrowError("First argument of constructor must be a positive integer."); + } + if (info.Length() < 2 || !info[1]->IsString()) { + return ThrowError("Second argument of constructor must be a path."); + } + if (info.Length() < 3 || !info[2]->IsFunction()) { + return ThrowError("Third argument of constructor must be a callback."); + } + if (info.Length() < 4 || !info[3]->IsFunction()) { + return ThrowError("Fourth argument of constructor must be a callback."); } -} -void NSFW::StartWorker::OnOK() { - std::lock_guard lock(mNSFW->mInterfaceLock); - auto env = Env(); - switch (mStatus) { - case ALREADY_RUNNING: - mNSFW->Unref(); - mDeferred.Reject(Napi::Error::New(env, "This NSFW cannot be started, because it is already running.").Value()); - break; + v8::Local context = Nan::GetCurrentContext(); + uint32_t debounceMS = info[0]->Uint32Value(context).FromJust(); + Nan::Utf8String utf8Value(Nan::To(info[1]).ToLocalChecked()); + std::string path = std::string(*utf8Value); + Callback *eventCallback = new Callback(info[2].As()); + Callback *errorCallback = new Callback(info[3].As()); - case COULD_NOT_START: - mNSFW->Unref(); - mDeferred.Reject(Napi::Error::New(env, "NSFW was unable to start watching that directory.").Value()); - break; + NSFW *nsfw = new NSFW(debounceMS, path, eventCallback, errorCallback); + nsfw->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); +} - case STARTED: - mDeferred.Resolve(env.Undefined()); - break; +NAN_METHOD(NSFW::Start) { + Nan::HandleScope scope; - default: - mNSFW->Unref(); - mDeferred.Reject(Napi::Error::New( - env, - "Execute did not run, but OnOK fired. This should never have happened." - ).Value()); + NSFW *nsfw = ObjectWrap::Unwrap(info.This()); + if (!nsfw->mInterfaceLockValid) { + return ThrowError("NSFW failed to initialize properly. Try creating a new NSFW."); } -} -Napi::Value NSFW::Start(const Napi::CallbackInfo &info) { - return (new StartWorker(info.Env(), this))->RunJob(); -} + if ( + info.Length() < 1 || + !info[0]->IsFunction() + ) { + return ThrowError("Must provide callback to start."); + } + + Callback *callback = new Callback(info[0].As()); + + if (nsfw->mInterface != NULL) { + v8::Local argv[1] = { + Nan::Error("This NSFW cannot be started, because it is already running.") + }; + callback->Call(1, argv); + delete callback; + return; + } -NSFW::StopWorker::StopWorker(Napi::Env env, NSFW *nsfw): - Napi::AsyncWorker(env, "nsfw"), - mDeferred(Napi::Promise::Deferred::New(env)), - mDidStopWatching(false), - mNSFW(nsfw) -{} + Nan::Set(Nan::New(nsfw->mPersistentHandle), Nan::New("nsfw").ToLocalChecked(), info.This()); -Napi::Promise NSFW::StopWorker::RunJob() { - this->Queue(); - return mDeferred.Promise(); + AsyncQueueWorker(new StartWorker(nsfw, callback)); } -void NSFW::StopWorker::Execute() { - { - std::lock_guard lock(mNSFW->mInterfaceLock); - if (!mNSFW->mInterface) { - return; - } +NSFW::StartWorker::StartWorker(NSFW *nsfw, Callback *callback): + AsyncWorker(callback), mNSFW(nsfw) { + uv_async_init(uv_default_loop(), &nsfw->mErrorCallbackAsync, &NSFW::fireErrorCallback); + uv_async_init(uv_default_loop(), &nsfw->mEventCallbackAsync, &NSFW::fireEventCallback); } - mDidStopWatching = true; - mNSFW->mRunning = false; - mNSFW->mPollThread.join(); +void NSFW::StartWorker::Execute() { + uv_mutex_lock(&mNSFW->mInterfaceLock); + + if (mNSFW->mInterface != NULL) { + uv_mutex_unlock(&mNSFW->mInterfaceLock); + return; + } - std::lock_guard lock(mNSFW->mInterfaceLock); - mNSFW->mInterface.reset(nullptr); mNSFW->mQueue->clear(); + mNSFW->mInterface = new NativeInterface(mNSFW->mPath, mNSFW->mQueue); + if (mNSFW->mInterface->isWatching()) { + mNSFW->mRunning = true; + uv_thread_create(&mNSFW->mPollThread, NSFW::pollForEvents, mNSFW); + } else { + delete mNSFW->mInterface; + mNSFW->mInterface = NULL; + } + + uv_mutex_unlock(&mNSFW->mInterfaceLock); } -void NSFW::StopWorker::OnOK() { - std::lock_guard lock(mNSFW->mInterfaceLock); - if (mDidStopWatching) { - mNSFW->Unref(); - mDeferred.Resolve(Env().Undefined()); +void NSFW::StartWorker::HandleOKCallback() { + HandleScope(); + if (mNSFW->mInterface == NULL) { + if (!mNSFW->mPersistentHandle.IsEmpty()) { + v8::Local obj = New(); + mNSFW->mPersistentHandle.Reset(obj); + } + v8::Local argv[1] = { + Nan::Error("NSFW was unable to start watching that directory.") + }; + callback->Call(1, argv); } else { - mDeferred.Reject(Napi::Error::New(Env(), "This NSFW cannot be stopped, because it is not running.").Value()); + callback->Call(0, NULL); } } -Napi::Value NSFW::Stop(const Napi::CallbackInfo &info) { - return (new StopWorker(info.Env(), this))->RunJob(); -} +NAN_METHOD(NSFW::Stop) { + Nan::HandleScope scope; -void NSFW::pollForEvents() { - while (mRunning) { - uint32_t sleepDuration = 50; - { - std::lock_guard lock(mInterfaceLock); - - if (mInterface->hasErrored()) { - const std::string &error = mInterface->getError(); - mErrorCallback.NonBlockingCall([error](Napi::Env env, Napi::Function jsCallback) { - Napi::Value jsError = Napi::Error::New(env, error).Value(); - jsCallback.Call({ jsError }); - }); - mRunning = false; - break; - } - - if (mQueue->count() != 0) { - auto events = mQueue->dequeueAll(); - if (events != nullptr) { - sleepDuration = mDebounceMS; - auto callback = [](Napi::Env env, Napi::Function jsCallback, std::vector> *eventsRaw) { - std::unique_ptr>> events(eventsRaw); - eventsRaw = nullptr; - - int numEvents = events->size(); - Napi::Array eventArray = Napi::Array::New(env, numEvents); - - for (int i = 0; i < numEvents; ++i) { - auto event = Napi::Object::New(env); - event["action"] = Napi::Number::New(env, (*events)[i]->type); - event["directory"] = Napi::String::New(env, (*events)[i]->fromDirectory); - - if ((*events)[i]->type == RENAMED) { - event["oldFile"] = Napi::String::New(env, (*events)[i]->fromFile); - event["newDirectory"] = Napi::String::New(env, (*events)[i]->toDirectory); - event["newFile"] = Napi::String::New(env, (*events)[i]->toFile); - } else { - event["file"] = Napi::String::New(env, (*events)[i]->fromFile); - } - - eventArray[(uint32_t)i] = event; - } - - jsCallback.Call({ eventArray }); - }; - - mEventCallback.NonBlockingCall(events.release(), callback); - } - } - } + NSFW *nsfw = ObjectWrap::Unwrap(info.This()); + if (!nsfw->mInterfaceLockValid) { + return ThrowError("NSFW failed to initialize properly. Try creating a new NSFW."); + } - std::this_thread::sleep_for(std::chrono::milliseconds(sleepDuration)); + if ( + info.Length() < 1 || + !info[0]->IsFunction() + ) { + return ThrowError("Must provide callback to stop."); } - mErrorCallback.Release(); - mEventCallback.Release(); -} + Callback *callback = new Callback(info[0].As()); -Napi::Value NSFW::InstanceCount(const Napi::CallbackInfo &info) { - return Napi::Number::New(info.Env(), instanceCount); -} + if (nsfw->mInterface == NULL) { + v8::Local argv[1] = { + Nan::Error("This NSFW cannot be stopped, because it is not running.") + }; + callback->Call(1, argv); + delete callback; + return; + } -Napi::Object NSFW::Init(Napi::Env env, Napi::Object exports) { - gcEnabled = ((Napi::Value)env.Global()["gc"]).IsFunction(); + AsyncQueueWorker(new StopWorker(nsfw, callback)); +} - Napi::Function nsfwConstructor = DefineClass(env, "NSFW", { - InstanceMethod("start", &NSFW::Start), - InstanceMethod("stop", &NSFW::Stop) - }); +NSFW::StopWorker::StopWorker(NSFW *nsfw, Callback *callback): + AsyncWorker(callback), mNSFW(nsfw) {} - if (gcEnabled) { - nsfwConstructor.DefineProperty(Napi::PropertyDescriptor::Function( - "getAllocatedInstanceCount", - &NSFW::InstanceCount, - napi_static - )); +void NSFW::StopWorker::Execute() { + uv_mutex_lock(&mNSFW->mInterfaceLock); + if (mNSFW->mInterface == NULL) { + uv_mutex_unlock(&mNSFW->mInterfaceLock); + return; } + uv_mutex_unlock(&mNSFW->mInterfaceLock); + + // unlock the mInterfaceLock mutex while operate on the running identifier + mNSFW->mRunning = false; - constructor = Napi::Persistent(nsfwConstructor); - constructor.SuppressDestruct(); + uv_thread_join(&mNSFW->mPollThread); + + uv_mutex_lock(&mNSFW->mInterfaceLock); + delete mNSFW->mInterface; + mNSFW->mInterface = NULL; + mNSFW->mQueue->clear(); - return nsfwConstructor; + uv_mutex_unlock(&mNSFW->mInterfaceLock); } -static Napi::Object Init(Napi::Env env, Napi::Object exports) { - return NSFW::Init(env, exports); +void NSFW::StopWorker::HandleOKCallback() { + HandleScope(); + + if (!mNSFW->mPersistentHandle.IsEmpty()) { + v8::Local obj = New(); + mNSFW->mPersistentHandle.Reset(obj); + } + + uv_close(reinterpret_cast(&mNSFW->mErrorCallbackAsync), nullptr); + uv_close(reinterpret_cast(&mNSFW->mEventCallbackAsync), nullptr); + + callback->Call(0, NULL); } -NODE_API_MODULE(nsfw, Init) +NODE_MODULE(nsfw, NSFW::Init) diff --git a/yarn.lock b/yarn.lock index ce146733..259e7d8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -894,6 +894,11 @@ mute-stream@0.0.8: resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +nan@^2.0.0: + version "2.14.0" + resolved "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -904,11 +909,6 @@ nice-try@^1.0.4: resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-addon-api@*: - version "2.0.0" - resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz#f9afb8d777a91525244b01775ea0ddbe1125483b" - integrity sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA== - node-environment-flags@1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" From 05d18634f4486b0b879724c5e7cf199517288359 Mon Sep 17 00:00:00 2001 From: Tyler Ang-Wanek Date: Wed, 8 Apr 2020 08:50:50 -0700 Subject: [PATCH 2/2] Revert javascript interface changes --- js/spec/index-spec.js | 10 ---------- js/src/index.js | 15 ++++++++++----- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/js/spec/index-spec.js b/js/spec/index-spec.js index 40fa5da8..82835d5a 100644 --- a/js/spec/index-spec.js +++ b/js/spec/index-spec.js @@ -673,14 +673,4 @@ describe('Node Sentinel File Watcher', function() { } }); }); - - describe('Garbage collection', function() { - it('can garbage collect all instances', async function () { - this.timeout(60000); - while (nsfw.getAllocatedInstanceCount() > 0) { - global.gc(); - await sleep(0); - } - }); - }); }); diff --git a/js/src/index.js b/js/src/index.js index 2cfa89fb..84aec1e0 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -1,7 +1,8 @@ const { promises: fs } = require('fs'); const path = require('path'); +const { promisify } = require('util'); -const NSFW = require('../../build/Release/nsfw.node'); +const { NSFW } = require('../../build/Release/nsfw.node'); function NSFWFilePoller(watchPath, eventCallback, debounceMS) { const { CREATED, DELETED, MODIFIED } = nsfw.actions; @@ -71,7 +72,7 @@ const buildNSFW = async (watchPath, eventCallback, { debounceMS = 500, errorCall } if (stats.isDirectory()) { - return new NSFW(watchPath, eventCallback, { debounceMS, errorCallback }); + return new NSFW(debounceMS, watchPath, eventCallback, errorCallback); } else if (stats.isFile()) { return new NSFWFilePoller(watchPath, eventCallback, debounceMS); } else { @@ -85,9 +86,13 @@ function nsfw(watchPath, eventCallback, options) { } const implementation = watchPath; - - this.start = () => implementation.start(); - this.stop = () => implementation.stop(); + if (implementation instanceof NSFW) { + this.start = promisify((callback) => implementation.start(callback)); + this.stop = promisify((callback) => implementation.stop(callback)); + } else { + this.start = () => implementation.start(); + this.stop = () => implementation.stop(); + } } nsfw.actions = {