Skip to content

Conversation

@srajko
Copy link
Collaborator

@srajko srajko commented Dec 17, 2015

Prototype for a solution to #827

@srajko srajko mentioned this pull request Dec 17, 2015
@srajko srajko force-pushed the thread-safety-locking branch 4 times, most recently from 4587b9a to 1f89352 Compare December 18, 2015 20:29
@srajko srajko force-pushed the thread-safety-locking branch from b45475e to 5efab3f Compare December 30, 2015 21:38
@srajko srajko force-pushed the thread-safety-locking branch from 7f12726 to 4b506f7 Compare December 31, 2015 00:50
@srajko
Copy link
Collaborator Author

srajko commented Jan 6, 2016

I'm at a stopping point with this implementation. The main part is the LockMaster class which holds locks related to parameters being passed to libgit2. Currently, when we pass a repository object, we lock the repository. When we pass an index, we lock the repository that owns the index (or the index, if there is no owner). When we pass a commit, we lock the repository that owns the commit. Support for more types can be easily added.

The objects are locked when the LockMaster is instantiated, and unlocked when it's destroyed. Another class, LockMaster::TemporaryUnlock, is used to temporarily unlock these objects when the native layer in an async thread needs to process a callback in the javascript layer (otherwise a call from inside the callback to a sync method that needs to lock one of the locked objects would deadlock the system).

The current implementation uses a few c++11 features, like variadic templates, range-based loops and auto. All of these can be replaced with non-c++11 code, if needed, but I think the library should still build on the compilers we are targeting.

The thread safety system can be disabled / enabled, and it is currently disabled by default.

It would be great to get some feedback on the implementation, also trying to build on different systems would be helpful.

@johnhaley81
Copy link
Collaborator

This does compile on gcc 4.6 and clang 3.4

@joshaber
Copy link
Collaborator

joshaber commented Jan 7, 2016

Wow, this is amazing!

The thread safety system can be disabled / enabled, and it is currently disabled by default.

I'd tentatively argue is should be safe (enabled) by default.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This could be pulled outside the if/else since we do it in both cases.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I almost did that once too, going back through the code. Then I remembered that the following line (mutexes.erase(to_erase);) invalidates everything referring to the erased element, including it before it gets incremented. So it has to be incremented before we erase. In c++11, erase returns an iterator that refers to the next element and that avoids the problem, but that particular functionality isn't available on some of our targeted environments.

@joshaber
Copy link
Collaborator

joshaber commented Jan 7, 2016

My C++ is pretty rusty, but I didn't see any obvious problems 👍

@srajko
Copy link
Collaborator Author

srajko commented Jan 10, 2016

@joshaber thanks for taking a look! I think you are right about having thread safety enabled by default, but maybe we could ease into it (have it disabled by default for a release or few, then turn it on). My main concern is that turning it on makes it possible to block the main node thread, so existing code using NodeGit can be adversely affected (and if it wasn't running into thread-safety issues, it would be adversely affected with no benefit... though scenarios in which the main thread would get blocked are likely the scenarios in which thread safety is an issue, so maybe my concern is unwarranted).

It would be nice if in the long term, we find a way to avoid the risk of blocking the main thread. That would mean that sync functions would never lock - and we've already discussed a few ways of achieving that (make sure that sync calls read data in a thread safe way, make everything else async, etc.), so maybe it wouldn't be too hard to get to that point.

@srajko
Copy link
Collaborator Author

srajko commented Jan 10, 2016

Do we want to have tests running for both thread safety enabled and disabled? Currently they run with the default (disabled) except for index tests, where they are explicitly turned on to test a deadlock scenario.

If we want both, what would be a good way to implement that?

@joshaber
Copy link
Collaborator

I think you are right about having thread safety enabled by default, but maybe we could ease into it (have it disabled by default for a release or few, then turn it on).

Yeah, that's a fair concern too. I'm not sure how to best communicate this change. /cc @nodegit/owners

If we want both, what would be a good way to implement that?

Perhaps an environment variable, so that it can be part of the build matrix?

@maxkorp
Copy link
Collaborator

maxkorp commented Jan 12, 2016

eh, not a bit fan of the both option, as then there are 2 modules to install. I'd say get through like 0.8.x with it disabled by default, and then a bump to 0.9.x with it enabled by default

@johnhaley81
Copy link
Collaborator

I'd say make it default when we go to 1.0

@maxkorp
Copy link
Collaborator

maxkorp commented Jan 12, 2016

Yeah thats a good idea, that way anybody has to explicitly upgrade to it in a project if htey've got stupid ^ deps in their package.json

@tbranyen
Copy link
Member

I agree with switching on by default in 1.0, even though thread safety is more of a guideline than a rule /s

@johnhaley81
Copy link
Collaborator

Just looked over this PR and honestly I think it's ready. Since it defaults to off I feel like this is a moderately safe PR to pull in and we'll be flipping thread safety on in GitKraken so we'll be doing some real-life testing and we'll iron out bugs as we go. I'm removing the [WIP] from this and if nobody speaks up I'll merge this in in about an hour.

@johnhaley81 johnhaley81 changed the title [WIP] Thread safety implementation using locking Thread safety implementation using locking Jan 12, 2016
johnhaley81 added a commit that referenced this pull request Jan 12, 2016
Thread safety implementation using locking
@johnhaley81 johnhaley81 merged commit 80aabf2 into nodegit:master Jan 12, 2016
@srajko
Copy link
Collaborator Author

srajko commented Jan 14, 2016

@joshaber the environment variable idea sounds great to me. @maxkorp I don't think this would require two versions of the module (though you might be thinking back to when I was proposing to add a switch to build NodeGit with/without the thread support).

The way I am interpreting this is we would have an environment variable, maybe NODEGIT_TEST_THREADSAFETY, and if it's set the tests would (at run-time) turn on thread safety. By default, they would test with thread safety off.

I'd like to add this anyway, if nothing else to make it easier to test the thread safety code locally. I'll open up a separate PR when I get a chance.

@johnhaley81 johnhaley81 mentioned this pull request Jan 19, 2016
@srajko srajko deleted the thread-safety-locking branch March 24, 2016 18:53
blowery added a commit to Automattic/dserve that referenced this pull request Jun 28, 2018
See nodegit/nodegit#836

May help with the segfaults we've been seeing.
blowery added a commit to Automattic/dserve that referenced this pull request Jun 28, 2018
See nodegit/nodegit#836

May help with the segfaults we've been seeing.
blowery added a commit to Automattic/dserve that referenced this pull request Jun 28, 2018
* Try using the unpublished nodegit thread safety switch

See nodegit/nodegit#836

May help with the segfaults we've been seeing.

* update mock to include method
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.

5 participants