Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7949764
Cleanup unused ifdefs
implausible Jul 31, 2020
33326b4
Guard initialization so that we only initialize our core libraries once
implausible Jul 31, 2020
5ad8682
Use NAN_MODULE_INIT and prevent worker_threads from booting library
implausible Jul 31, 2020
6167a10
Make LockMaster shareable across contexts
implausible Aug 3, 2020
a820ff4
Get rid of libuv in lock_master
implausible Aug 5, 2020
67d77d9
Get rid of unnecessary libuv in thread pool
implausible Aug 17, 2020
88711bf
Fix jshint
implausible Aug 18, 2020
deb1430
Rewrite thread pool for context awareness
implausible Aug 25, 2020
42e266e
Skeleton shutdown logic for thread pool and cancellation of work
implausible Aug 25, 2020
317f3a7
Complete implementation of libgit2 callback cancellation
implausible Aug 28, 2020
fa03769
Add HandleErrorCallback skeleton
ianhattendorf Aug 27, 2020
f259b6f
Add skeleton of manual templates
ianhattendorf Aug 27, 2020
50444d0
Fix Repository.discover description
ianhattendorf Aug 27, 2020
c0ff5e6
Cleanup additional baton members
ianhattendorf Aug 27, 2020
ce43d92
Call callback from HandleErrorCallback
ianhattendorf Aug 27, 2020
223bf7e
Don't call callback if AsyncWorker is cancelled
ianhattendorf Aug 28, 2020
af5585c
Cleanup HandleErrorCallback template
ianhattendorf Aug 28, 2020
610104b
Fill in skeleton HandleErrorCallbacks
ianhattendorf Aug 28, 2020
9a51900
Add missing HandleErrorCallbacks
ianhattendorf Aug 28, 2020
bed58a4
Value initialize batons
ianhattendorf Sep 1, 2020
74fe6e1
Use freeFunctionName to free returned args on error
ianhattendorf Sep 1, 2020
046a7ad
Value initialize manual template batons as well
ianhattendorf Sep 2, 2020
bf7b13f
Cleanup manual template batons
ianhattendorf Sep 2, 2020
e4a9092
Enable module from workers
ianhattendorf Sep 3, 2020
7f358d1
Test clone via worker thread
ianhattendorf Sep 3, 2020
7c50ce5
Allow nodegit to run via worker thread or web worker
ianhattendorf Sep 3, 2020
f82e10c
More reliable worker thread test
ianhattendorf Sep 3, 2020
8a5ee94
Remove duplicate locks
ianhattendorf Sep 8, 2020
aaa34ee
Verify context before running cppCallback
ianhattendorf Sep 10, 2020
69d9d4e
Stick to c++11 for a little bit longer
ianhattendorf Sep 11, 2020
ee1865c
Only test worker_threads if they are available
ianhattendorf Sep 11, 2020
a21efa6
Threadpool shutdown uv_close asynchronously
ianhattendorf Sep 11, 2020
130560b
Fix misc warnings
ianhattendorf Sep 11, 2020
9d5a42b
Test on node 14 only until backport of required node PR
ianhattendorf Sep 11, 2020
01765fb
Don't leak jsThreadCallbackAsync
ianhattendorf Sep 11, 2020
fb17372
Build a cleanup handle that can be deleted by last user
implausible Sep 12, 2020
7a7e1fa
Use a better data type for data on uv_async_t
implausible Sep 12, 2020
cb7cd26
Fix ordering issue on Windows
implausible Sep 14, 2020
85f55d4
Require 12.19.0+ or 14.10.0+ due to async cleanup
ianhattendorf Oct 19, 2020
08db6fc
Check for latest node version when running tests
ianhattendorf Oct 19, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Skeleton shutdown logic for thread pool and cancellation of work
  • Loading branch information
implausible committed Aug 27, 2020
commit 42e266ed124b1524acaa693a499c08f506db543d
10 changes: 5 additions & 5 deletions generate/templates/manual/include/async_baton.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace nodegit {
Nan::AsyncResource *GetAsyncResource();

protected:
void ExecuteAsyncPerform(AsyncCallback asyncCallback, CompletionCallback onCompletion);
void ExecuteAsyncPerform(AsyncCallback asyncCallback, AsyncCallback asyncCancelCb, CompletionCallback onCompletion);

private:
void SignalCompletion();
Expand All @@ -53,17 +53,17 @@ namespace nodegit {
: defaultResult(defaultResult) {
}

ResultT ExecuteAsync(AsyncBaton::AsyncCallback asyncCallback, AsyncBaton::CompletionCallback onCompletion = nullptr) {
ResultT ExecuteAsync(AsyncBaton::AsyncCallback asyncCallback, AsyncBaton::AsyncCallback asyncCancelCb, AsyncBaton::CompletionCallback onCompletion = nullptr) {
result = 0;
ExecuteAsyncPerform(asyncCallback, onCompletion);
ExecuteAsyncPerform(asyncCallback, asyncCancelCb, onCompletion);
return result;
}
};

class AsyncBatonWithNoResult : public AsyncBaton {
public:
void ExecuteAsync(AsyncBaton::AsyncCallback asyncCallback, AsyncBaton::CompletionCallback onCompletion = nullptr) {
ExecuteAsyncPerform(asyncCallback, onCompletion);
void ExecuteAsync(AsyncBaton::AsyncCallback asyncCallback, AsyncBaton::AsyncCallback asyncCancelCb, AsyncBaton::CompletionCallback onCompletion = nullptr) {
ExecuteAsyncPerform(asyncCallback, asyncCancelCb, onCompletion);
}
};
}
Expand Down
10 changes: 10 additions & 0 deletions generate/templates/manual/include/async_worker.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,18 @@ namespace nodegit {
public:
AsyncWorker(Nan::Callback *callback, const char *resourceName);

// This must be implemented by every async worker
// so that the thread pool can lock separately
// from the execute method in the AsyncWorker
virtual nodegit::LockMaster AcquireLocks() = 0;

// Ensure that the `HandleErrorCallback` will be called
// when the AsyncWork is complete
void Cancel();

// Retrieves the async resource attached to this AsyncWorker
// This is used to inform libgit2 callbacks what asyncResource
// they should use when working with any javascript
Nan::AsyncResource *GetAsyncResource();
};
}
Expand Down
10 changes: 6 additions & 4 deletions generate/templates/manual/include/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ namespace nodegit {
public:
Context(v8::Isolate *isolate);

void QueueWorker(nodegit::AsyncWorker *worker);
~Context();

void SaveToPersistent(std::string key, const v8::Local<v8::Value> &value);
static Context *GetCurrentContext();

v8::Local<v8::Value> GetFromPersistent(std::string key);

static Context *GetCurrentContext();
void QueueWorker(nodegit::AsyncWorker *worker);

~Context();
void SaveToPersistent(std::string key, const v8::Local<v8::Value> &value);

void ShutdownThreadPool();

private:
v8::Isolate *isolate;
Expand Down
6 changes: 5 additions & 1 deletion generate/templates/manual/include/thread_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace nodegit {
class ThreadPool {
public:
typedef std::function<void()> Callback;
typedef std::function<void(Callback)> QueueCallbackFn;
typedef std::function<void(Callback, Callback)> QueueCallbackFn;
typedef std::function<Callback(QueueCallbackFn, Callback)> OnPostCallbackFn;

// Initializes thread pool and spins up the requested number of threads
Expand All @@ -41,6 +41,10 @@ namespace nodegit {
// Called once at libgit2 initialization to setup contracts with libgit2
static void InitializeGlobal();

// Will wait for all threads to terminate before returning
// It will also clean up any resources that the thread pool is keeping alive
void Shutdown();

private:
std::unique_ptr<ThreadPoolImpl> impl;
};
Expand Down
13 changes: 8 additions & 5 deletions generate/templates/manual/src/async_baton.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,36 +23,39 @@ namespace nodegit {
return asyncResource;
}

void AsyncBaton::ExecuteAsyncPerform(AsyncCallback asyncCallback, CompletionCallback onCompletion) {
void AsyncBaton::ExecuteAsyncPerform(AsyncCallback asyncCallback, AsyncCallback asyncCancelCb, CompletionCallback onCompletion) {
auto jsCallback = [asyncCallback, this]() {
asyncCallback(this);
};
auto cancelCallback = [asyncCancelCb, this]() {
asyncCancelCb(this);
};

if (onCompletion) {
this->onCompletion = [this, onCompletion]() {
onCompletion(this);
};

ThreadPool::PostCallbackEvent(
[this, jsCallback](
[this, jsCallback, cancelCallback](
ThreadPool::QueueCallbackFn queueCallback,
ThreadPool::Callback callbackCompleted
) -> ThreadPool::Callback {
queueCallback(jsCallback);
queueCallback(jsCallback, cancelCallback);
callbackCompleted();

return []() {};
}
);
} else {
ThreadPool::PostCallbackEvent(
[this, jsCallback](
[this, jsCallback, cancelCallback](
ThreadPool::QueueCallbackFn queueCallback,
ThreadPool::Callback callbackCompleted
) -> ThreadPool::Callback {
this->onCompletion = callbackCompleted;

queueCallback(jsCallback);
queueCallback(jsCallback, cancelCallback);

return std::bind(&AsyncBaton::SignalCompletion, this);
}
Expand Down
7 changes: 7 additions & 0 deletions generate/templates/manual/src/async_worker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ namespace nodegit {
: Nan::AsyncWorker(callback, resourceName)
{}

void AsyncWorker::Cancel() {
// We use Nan::AsyncWorker's ErrorMessage flow
// to trigger `HandleErrorCallback` for cancellation
// of AsyncWork
SetErrorMessage("SHUTTING DOWN");
}

Nan::AsyncResource *AsyncWorker::GetAsyncResource() {
return async_resource;
}
Expand Down
37 changes: 25 additions & 12 deletions generate/templates/manual/src/context.cc
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
#include "../include/context.h"

namespace nodegit {
std::map<v8::Isolate *, Context *> Context::contexts;

static void CleanupContext(void *data) {
Context *context = static_cast<Context *>(data);

context->ShutdownThreadPool();

delete context;
}

Context::Context(v8::Isolate *isolate)
: isolate(isolate), threadPool(10, node::GetCurrentEventLoop(isolate))
{
Nan::HandleScope scopoe;
v8::Local<v8::Object> storage = Nan::New<v8::Object>();
persistentStorage.Reset(storage);
contexts[isolate] = this;
node::AddEnvironmentCleanupHook(isolate, CleanupContext, this);
}

Context::~Context() {
contexts.erase(isolate);
}

void Context::QueueWorker(nodegit::AsyncWorker *worker) {
threadPool.QueueWorker(worker);
}

void Context::SaveToPersistent(std::string key, const v8::Local<v8::Value> &value) {
Context *Context::GetCurrentContext() {
Nan::HandleScope scope;
v8::Local<v8::Object> storage = Nan::New(persistentStorage);
Nan::Set(storage, Nan::New(key).ToLocalChecked(), value);
v8::Local<v8::Context> context = Nan::GetCurrentContext();
v8::Isolate *isolate = context->GetIsolate();
return contexts[isolate];
}

v8::Local<v8::Value> Context::GetFromPersistent(std::string key) {
Expand All @@ -31,12 +39,17 @@ namespace nodegit {
return scope.Escape(value.ToLocalChecked());
}

Context *Context::GetCurrentContext() {
void Context::QueueWorker(nodegit::AsyncWorker *worker) {
threadPool.QueueWorker(worker);
}

void Context::SaveToPersistent(std::string key, const v8::Local<v8::Value> &value) {
Nan::HandleScope scope;
v8::Local<v8::Context> context = Nan::GetCurrentContext();
v8::Isolate *isolate = context->GetIsolate();
return contexts[isolate];
v8::Local<v8::Object> storage = Nan::New(persistentStorage);
Nan::Set(storage, Nan::New(key).ToLocalChecked(), value);
}

std::map<v8::Isolate *, Context *> Context::contexts;
void Context::ShutdownThreadPool() {
threadPool.Shutdown();
}
}
Loading