Skip to content

Commit dd18a13

Browse files
Merge pull request #151 from julianmesa-gitkraken/fix-segfault-on-exit
Fix segfault on exit from an electron app
2 parents d8f5dfb + 8b7f498 commit dd18a13

File tree

2 files changed

+45
-10
lines changed

2 files changed

+45
-10
lines changed

includes/NSFW.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ class NSFW : public Napi::ObjectWrap<NSFW> {
2626
std::string mPath;
2727
std::thread mPollThread;
2828
std::atomic<bool> mRunning;
29+
std::atomic<bool> mFinalizing;
30+
std::condition_variable mWaitPoolEvents;
31+
std::mutex mRunningLock;
2932

3033
class StartWorker: public Napi::AsyncWorker {
3134
public:
@@ -95,6 +98,7 @@ class NSFW : public Napi::ObjectWrap<NSFW> {
9598
void resumeQueue();
9699
void pollForEvents();
97100

101+
void Finalize(Napi::Env env);
98102
NSFW(const Napi::CallbackInfo &info);
99103
~NSFW();
100104
};

src/NSFW.cpp

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ NSFW::NSFW(const Napi::CallbackInfo &info):
1010
mInterface(nullptr),
1111
mQueue(std::make_shared<EventQueue>()),
1212
mPath(""),
13-
mRunning(false)
13+
mRunning(false),
14+
mFinalizing(false)
1415
{
1516
auto env = info.Env();
1617
if (info.Length() < 1 || !info[0].IsString()) {
@@ -74,14 +75,24 @@ NSFW::NSFW(const Napi::CallbackInfo &info):
7475
}
7576

7677
NSFW::~NSFW() {
77-
mErrorCallback.Release();
78-
mEventCallback.Release();
79-
8078
if (gcEnabled) {
8179
instanceCount--;
8280
}
8381
}
8482

83+
void NSFW::Finalize(Napi::Env env) {
84+
if (mRunning) {
85+
mFinalizing = true;
86+
{
87+
std::lock_guard<std::mutex> lock(mRunningLock);
88+
mRunning = false;
89+
}
90+
Unref();
91+
mWaitPoolEvents.notify_one();
92+
mPollThread.join();
93+
}
94+
}
95+
8596
NSFW::StartWorker::StartWorker(Napi::Env env, NSFW *nsfw):
8697
Napi::AsyncWorker(env, "nsfw"),
8798
mDeferred(Napi::Promise::Deferred::New(env)),
@@ -109,7 +120,10 @@ void NSFW::StartWorker::Execute() {
109120

110121
if (mNSFW->mInterface->isWatching()) {
111122
mStatus = STARTED;
112-
mNSFW->mRunning = true;
123+
{
124+
std::lock_guard<std::mutex> lock(mNSFW->mRunningLock);
125+
mNSFW->mRunning = true;
126+
}
113127
mNSFW->mErrorCallback.Acquire();
114128
mNSFW->mEventCallback.Acquire();
115129
mNSFW->mPollThread = std::thread([] (NSFW *nsfw) { nsfw->pollForEvents(); }, mNSFW);
@@ -171,7 +185,11 @@ void NSFW::StopWorker::Execute() {
171185
}
172186

173187
mDidStopWatching = true;
174-
mNSFW->mRunning = false;
188+
{
189+
std::lock_guard<std::mutex> lock(mNSFW->mRunningLock);
190+
mNSFW->mRunning = false;
191+
}
192+
mNSFW->mWaitPoolEvents.notify_one();
175193
mNSFW->mPollThread.join();
176194

177195
std::lock_guard<std::mutex> lock(mNSFW->mInterfaceLock);
@@ -271,7 +289,10 @@ void NSFW::pollForEvents() {
271289
Napi::Value jsError = Napi::Error::New(env, error).Value();
272290
jsCallback.Call({ jsError });
273291
});
274-
mRunning = false;
292+
{
293+
std::lock_guard<std::mutex> lock(mRunningLock);
294+
mRunning = false;
295+
}
275296
break;
276297
}
277298

@@ -310,11 +331,21 @@ void NSFW::pollForEvents() {
310331
}
311332
}
312333

313-
std::this_thread::sleep_for(std::chrono::milliseconds(sleepDuration));
334+
std::unique_lock<std::mutex> lck(mRunningLock);
335+
const auto waitUntil = std::chrono::steady_clock::now() + std::chrono::milliseconds(sleepDuration);
336+
mWaitPoolEvents.wait_until(lck, waitUntil,
337+
[this, waitUntil](){
338+
return !mRunning || std::chrono::steady_clock::now() >= waitUntil;
339+
}
340+
);
314341
}
315342

316-
mErrorCallback.Release();
317-
mEventCallback.Release();
343+
// If we are destroying NFSW object (destructor) we cannot release the thread safe functions at this point
344+
// or we get a segfault
345+
if (!mFinalizing) {
346+
mErrorCallback.Release();
347+
mEventCallback.Release();
348+
}
318349
}
319350

320351
Napi::Value NSFW::InstanceCount(const Napi::CallbackInfo &info) {

0 commit comments

Comments
 (0)