| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,8 +4,15 @@ | |||
| 4 | 4 | class LockMasterImpl; | |
| 5 | 5 | ||
| 6 | 6 | class LockMaster { | |
| 7 | + public: | ||
| 8 | + enum Status { | ||
| 9 | + Disabled = 0, | ||
| 10 | + EnabledForAsyncOnly, | ||
| 11 | + Enabled | ||
| 12 | + }; | ||
| 7 | 13 | ||
| 8 | - static bool enabled; | ||
| 14 | + private: | ||
| 15 | + static Status status; | ||
| 9 | 16 | ||
| 10 | 17 | LockMasterImpl *impl; | |
| 11 | 18 | ||
@@ -34,8 +41,8 @@ class LockMaster { | |||
| 34 | 41 | public: | |
| 35 | 42 | ||
| 36 | 43 | // we lock on construction | |
| 37 | - template<typename ...Types> LockMaster(bool emptyGuard, const Types*... types) { | ||
| 38 | - if(!enabled) { | ||
| 44 | + template<typename ...Types> LockMaster(bool asyncAction, const Types*... types) { | ||
| 45 | + if((status == Disabled) || ((status == EnabledForAsyncOnly) && !asyncAction)) { | ||
| 39 | 46 | impl = NULL; | |
| 40 | 47 | return; | |
| 41 | 48 | } | |
@@ -62,7 +69,7 @@ class LockMaster { | |||
| 62 | 69 | void DestructorImpl(); | |
| 63 | 70 | public: | |
| 64 | 71 | TemporaryUnlock() { | |
| 65 | - // We can't return here if enabled is false | ||
| 72 | + // We can't return here if disabled | ||
| 66 | 73 | // It's possible that a LockMaster was fully constructed and registered | |
| 67 | 74 | // before the thread safety was disabled. | |
| 68 | 75 | // So we rely on ConstructorImpl to abort if there is no registered LockMaster | |
@@ -80,15 +87,19 @@ class LockMaster { | |||
| 80 | 87 | ||
| 81 | 88 | // Enables the thread safety system | |
| 82 | 89 | static void Enable() { | |
| 83 | - enabled = true; | ||
| 90 | + status = Enabled; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + static void SetStatus(Status status) { | ||
| 94 | + LockMaster::status = status; | ||
| 84 | 95 | } | |
| 85 | 96 | ||
| 86 | 97 | static void Disable() { | |
| 87 | - enabled = false; | ||
| 98 | + status = Disabled; | ||
| 88 | 99 | } | |
| 89 | 100 | ||
| 90 | - static bool IsEnabled() { | ||
| 91 | - return enabled; | ||
| 101 | + static Status GetStatus() { | ||
| 102 | + return status; | ||
| 92 | 103 | } | |
| 93 | 104 | ||
| 94 | 105 | // Diagnostic information that can be provided to the JavaScript layer | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -87,7 +87,7 @@ NAN_GC_CALLBACK(LockMasterImpl::CleanupMutexes) { | |||
| 87 | 87 | // this means that turning thread safety on and then off | |
| 88 | 88 | // could result in remaining mutexes - but they would get cleaned up | |
| 89 | 89 | // if thread safety is turned on again | |
| 90 | - if (!LockMaster::IsEnabled()) { | ||
| 90 | + if (LockMaster::GetStatus() == LockMaster::Disabled) { | ||
| 91 | 91 | return; | |
| 92 | 92 | } | |
| 93 | 93 | ||
@@ -243,4 +243,4 @@ void LockMaster::TemporaryUnlock::DestructorImpl() { | |||
| 243 | 243 | impl->Lock(false); | |
| 244 | 244 | } | |
| 245 | 245 | ||
| 246 | - bool LockMaster::enabled = false; | ||
| 246 | + LockMaster::Status LockMaster::status = LockMaster::Disabled; | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -80,9 +80,9 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) { | |||
| 80 | 80 | ||
| 81 | 81 | void {{ cppClassName }}::{{ cppFunctionName }}Worker::Execute() { | |
| 82 | 82 | giterr_clear(); | |
| 83 | - | ||
| 83 | + | ||
| 84 | 84 | { | |
| 85 | - LockMaster lockMaster(true{%each args|argsInfo as arg %} | ||
| 85 | + LockMaster lockMaster(/*asyncAction: */true{%each args|argsInfo as arg %} | ||
| 86 | 86 | {%if arg.cType|isPointer%}{%if not arg.cType|isDoublePointer%} | |
| 87 | 87 | ,baton->{{ arg.name }} | |
| 88 | 88 | {%endif%}{%endif%} | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -35,9 +35,9 @@ if (Nan::ObjectWrap::Unwrap<{{ cppClassName }}>(info.This())->GetValue() != NULL | |||
| 35 | 35 | {% endif %} | |
| 36 | 36 | ||
| 37 | 37 | giterr_clear(); | |
| 38 | - | ||
| 38 | + | ||
| 39 | 39 | { | |
| 40 | - LockMaster lockMaster(true{%each args|argsInfo as arg %} | ||
| 40 | + LockMaster lockMaster(/*asyncAction: */false{%each args|argsInfo as arg %} | ||
| 41 | 41 | {%if arg.cType|isPointer%}{%if not arg.isReturn%} | |
| 42 | 42 | ,{%if arg.isSelf %} | |
| 43 | 43 | Nan::ObjectWrap::Unwrap<{{ arg.cppClassName }}>(info.This())->GetValue() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,12 +25,25 @@ void LockMasterEnable(const FunctionCallbackInfo<Value>& info) { | |||
| 25 | 25 | LockMaster::Enable(); | |
| 26 | 26 | } | |
| 27 | 27 | ||
| 28 | - void LockMasterDisable(const FunctionCallbackInfo<Value>& info) { | ||
| 29 | - LockMaster::Disable(); | ||
| 28 | + void LockMasterSetStatus(const FunctionCallbackInfo<Value>& info) { | ||
| 29 | + Nan::HandleScope scope; | ||
| 30 | + | ||
| 31 | + // convert the first argument to Status | ||
| 32 | + if(info.Length() >= 0 && info[0]->IsNumber()) { | ||
| 33 | + v8::Local<v8::Int32> value = info[0]->ToInt32(); | ||
| 34 | + LockMaster::Status status = static_cast<LockMaster::Status>(value->Value()); | ||
| 35 | + if(status >= LockMaster::Disabled && status <= LockMaster::Enabled) { | ||
| 36 | + LockMaster::SetStatus(status); | ||
| 37 | + return; | ||
| 38 | + } | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + // argument error | ||
| 42 | + Nan::ThrowError("Argument must be one 0, 1 or 2"); | ||
| 30 | 43 | } | |
| 31 | 44 | ||
| 32 | - void LockMasterIsEnabled(const FunctionCallbackInfo<Value>& info) { | ||
| 33 | - info.GetReturnValue().Set(Nan::New(LockMaster::IsEnabled())); | ||
| 45 | + void LockMasterGetStatus(const FunctionCallbackInfo<Value>& info) { | ||
| 46 | + info.GetReturnValue().Set(Nan::New(LockMaster::GetStatus())); | ||
| 34 | 47 | } | |
| 35 | 48 | ||
| 36 | 49 | void LockMasterGetDiagnostics(const FunctionCallbackInfo<Value>& info) { | |
@@ -88,10 +101,17 @@ extern "C" void init(Local<v8::Object> target) { | |||
| 88 | 101 | ConvenientPatch::InitializeComponent(target); | |
| 89 | 102 | ||
| 90 | 103 | NODE_SET_METHOD(target, "enableThreadSafety", LockMasterEnable); | |
| 91 | - NODE_SET_METHOD(target, "disableThreadSafety", LockMasterDisable); | ||
| 92 | - NODE_SET_METHOD(target, "isThreadSafetyEnabled", LockMasterIsEnabled); | ||
| 104 | + NODE_SET_METHOD(target, "setThreadSafetyStatus", LockMasterSetStatus); | ||
| 105 | + NODE_SET_METHOD(target, "getThreadSafetyStatus", LockMasterGetStatus); | ||
| 93 | 106 | NODE_SET_METHOD(target, "getThreadSafetyDiagnostics", LockMasterGetDiagnostics); | |
| 94 | 107 | ||
| 108 | + Local<v8::Object> threadSafety = Nan::New<v8::Object>(); | ||
| 109 | + threadSafety->Set(Nan::New("DISABLED").ToLocalChecked(), Nan::New((int)LockMaster::Disabled)); | ||
| 110 | + threadSafety->Set(Nan::New("ENABLED_FOR_ASYNC_ONLY").ToLocalChecked(), Nan::New((int)LockMaster::EnabledForAsyncOnly)); | ||
| 111 | + threadSafety->Set(Nan::New("ENABLED").ToLocalChecked(), Nan::New((int)LockMaster::Enabled)); | ||
| 112 | + | ||
| 113 | + target->Set(Nan::New("THREAD_SAFETY").ToLocalChecked(), threadSafety); | ||
| 114 | + | ||
| 95 | 115 | LockMaster::Initialize(); | |
| 96 | 116 | } | |
| 97 | 117 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,7 +6,11 @@ var local = path.join.bind(path, __dirname); | |||
| 6 | 6 | var NodeGit = require('..'); | |
| 7 | 7 | ||
| 8 | 8 | if(process.env.NODEGIT_TEST_THREADSAFETY) { | |
| 9 | + console.log('Enabling thread safety in NodeGit'); | ||
| 9 | 10 | NodeGit.enableThreadSafety(); | |
| 11 | + } else if (process.env.NODEGIT_TEST_THREADSAFETY_ASYNC) { | ||
| 12 | + console.log('Enabling thread safety for async actions only in NodeGit'); | ||
| 13 | + NodeGit.setThreadSafetyStatus(NodeGit.THREAD_SAFETY.ENABLED_FOR_ASYNC_ONLY); | ||
| 10 | 14 | } | |
| 11 | 15 | ||
| 12 | 16 | // Have to wrap exec, since it has a weird callback signature. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -31,7 +31,6 @@ describe("Index", function() { | |||
| 31 | 31 | ||
| 32 | 32 | after(function() { | |
| 33 | 33 | this.index.clear(); | |
| 34 | - NodeGit.disableThreadSafety(); | ||
| 35 | 34 | }); | |
| 36 | 35 | ||
| 37 | 36 | it("can get the index of a repo and examine entries", function() { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,43 +22,44 @@ describe("ThreadSafety", function() { | |||
| 22 | 22 | }); | |
| 23 | 23 | ||
| 24 | 24 | it("can enable and disable thread safety", function() { | |
| 25 | - var originalValue = NodeGit.isThreadSafetyEnabled(); | ||
| 25 | + var originalValue = NodeGit.getThreadSafetyStatus(); | ||
| 26 | 26 | ||
| 27 | 27 | NodeGit.enableThreadSafety(); | |
| 28 | - assert.equal(true, NodeGit.isThreadSafetyEnabled()); | ||
| 28 | + assert.equal(NodeGit.THREAD_SAFETY.ENABLED, | ||
| 29 | + NodeGit.getThreadSafetyStatus()); | ||
| 29 | 30 | ||
| 30 | - NodeGit.disableThreadSafety(); | ||
| 31 | - assert.equal(false, NodeGit.isThreadSafetyEnabled()); | ||
| 31 | + NodeGit.setThreadSafetyStatus(NodeGit.THREAD_SAFETY.ENABLED_FOR_ASYNC_ONLY); | ||
| 32 | + assert.equal(NodeGit.THREAD_SAFETY.ENABLED_FOR_ASYNC_ONLY, | ||
| 33 | + NodeGit.getThreadSafetyStatus()); | ||
| 32 | 34 | ||
| 33 | - // flip the switch again, to make sure we test all transitions | ||
| 34 | - // (we could have started with thread safety enabled) | ||
| 35 | - NodeGit.enableThreadSafety(); | ||
| 36 | - assert.equal(true, NodeGit.isThreadSafetyEnabled()); | ||
| 35 | + NodeGit.setThreadSafetyStatus(NodeGit.THREAD_SAFETY.DISABLED); | ||
| 36 | + assert.equal(NodeGit.THREAD_SAFETY.DISABLED, | ||
| 37 | + NodeGit.getThreadSafetyStatus()); | ||
| 37 | 38 | ||
| 38 | - if (originalValue) { | ||
| 39 | - NodeGit.enableThreadSafety(); | ||
| 40 | - } else { | ||
| 41 | - NodeGit.disableThreadSafety(); | ||
| 42 | - } | ||
| 39 | + NodeGit.setThreadSafetyStatus(originalValue); | ||
| 43 | 40 | }); | |
| 44 | 41 | ||
| 45 | 42 | it("can lock something and cleanup mutex", function() { | |
| 43 | + var diagnostics = NodeGit.getThreadSafetyDiagnostics(); | ||
| 44 | + var originalCount = diagnostics.storedMutexesCount; | ||
| 46 | 45 | // call a sync method to guarantee that it stores a mutex, | |
| 47 | 46 | // and that it will clean up the mutex in a garbage collection cycle | |
| 48 | 47 | this.repository.headDetached(); | |
| 49 | 48 | ||
| 50 | - var diagnostics = NodeGit.getThreadSafetyDiagnostics(); | ||
| 51 | - if (NodeGit.isThreadSafetyEnabled()) { | ||
| 52 | - // this is a fairly vague test - it just tests that something | ||
| 53 | - // had a mutex created for it at some point (i.e., the thread safety | ||
| 54 | - // code is not completely dead) | ||
| 55 | - assert.ok(diagnostics.storedMutexesCount > 0); | ||
| 56 | - // now test that GC cleans up mutexes | ||
| 57 | - global.gc(); | ||
| 58 | - diagnostics = NodeGit.getThreadSafetyDiagnostics(); | ||
| 59 | - assert.equal(0, diagnostics.storedMutexesCount); | ||
| 60 | - } else { | ||
| 61 | - assert.equal(0, diagnostics.storedMutexesCount); | ||
| 49 | + diagnostics = NodeGit.getThreadSafetyDiagnostics(); | ||
| 50 | + switch(NodeGit.getThreadSafetyStatus()) { | ||
| 51 | + case NodeGit.THREAD_SAFETY.ENABLED: | ||
| 52 | + // this is a fairly vague test - it just tests that something | ||
| 53 | + // had a mutex created for it at some point (i.e., the thread safety | ||
| 54 | + // code is not completely dead) | ||
| 55 | + assert.ok(diagnostics.storedMutexesCount > 0); | ||
| 56 | + break; | ||
| 57 | + case NodeGit.THREAD_SAFETY.ENABLED_FOR_ASYNC_ONLY: | ||
| 58 | + assert.equal(originalCount, diagnostics.storedMutexesCount); | ||
| 59 | + break; | ||
| 60 | + | ||
| 61 | + case NodeGit.THREAD_SAFETY.DISABLED: | ||
| 62 | + assert.equal(0, diagnostics.storedMutexesCount); | ||
| 62 | 63 | } | |
| 63 | 64 | }); | |
| 64 | 65 | }); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments