| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 4ed1c78 commit b4ccac5
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,6 +8,20 @@ | |||
| 8 | 8 | ||
| 9 | 9 | #include "async_worker.h" | |
| 10 | 10 | ||
| 11 | + // Temporary workaround for LFS checkout. Comment added to be reverted. | ||
| 12 | + // With the threadpool rewrite, a Worker will execute its callbacks with | ||
| 13 | + // objects temporary unlock (to prevent deadlocks), and we'll wait until | ||
| 14 | + // the callback is done to lock them back again (to make sure it's thread-safe). | ||
| 15 | + // LFS checkout lost performance after this, and the proper way to fix it is | ||
| 16 | + // to integrate nodegit-lfs into nodegit. Until this is implemented, a | ||
| 17 | + // temporary workaround has been applied, which affects only Workers leveraging | ||
| 18 | + // threaded libgit2 functions (at the moment only checkout) and does the | ||
| 19 | + // following: | ||
| 20 | + // - do not wait for the current callback to end, so that it can send the | ||
| 21 | + // next callback to the main JS thread. | ||
| 22 | + // - do not temporary unlock the objects, since they would be locked back | ||
| 23 | + // again before the callback is executed. | ||
| 24 | + | ||
| 11 | 25 | namespace nodegit { | |
| 12 | 26 | class Context; | |
| 13 | 27 | class AsyncContextCleanupHandle; | |
@@ -17,7 +31,9 @@ namespace nodegit { | |||
| 17 | 31 | public: | |
| 18 | 32 | typedef std::function<void()> Callback; | |
| 19 | 33 | typedef std::function<void(Callback, Callback)> QueueCallbackFn; | |
| 20 | - typedef std::function<Callback(QueueCallbackFn, Callback)> OnPostCallbackFn; | ||
| 34 | + // Temporary workaround for LFS checkout. Code modified to be reverted. | ||
| 35 | + // typedef std::function<Callback(QueueCallbackFn, Callback)> OnPostCallbackFn; | ||
| 36 | + typedef std::function<Callback(QueueCallbackFn, Callback, bool)> OnPostCallbackFn; | ||
| 21 | 37 | ||
| 22 | 38 | // Initializes thread pool and spins up the requested number of threads | |
| 23 | 39 | // The provided loop will be used for completion callbacks, whenever | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -46,7 +46,8 @@ namespace nodegit { | |||
| 46 | 46 | ThreadPool::PostCallbackEvent( | |
| 47 | 47 | [jsCallback, cancelCallback]( | |
| 48 | 48 | ThreadPool::QueueCallbackFn queueCallback, | |
| 49 | - ThreadPool::Callback callbackCompleted | ||
| 49 | + ThreadPool::Callback callbackCompleted, | ||
| 50 | + bool isThreaded // Temporary workaround for LFS checkout. Code added to be reverted. | ||
| 50 | 51 | ) -> ThreadPool::Callback { | |
| 51 | 52 | queueCallback(jsCallback, cancelCallback); | |
| 52 | 53 | callbackCompleted(); | |
@@ -58,13 +59,22 @@ namespace nodegit { | |||
| 58 | 59 | ThreadPool::PostCallbackEvent( | |
| 59 | 60 | [this, jsCallback, cancelCallback]( | |
| 60 | 61 | ThreadPool::QueueCallbackFn queueCallback, | |
| 61 | - ThreadPool::Callback callbackCompleted | ||
| 62 | + ThreadPool::Callback callbackCompleted, | ||
| 63 | + bool isThreaded // Temporary workaround for LFS checkout. Code added to be reverted. | ||
| 62 | 64 | ) -> ThreadPool::Callback { | |
| 63 | - this->onCompletion = callbackCompleted; | ||
| 65 | + // Temporary workaround for LFS checkout. Code modified to be reverted. | ||
| 66 | + if (!isThreaded) { | ||
| 67 | + this->onCompletion = callbackCompleted; | ||
| 64 | 68 | ||
| 65 | - queueCallback(jsCallback, cancelCallback); | ||
| 69 | + queueCallback(jsCallback, cancelCallback); | ||
| 66 | 70 | ||
| 67 | - return std::bind(&AsyncBaton::SignalCompletion, this); | ||
| 71 | + return std::bind(&AsyncBaton::SignalCompletion, this); | ||
| 72 | + } | ||
| 73 | + else { | ||
| 74 | + this->onCompletion = std::bind(&AsyncBaton::SignalCompletion, this); | ||
| 75 | + queueCallback(jsCallback, cancelCallback); | ||
| 76 | + return []() {}; | ||
| 77 | + } | ||
| 68 | 78 | } | |
| 69 | 79 | ); | |
| 70 | 80 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,6 +7,7 @@ | |||
| 7 | 7 | #include <queue> | |
| 8 | 8 | #include <thread> | |
| 9 | 9 | #include <utility> | |
| 10 | + #include <atomic> // Temporary workaround for LFS checkout. Code added to be reverted. | ||
| 10 | 11 | ||
| 11 | 12 | extern "C" { | |
| 12 | 13 | #include <git2/sys/custom_tls.h> | |
@@ -81,8 +82,11 @@ namespace nodegit { | |||
| 81 | 82 | : Event(CALLBACK_TYPE), callback(initCallback) | |
| 82 | 83 | {} | |
| 83 | 84 | ||
| 84 | - ThreadPool::Callback operator()(ThreadPool::QueueCallbackFn queueCb, ThreadPool::Callback completedCb) { | ||
| 85 | - return callback(queueCb, completedCb); | ||
| 85 | + // Temporary workaround for LFS checkout. Code modified to be reverted. | ||
| 86 | + // ThreadPool::Callback operator()(ThreadPool::QueueCallbackFn queueCb, ThreadPool::Callback completedCb) { | ||
| 87 | + // return callback(queueCb, completedCb); | ||
| 88 | + ThreadPool::Callback operator()(ThreadPool::QueueCallbackFn queueCb, ThreadPool::Callback completedCb, bool isThreaded) { | ||
| 89 | + return callback(queueCb, completedCb, isThreaded); | ||
| 86 | 90 | } | |
| 87 | 91 | ||
| 88 | 92 | private: | |
@@ -102,6 +106,10 @@ namespace nodegit { | |||
| 102 | 106 | // the Orchestrator's memory | |
| 103 | 107 | void WaitForThreadClose(); | |
| 104 | 108 | ||
| 109 | + // Temporary workaround for LFS checkout. Code added to be reverted. | ||
| 110 | + // Returns true if the task running spawned threads within libgit2 | ||
| 111 | + bool IsGitThreaded() { return currentGitThreads > kInitialGitThreads; } | ||
| 112 | + | ||
| 105 | 113 | static Nan::AsyncResource *GetCurrentAsyncResource(); | |
| 106 | 114 | ||
| 107 | 115 | static const nodegit::Context *GetCurrentContext(); | |
@@ -139,6 +147,12 @@ namespace nodegit { | |||
| 139 | 147 | PostCompletedEventToOrchestratorFn postCompletedEventToOrchestrator; | |
| 140 | 148 | TakeNextTaskFn takeNextTask; | |
| 141 | 149 | std::thread thread; | |
| 150 | + | ||
| 151 | + // Temporary workaround for LFS checkout. Code added to be reverted. | ||
| 152 | + static constexpr int kInitialGitThreads {0}; | ||
| 153 | + // Number of threads spawned internally by libgit2 to deal with | ||
| 154 | + // the task of this Executor instance. Defaults to kInitialGitThreads. | ||
| 155 | + std::atomic<int> currentGitThreads {kInitialGitThreads}; | ||
| 142 | 156 | }; | |
| 143 | 157 | ||
| 144 | 158 | Executor::Executor( | |
@@ -170,6 +184,9 @@ namespace nodegit { | |||
| 170 | 184 | ||
| 171 | 185 | WorkTask *workTask = static_cast<WorkTask *>(task.get()); | |
| 172 | 186 | ||
| 187 | + // Temporary workaround for LFS checkout. Code added to be reverted. | ||
| 188 | + currentGitThreads = kInitialGitThreads; | ||
| 189 | + | ||
| 173 | 190 | currentAsyncResource = workTask->asyncResource; | |
| 174 | 191 | currentCallbackErrorHandle = workTask->callbackErrorHandle; | |
| 175 | 192 | workTask->callback(); | |
@@ -221,6 +238,8 @@ namespace nodegit { | |||
| 221 | 238 | } | |
| 222 | 239 | ||
| 223 | 240 | void *Executor::RetrieveTLSForLibgit2ChildThread() { | |
| 241 | + // Temporary workaround for LFS checkout. Code added to be reverted. | ||
| 242 | + ++Executor::executor->currentGitThreads; | ||
| 224 | 243 | return Executor::executor; | |
| 225 | 244 | } | |
| 226 | 245 | ||
@@ -230,6 +249,8 @@ namespace nodegit { | |||
| 230 | 249 | ||
| 231 | 250 | void Executor::TeardownTLSOnLibgit2ChildThread() { | |
| 232 | 251 | if (!isExecutorThread) { | |
| 252 | + // Temporary workaround for LFS checkout. Code added to be reverted. | ||
| 253 | + --Executor::executor->currentGitThreads; | ||
| 233 | 254 | Executor::executor = nullptr; | |
| 234 | 255 | } | |
| 235 | 256 | } | |
@@ -378,21 +399,46 @@ namespace nodegit { | |||
| 378 | 399 | std::shared_ptr<std::condition_variable> callbackCondition(new std::condition_variable); | |
| 379 | 400 | bool hasCompleted = false; | |
| 380 | 401 | ||
| 381 | - LockMaster::TemporaryUnlock temporaryUnlock; | ||
| 402 | + // Temporary workaround for LFS checkout. Code removed to be reverted. | ||
| 403 | + //LockMaster::TemporaryUnlock temporaryUnlock; | ||
| 404 | + | ||
| 405 | + // Temporary workaround for LFS checkout. Code added to be reverted. | ||
| 406 | + bool isWorkerThreaded = executor.IsGitThreaded(); | ||
| 407 | + ThreadPool::Callback callbackCompleted = []() {}; | ||
| 408 | + if (!isWorkerThreaded) { | ||
| 409 | + callbackCompleted = [callbackCondition, callbackMutex, &hasCompleted]() { | ||
| 410 | + std::lock_guard<std::mutex> lock(*callbackMutex); | ||
| 411 | + hasCompleted = true; | ||
| 412 | + callbackCondition->notify_one(); | ||
| 413 | + }; | ||
| 414 | + } | ||
| 415 | + std::unique_ptr<LockMaster::TemporaryUnlock> temporaryUnlock {nullptr}; | ||
| 416 | + if (!isWorkerThreaded) { | ||
| 417 | + temporaryUnlock = std::make_unique<LockMaster::TemporaryUnlock>(); | ||
| 418 | + } | ||
| 419 | + | ||
| 382 | 420 | auto onCompletedCallback = (*callbackEvent)( | |
| 383 | 421 | [this](ThreadPool::Callback callback, ThreadPool::Callback cancelCallback) { | |
| 384 | 422 | queueCallbackOnJSThread(callback, cancelCallback, false); | |
| 385 | 423 | }, | |
| 424 | + // Temporary workaround for LFS checkout. Code modified to be reverted. | ||
| 425 | + /* | ||
| 386 | 426 | [callbackCondition, callbackMutex, &hasCompleted]() { | |
| 387 | 427 | std::lock_guard<std::mutex> lock(*callbackMutex); | |
| 388 | 428 | hasCompleted = true; | |
| 389 | 429 | callbackCondition->notify_one(); | |
| 390 | 430 | } | |
| 431 | + */ | ||
| 432 | + callbackCompleted, | ||
| 433 | + isWorkerThreaded | ||
| 391 | 434 | ); | |
| 392 | 435 | ||
| 393 | - std::unique_lock<std::mutex> lock(*callbackMutex); | ||
| 394 | - while (!hasCompleted) callbackCondition->wait(lock); | ||
| 395 | - onCompletedCallback(); | ||
| 436 | + // Temporary workaround for LFS checkout. Code modified to be reverted. | ||
| 437 | + if (!isWorkerThreaded) { | ||
| 438 | + std::unique_lock<std::mutex> lock(*callbackMutex); | ||
| 439 | + while (!hasCompleted) callbackCondition->wait(lock); | ||
| 440 | + onCompletedCallback(); | ||
| 441 | + } | ||
| 396 | 442 | } | |
| 397 | 443 | ||
| 398 | 444 | queueCallbackOnJSThread( | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -367,7 +367,8 @@ describe("Filter", function() { | |||
| 367 | 367 | ||
| 368 | 368 | // 'Checkout.head' and 'Submodule.lookup' do work with the repo locked. | |
| 369 | 369 | // They should work together without deadlocking. | |
| 370 | - it("can run async callback on checkout without deadlocking", function() { // jshint ignore:line | ||
| 370 | + // Temporary workaround for LFS checkout. Test skipped to be reverted. | ||
| 371 | + it.skip("can run async callback on checkout without deadlocking", function() { // jshint ignore:line | ||
| 371 | 372 | var test = this; | |
| 372 | 373 | var submoduleNameIn = "vendor/libgit2"; | |
| 373 | 374 | var asyncCallbackResult = ""; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments