From bc737cdbd061f9776f821529e75922b7d7973b85 Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 18:09:54 -0400 Subject: [PATCH 01/14] Add initial native bindings thread pool implementation --- binding.gyp | 12 +++ .../threadpool/include/threadpool.h | 52 +++++++++ .../threadpool/src/threadpool.cc | 102 ++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 deps/exokit-bindings/threadpool/include/threadpool.h create mode 100644 deps/exokit-bindings/threadpool/src/threadpool.cc diff --git a/binding.gyp b/binding.gyp index 3e6f59b429..ebe77a3d22 100644 --- a/binding.gyp +++ b/binding.gyp @@ -12,6 +12,7 @@ 'deps/exokit-bindings/util/src/*.cc', 'deps/exokit-bindings/console/src/*.cc', 'deps/exokit-bindings/cache/src/*.cc', + 'deps/exokit-bindings/threadpool/src/*.cc', 'deps/exokit-bindings/browser/src/*.cpp', 'deps/exokit-bindings/canvas/src/*.cpp', 'deps/exokit-bindings/nanosvg/src/*.cpp', @@ -49,6 +50,7 @@ '<(module_root_dir)/deps/exokit-bindings/util/include', '<(module_root_dir)/deps/exokit-bindings/console/include', '<(module_root_dir)/deps/exokit-bindings/cache/include', + '<(module_root_dir)/deps/exokit-bindings/threadpool/include', '<(module_root_dir)/deps/exokit-bindings/bindings/include', '<(module_root_dir)/deps/exokit-bindings/canvas/include', '<(module_root_dir)/deps/exokit-bindings/browser/include', @@ -135,6 +137,7 @@ ' +#include +#include + +#include +#include + +#include + +using namespace v8; + +#define NUM_THREADS 4 + +namespace threadpool { + +class QueueEntry { +public: + QueueEntry(std::function workFn, std::function cbFn); + QueueEntry(); + + std::function workFn; + std::function cbFn; +}; + +class ThreadPool { +public: + ThreadPool(); + ~ThreadPool(); + + void queueWork(std::function workFn, std::function cbFn); + static void ThreadPool::asyncFn(uv_async_t *handle); + +// protected: + std::vector threads; + std::deque reqQueue; + std::deque> resQueue; + uv_async_t *asyncHandle; + std::mutex mutex; + uv_sem_t sem; + bool live; +}; + +extern thread_local ThreadPool *windowThreadPool; +ThreadPool *getWindowThreadPool(); +void destroyWindowThreadPool(); + +}; + +#endif \ No newline at end of file diff --git a/deps/exokit-bindings/threadpool/src/threadpool.cc b/deps/exokit-bindings/threadpool/src/threadpool.cc new file mode 100644 index 0000000000..193ab5e171 --- /dev/null +++ b/deps/exokit-bindings/threadpool/src/threadpool.cc @@ -0,0 +1,102 @@ +#include + +namespace threadpool { + +QueueEntry::QueueEntry(std::function workFn, std::function cbFn) : workFn(workFn), cbFn(cbFn) {} +QueueEntry::QueueEntry() {} + +ThreadPool::ThreadPool() : live(true) { + threads.reserve(NUM_THREADS); + for (int i = 0; i < NUM_THREADS; i++) { + std::thread *thread = new std::thread([this]() -> void { + for (;;) { + QueueEntry queueEntry; + + { + std::lock_guard lock(mutex); + + if (live) { + queueEntry = std::move(reqQueue.front()); + reqQueue.pop_front(); + } + } + + if (queueEntry.workFn) { + queueEntry.workFn(); + + resQueue.push_back(queueEntry.cbFn); + uv_async_send(asyncHandle); + } else { + break; + } + } + }); + threads.push_back(thread); + } + + asyncHandle = new uv_async_t(); + asyncHandle->data = this; + uv_loop_t *loop = windowsystembase::GetEventLoop(); + uv_async_init(loop, asyncHandle, asyncFn); + uv_sem_init(&sem, NUM_THREADS); +} + +void deleteHandle(uv_handle_t *handle) { + delete (uv_async_t *)handle; +} +ThreadPool::~ThreadPool() { + { + std::lock_guard lock(mutex); + + live = false; + } + + for (int i = 0; i < NUM_THREADS; i++) { + uv_sem_post(&sem); + } + for (int i = 0; i < NUM_THREADS; i++) { + delete threads[i]; + } + uv_close((uv_handle_t *)asyncHandle, deleteHandle); + uv_sem_destroy(&sem); +} + +void ThreadPool::queueWork(std::function workFn, std::function cbFn) { + { + std::lock_guard lock(mutex); + + reqQueue.push_back(QueueEntry(workFn, cbFn)); + + uv_sem_post(&sem); + } +} + +void ThreadPool::asyncFn(uv_async_t *handle) { + ThreadPool *threadPool = (ThreadPool *)handle->data; + + std::function queueEntry; + { + std::lock_guard lock(threadPool->mutex); + + queueEntry = threadPool->resQueue.front(); + threadPool->resQueue.pop_front(); + } + + queueEntry(); +} + +thread_local ThreadPool *windowThreadPool = nullptr; +ThreadPool *getWindowThreadPool() { + if (!windowThreadPool) { + windowThreadPool = new ThreadPool(); + } + return windowThreadPool; +} +void destroyWindowThreadPool() { + if (windowThreadPool) { + delete windowThreadPool; + windowThreadPool = nullptr; + } +} + +}; \ No newline at end of file From 29774b7618923c00787427b7846d6edbb5c90092 Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 18:10:18 -0400 Subject: [PATCH 02/14] Small image-context header include sorting cleanup --- deps/exokit-bindings/canvascontext/include/image-context.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/deps/exokit-bindings/canvascontext/include/image-context.h b/deps/exokit-bindings/canvascontext/include/image-context.h index f7fcde37a1..edbbc92d31 100644 --- a/deps/exokit-bindings/canvascontext/include/image-context.h +++ b/deps/exokit-bindings/canvascontext/include/image-context.h @@ -1,9 +1,13 @@ #ifndef _CANVASCONTEXT_IMAGE_H_ #define _CANVASCONTEXT_IMAGE_H_ +#include +#include + #include #include #include + #include #include #include @@ -14,8 +18,6 @@ #include #include #include -#include -#include using namespace v8; using namespace node; From 82b71b0f14055e2556b65644e2f3a652dd3a21a8 Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 18:10:49 -0400 Subject: [PATCH 03/14] Hook in new thread pool implementation to image loading --- .../canvascontext/include/image-context.h | 3 +- .../canvascontext/src/image-context.cc | 65 ++++++++----------- 2 files changed, 29 insertions(+), 39 deletions(-) diff --git a/deps/exokit-bindings/canvascontext/include/image-context.h b/deps/exokit-bindings/canvascontext/include/image-context.h index edbbc92d31..158c852120 100644 --- a/deps/exokit-bindings/canvascontext/include/image-context.h +++ b/deps/exokit-bindings/canvascontext/include/image-context.h @@ -18,6 +18,7 @@ #include #include #include +#include using namespace v8; using namespace node; @@ -29,7 +30,6 @@ class Image : public ObjectWrap { unsigned int GetHeight(); unsigned int GetNumChannels(); // unsigned char *GetData(); - static void RunInMainThread(uv_async_t *handle); void Load(Local arrayBuffer, size_t byteOffset, size_t byteLength, Local cbFn); // void Set(canvas::Image *image); @@ -52,7 +52,6 @@ class Image : public ObjectWrap { bool loading; bool hasCbFn; std::string error; - uv_async_t threadAsyncHandle; uv_sem_t sem; friend class CanvasRenderingContext2D; diff --git a/deps/exokit-bindings/canvascontext/src/image-context.cc b/deps/exokit-bindings/canvascontext/src/image-context.cc index 5794355d80..cb2fe7a78b 100644 --- a/deps/exokit-bindings/canvascontext/src/image-context.cc +++ b/deps/exokit-bindings/canvascontext/src/image-context.cc @@ -46,28 +46,6 @@ unsigned int Image::GetNumChannels() { } } */ -void Image::RunInMainThread(uv_async_t *handle) { - Nan::HandleScope scope; - - Image *image = (Image *)handle->data; - - Local asyncObject = Nan::New(); - AsyncResource asyncResource(Isolate::GetCurrent(), asyncObject, "Image::RunInMainThread"); - - Local cbFn = Nan::New(image->cbFn); - Local arg0 = Nan::New(image->error).ToLocalChecked(); - Local argv[] = { - arg0, - }; - asyncResource.MakeCallback(cbFn, sizeof(argv)/sizeof(argv[0]), argv); - - image->cbFn.Reset(); - image->arrayBuffer.Reset(); - image->error = ""; - - uv_close((uv_handle_t *)handle, nullptr); -} - void Image::Load(Local arrayBuffer, size_t byteOffset, size_t byteLength, Local cbFn) { if (!this->loading) { unsigned char *buffer = (unsigned char *)arrayBuffer->GetContents().Data() + byteOffset; @@ -76,16 +54,8 @@ void Image::Load(Local arrayBuffer, size_t byteOffset, size_t byteL this->cbFn.Reset(cbFn); this->loading = true; this->hasCbFn = !cbFn.IsEmpty(); - - if (this->hasCbFn) { - uv_loop_t *loop = windowsystembase::GetEventLoop(); - uv_async_init(loop, &this->threadAsyncHandle, RunInMainThread); - this->threadAsyncHandle.data = this; - } else { - uv_sem_init(&this->sem, 0); - } - - std::thread([this, buffer, byteLength]() -> void { + + auto loadFn = [this, buffer, byteLength]() -> void { sk_sp data = SkData::MakeWithoutCopy(buffer, byteLength); SkBitmap bitmap; bool ok = DecodeDataToBitmap(data, &bitmap); @@ -134,14 +104,35 @@ void Image::Load(Local arrayBuffer, size_t byteOffset, size_t byteL } } - if (this->hasCbFn) { - uv_async_send(&this->threadAsyncHandle); - } else { + if (!this->hasCbFn) { uv_sem_post(&this->sem); } - }).detach(); + }; + + if (this->hasCbFn) { + threadpool::ThreadPool *threadPool = threadpool::getWindowThreadPool(); + threadPool->queueWork(loadFn, [this]() -> void { + Nan::HandleScope scope; + + Local asyncObject = Nan::New(); + AsyncResource asyncResource(Isolate::GetCurrent(), asyncObject, "Image::Load"); + + Local cbFn = Nan::New(this->cbFn); + Local arg0 = Nan::New(this->error).ToLocalChecked(); + Local argv[] = { + arg0, + }; + asyncResource.MakeCallback(cbFn, sizeof(argv)/sizeof(argv[0]), argv); + + this->cbFn.Reset(); + this->arrayBuffer.Reset(); + this->error = ""; + }); + } else { + uv_sem_init(&this->sem, 0); + + loadFn(); - if (!this->hasCbFn) { uv_sem_wait(&this->sem); uv_sem_destroy(&this->sem); From b02a1c18ef06a5a636d9779c221139325ab18e12 Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 19:19:45 -0400 Subject: [PATCH 04/14] Bugfix threadpool initialization order --- deps/exokit-bindings/threadpool/src/threadpool.cc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/deps/exokit-bindings/threadpool/src/threadpool.cc b/deps/exokit-bindings/threadpool/src/threadpool.cc index 193ab5e171..33c1846c03 100644 --- a/deps/exokit-bindings/threadpool/src/threadpool.cc +++ b/deps/exokit-bindings/threadpool/src/threadpool.cc @@ -6,12 +6,19 @@ QueueEntry::QueueEntry(std::function workFn, std::function cbFn) QueueEntry::QueueEntry() {} ThreadPool::ThreadPool() : live(true) { + asyncHandle = new uv_async_t(); + asyncHandle->data = this; + uv_loop_t *loop = windowsystembase::GetEventLoop(); + uv_async_init(loop, asyncHandle, asyncFn); + uv_sem_init(&sem, 0); + threads.reserve(NUM_THREADS); for (int i = 0; i < NUM_THREADS; i++) { std::thread *thread = new std::thread([this]() -> void { for (;;) { - QueueEntry queueEntry; + uv_sem_wait(&sem); + QueueEntry queueEntry; { std::lock_guard lock(mutex); @@ -33,12 +40,6 @@ ThreadPool::ThreadPool() : live(true) { }); threads.push_back(thread); } - - asyncHandle = new uv_async_t(); - asyncHandle->data = this; - uv_loop_t *loop = windowsystembase::GetEventLoop(); - uv_async_init(loop, asyncHandle, asyncFn); - uv_sem_init(&sem, NUM_THREADS); } void deleteHandle(uv_handle_t *handle) { From ed08c4a89bd91f3183741260051f74dc617060c0 Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 19:20:22 -0400 Subject: [PATCH 05/14] Bugfix threadpool async function loop not handling all responses --- .../threadpool/src/threadpool.cc | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/deps/exokit-bindings/threadpool/src/threadpool.cc b/deps/exokit-bindings/threadpool/src/threadpool.cc index 33c1846c03..e7106f7f0d 100644 --- a/deps/exokit-bindings/threadpool/src/threadpool.cc +++ b/deps/exokit-bindings/threadpool/src/threadpool.cc @@ -75,15 +75,23 @@ void ThreadPool::queueWork(std::function workFn, std::function c void ThreadPool::asyncFn(uv_async_t *handle) { ThreadPool *threadPool = (ThreadPool *)handle->data; - std::function queueEntry; - { - std::lock_guard lock(threadPool->mutex); + for (;;) { + std::function queueEntry; + { + std::lock_guard lock(threadPool->mutex); + + if (threadPool->resQueue.size() > 0) { + queueEntry = threadPool->resQueue.front(); + threadPool->resQueue.pop_front(); + } + } - queueEntry = threadPool->resQueue.front(); - threadPool->resQueue.pop_front(); + if (queueEntry) { + queueEntry(); + } else { + break; + } } - - queueEntry(); } thread_local ThreadPool *windowThreadPool = nullptr; From f882dd2e5027655bce4f597810d5370dc9e54275 Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 19:42:49 -0400 Subject: [PATCH 06/14] Bump max thread count --- deps/exokit-bindings/threadpool/include/threadpool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/exokit-bindings/threadpool/include/threadpool.h b/deps/exokit-bindings/threadpool/include/threadpool.h index 35baaa6ea8..2479c6b879 100644 --- a/deps/exokit-bindings/threadpool/include/threadpool.h +++ b/deps/exokit-bindings/threadpool/include/threadpool.h @@ -12,7 +12,7 @@ using namespace v8; -#define NUM_THREADS 4 +#define NUM_THREADS 8 namespace threadpool { From 291d111dc33344711b095bf70f99503af645f110 Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 19:43:22 -0400 Subject: [PATCH 07/14] Add ThreadPool::queueWork default arguments --- deps/exokit-bindings/threadpool/include/threadpool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/exokit-bindings/threadpool/include/threadpool.h b/deps/exokit-bindings/threadpool/include/threadpool.h index 2479c6b879..898e06ec96 100644 --- a/deps/exokit-bindings/threadpool/include/threadpool.h +++ b/deps/exokit-bindings/threadpool/include/threadpool.h @@ -30,7 +30,7 @@ class ThreadPool { ThreadPool(); ~ThreadPool(); - void queueWork(std::function workFn, std::function cbFn); + void queueWork(std::function workFn = []() -> void {}, std::function cbFn = []() -> void {}); static void ThreadPool::asyncFn(uv_async_t *handle); // protected: From 98343544703f40475cef5430ca48539dffeb2ac5 Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 19:43:43 -0400 Subject: [PATCH 08/14] Use thread pool in audio loading --- .../exokit-bindings/webaudiocontext/include/AudioContext.h | 1 + deps/exokit-bindings/webaudiocontext/src/Audio.cpp | 7 ++++--- deps/exokit-bindings/webaudiocontext/src/AudioBuffer.cpp | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/deps/exokit-bindings/webaudiocontext/include/AudioContext.h b/deps/exokit-bindings/webaudiocontext/include/AudioContext.h index dabf62951e..7693db2f69 100644 --- a/deps/exokit-bindings/webaudiocontext/include/AudioContext.h +++ b/deps/exokit-bindings/webaudiocontext/include/AudioContext.h @@ -21,6 +21,7 @@ #include #include #include +#include #include using namespace std; diff --git a/deps/exokit-bindings/webaudiocontext/src/Audio.cpp b/deps/exokit-bindings/webaudiocontext/src/Audio.cpp index d3f70359a3..c6fdc332a7 100644 --- a/deps/exokit-bindings/webaudiocontext/src/Audio.cpp +++ b/deps/exokit-bindings/webaudiocontext/src/Audio.cpp @@ -61,11 +61,12 @@ void Audio::Load(uint8_t *bufferValue, size_t bufferLength, Local cbFn WebAudioAsync *webAudioAsync = getWebAudioAsync(); std::vector buffer(bufferLength); memcpy(buffer.data(), bufferValue, bufferLength); - std::thread([this, webAudioAsync, buffer{std::move(buffer)}]() mutable -> void { + threadpool::ThreadPool *threadPool = threadpool::getWindowThreadPool(); + threadPool->queueWork([this, buffer{std::move(buffer)}]() mutable -> void { this->audioBus = lab::MakeBusFromMemory(buffer, false, &this->error); - + webAudioAsync->QueueOnMainThread(std::bind(ProcessLoadInMainThread, this)); - }).detach(); + }); } else { Local arg0 = Nan::New("already loading").ToLocalChecked(); Local argv[] = { diff --git a/deps/exokit-bindings/webaudiocontext/src/AudioBuffer.cpp b/deps/exokit-bindings/webaudiocontext/src/AudioBuffer.cpp index 9bae6d3f91..7948ec9632 100644 --- a/deps/exokit-bindings/webaudiocontext/src/AudioBuffer.cpp +++ b/deps/exokit-bindings/webaudiocontext/src/AudioBuffer.cpp @@ -210,11 +210,13 @@ void AudioBuffer::Load(Local arrayBuffer, size_t byteOffset, size_t WebAudioAsync *webAudioAsync = getWebAudioAsync(); std::vector buffer(byteLength); memcpy(buffer.data(), (unsigned char *)arrayBuffer->GetContents().Data() + byteOffset, byteLength); - std::thread([this, webAudioAsync, buffer{std::move(buffer)}]() mutable -> void { + + threadpool::ThreadPool *threadPool = threadpool::getWindowThreadPool(); + threadPool->queueWork([this, webAudioAsync, buffer{std::move(buffer)}]() mutable -> void { this->audioBus = lab::MakeBusFromMemory(buffer, false, &this->error); webAudioAsync->QueueOnMainThread(std::bind(ProcessLoadInMainThread, this)); - }).detach(); + }); } else { Local arg0 = Nan::New("already loading").ToLocalChecked(); Local argv[] = { From 7c390a242cbc1ccba2e5ae58e69c2e3025391b34 Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 19:48:00 -0400 Subject: [PATCH 09/14] Bugfix Audio.cpp work queueing capture --- deps/exokit-bindings/webaudiocontext/src/Audio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/exokit-bindings/webaudiocontext/src/Audio.cpp b/deps/exokit-bindings/webaudiocontext/src/Audio.cpp index c6fdc332a7..b9af4eaee5 100644 --- a/deps/exokit-bindings/webaudiocontext/src/Audio.cpp +++ b/deps/exokit-bindings/webaudiocontext/src/Audio.cpp @@ -62,7 +62,7 @@ void Audio::Load(uint8_t *bufferValue, size_t bufferLength, Local cbFn std::vector buffer(bufferLength); memcpy(buffer.data(), bufferValue, bufferLength); threadpool::ThreadPool *threadPool = threadpool::getWindowThreadPool(); - threadPool->queueWork([this, buffer{std::move(buffer)}]() mutable -> void { + threadPool->queueWork([this, webAudioAsync, buffer{std::move(buffer)}]() mutable -> void { this->audioBus = lab::MakeBusFromMemory(buffer, false, &this->error); webAudioAsync->QueueOnMainThread(std::bind(ProcessLoadInMainThread, this)); From f3acdeaf9c76082608545cc2acd09206e6e1bac6 Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 20:14:27 -0400 Subject: [PATCH 10/14] Bugfix threadpool.h extra qualification --- deps/exokit-bindings/threadpool/include/threadpool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/exokit-bindings/threadpool/include/threadpool.h b/deps/exokit-bindings/threadpool/include/threadpool.h index 898e06ec96..f5342d890a 100644 --- a/deps/exokit-bindings/threadpool/include/threadpool.h +++ b/deps/exokit-bindings/threadpool/include/threadpool.h @@ -31,7 +31,7 @@ class ThreadPool { ~ThreadPool(); void queueWork(std::function workFn = []() -> void {}, std::function cbFn = []() -> void {}); - static void ThreadPool::asyncFn(uv_async_t *handle); + static void asyncFn(uv_async_t *handle); // protected: std::vector threads; From 0cbdfc32c0e55b0767b3ae0ebd2123f14ee2def5 Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 20:45:34 -0400 Subject: [PATCH 11/14] Add windowsystem DestroyThreadPool method hook --- deps/exokit-bindings/windowsystem/src/windowsystem.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/deps/exokit-bindings/windowsystem/src/windowsystem.cc b/deps/exokit-bindings/windowsystem/src/windowsystem.cc index 530472220b..85b380eb49 100644 --- a/deps/exokit-bindings/windowsystem/src/windowsystem.cc +++ b/deps/exokit-bindings/windowsystem/src/windowsystem.cc @@ -1,4 +1,5 @@ #include +#include #include @@ -1370,6 +1371,10 @@ NAN_METHOD(ClearFramebuffer) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); } +NAN_METHOD(DestroyThreadPool) { + threadpool::destroyWindowThreadPool(); +} + void Decorate(Local target) { Nan::SetMethod(target, "createRenderTarget", CreateRenderTarget); Nan::SetMethod(target, "resizeRenderTarget", ResizeRenderTarget); @@ -1383,6 +1388,7 @@ void Decorate(Local target) { Nan::SetMethod(target, "deleteSync", DeleteSync); Nan::SetMethod(target, "composeLayers", ComposeLayers); Nan::SetMethod(target, "clearFramebuffer", ClearFramebuffer); + Nan::SetMethod(target, "destroyThreadPool", DestroyThreadPool); } } From 59793835cd50b85e1cbc9ee7aa5f4f23b7805fdb Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 20:45:55 -0400 Subject: [PATCH 12/14] Call native window destroy thread pool on exit --- src/Window.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Window.js b/src/Window.js index 89995b4ca7..4e4129cd35 100644 --- a/src/Window.js +++ b/src/Window.js @@ -1438,5 +1438,6 @@ global.onexit = () => { } AudioContext.Destroy(); + nativeWindow.destroyThreadPool(); }; // global.setImmediate = undefined; // need this for the TLS implementation From b4ec03b9b2865e389660a992599be17d3e87208f Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 21:34:35 -0400 Subject: [PATCH 13/14] Do full copy of image data before loading in side thread --- .../canvascontext/include/image-context.h | 1 - .../canvascontext/src/image-context.cc | 14 ++++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/deps/exokit-bindings/canvascontext/include/image-context.h b/deps/exokit-bindings/canvascontext/include/image-context.h index 158c852120..d4d29949d0 100644 --- a/deps/exokit-bindings/canvascontext/include/image-context.h +++ b/deps/exokit-bindings/canvascontext/include/image-context.h @@ -47,7 +47,6 @@ class Image : public ObjectWrap { sk_sp image; Nan::Persistent dataArray; - Nan::Persistent arrayBuffer; Nan::Persistent cbFn; bool loading; bool hasCbFn; diff --git a/deps/exokit-bindings/canvascontext/src/image-context.cc b/deps/exokit-bindings/canvascontext/src/image-context.cc index cb2fe7a78b..8805aa5b02 100644 --- a/deps/exokit-bindings/canvascontext/src/image-context.cc +++ b/deps/exokit-bindings/canvascontext/src/image-context.cc @@ -49,14 +49,15 @@ unsigned int Image::GetNumChannels() { void Image::Load(Local arrayBuffer, size_t byteOffset, size_t byteLength, Local cbFn) { if (!this->loading) { unsigned char *buffer = (unsigned char *)arrayBuffer->GetContents().Data() + byteOffset; + sk_sp extendedData = SkData::MakeUninitialized(byteLength + 1); + memcpy((unsigned char *)extendedData->data(), buffer, byteLength); + sk_sp data = SkData::MakeWithoutCopy(buffer, byteLength); - this->arrayBuffer.Reset(arrayBuffer); this->cbFn.Reset(cbFn); this->loading = true; this->hasCbFn = !cbFn.IsEmpty(); - auto loadFn = [this, buffer, byteLength]() -> void { - sk_sp data = SkData::MakeWithoutCopy(buffer, byteLength); + auto loadFn = [this, extendedData, data]() -> void { SkBitmap bitmap; bool ok = DecodeDataToBitmap(data, &bitmap); @@ -64,11 +65,9 @@ void Image::Load(Local arrayBuffer, size_t byteOffset, size_t byteL bitmap.setImmutable(); this->image = SkImage::MakeFromBitmap(bitmap); } else { - unique_ptr svgString(new char[byteLength + 1]); - memcpy(svgString.get(), buffer, byteLength); - svgString[byteLength] = 0; + ((char *)extendedData->data())[extendedData->size() - 1] = 0; // NUL-terminate - NSVGimage *svgImage = nsvgParse(svgString.get(), "px", 96); + NSVGimage *svgImage = nsvgParse((char *)extendedData->data(), "px", 96); if (svgImage != nullptr) { if (svgImage->width > 0 && svgImage->height > 0 && svgImage->shapes != nullptr) { int w = svgImage->width; @@ -125,7 +124,6 @@ void Image::Load(Local arrayBuffer, size_t byteOffset, size_t byteL asyncResource.MakeCallback(cbFn, sizeof(argv)/sizeof(argv[0]), argv); this->cbFn.Reset(); - this->arrayBuffer.Reset(); this->error = ""; }); } else { From 654fa7cf19fb3f14b925562373111a3bceaf3283 Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Mon, 29 Jul 2019 21:35:17 -0400 Subject: [PATCH 14/14] Bugfix dangling response handling from destroyed windows --- src/Window.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Window.js b/src/Window.js index 4e4129cd35..11a3185497 100644 --- a/src/Window.js +++ b/src/Window.js @@ -1344,11 +1344,10 @@ global.onrunasync = req => { error: req.error, result: req.result, }); - - return Promise.resolve(); } else { - return Promise.reject(new Error(`response for unknown window ${method} ${JSON.stringify(windows.map(window => window.id))}`)); + console.warn('ignoring unknown response', req, {windowId}); } + return Promise.resolve(); } } case 'keyEvent': {