Skip to content

Switch to Napi from NAN #104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 4, 2020
Merged

Switch to Napi from NAN #104

merged 5 commits into from
Apr 4, 2020

Conversation

implausible
Copy link
Contributor

@implausible implausible commented Mar 31, 2020

I've decided to move NSFW to Napi so that we can be a little more future proof in relation to the direction Node is going with native modules. This should resolve any context issues or warnings about needing to leverage newer APIs in NSFW from here on out. We might also be able to start building 1 binary per OS for Node, which would be pretty neat; though the same may not apply for Electron.

I also went ahead and upgraded any of our old dependencies and removed babel, lodash, and fs-extra from the installed dependency chain. None of those really make sense to ship here anymore.

Closes #91
Closes #96
Closes #100

@implausible implausible changed the title Switch to Napi from NaN Switch to Napi from NAN Mar 31, 2020
Copy link
Contributor

@ianhattendorf ianhattendorf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple questions, looks great!

{}

Napi::Promise NSFW::StartWorker::RunJob() {
if (mNSFW->mInterface) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it's probably undefined behavior (race condition), and should probably either be behind an std::lock_guard, wrapped in an std::atomic, or ignored and handled in Execute().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not actually a race condition though, a value will either be there or it won't. If it was being destroyed while we were calling start, the operation won't start. We don't need to lock on this pointer, because we're not leveraging anything about the pointer except did it exist when we could read it at this point in time. I really don't put much emphasis on correctly queuing a new start operation when a stop operation is happening at this level.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The correct way for a user of the library to queue a start after a stop is to wait for the stop promise to complete before running start.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but the very act of accessing a variable (in this case checking if the pointer is null) that's modified in another thread is undefined behavior unless they're atomic or synchronized. There's no guarantee that changes made in one thread will be visible in another. So it could return true, it could return false, it could never return true because maybe it cached the initial value in each thread (or put it in a register) and that thread never busted the cache, etc.

That said, if it's incorrect for users to call start while an instance is already running, I don't mind too much.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it could never return true because maybe it cached the initial value in each thread (or put it in a register) and that thread never busted the cache, etc.

If this were ever true, why would leveraging the mutex change this behavior at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry I really don't see how this is a race condition that has any bearing on the correctness of the application. :( I specifically left out the lock here, because it will always be safe to perform this check, and will basically be correct in all cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat. Ian shared https://devblogs.microsoft.com/oldnewthing/20180924-00/?p=99805. Under O3 optimization in G++, this can in fact force-cache the variable in the register on the thread.

{}

Napi::Promise NSFW::StopWorker::RunJob() {
if (!mNSFW->mInterface) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible race condition.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's really not though. We're not accessing anything on this pointer except whether it existed when we read tried to start this. We don't want to lock here. We've also handled locking this interface where we are actually doing work in the execute thread.

@ianhattendorf ianhattendorf merged commit d787dee into master Apr 4, 2020
@ianhattendorf ianhattendorf deleted the refactor/napi branch April 4, 2020 00:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Make nsfw context aware
2 participants