-
Notifications
You must be signed in to change notification settings - Fork 133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixes for shutting down during async operations #1141
Open
bghgary
wants to merge
25
commits into
BabylonJS:master
Choose a base branch
from
bghgary:async-fixes
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
43dce92
WIP: Fixes for shutting down during async operations
bghgary 925d243
Change to explicitly call Rundown
bghgary c999153
Cannot throw in destructors
bghgary 204efb9
Merge remote-tracking branch 'origin/master' into async-fixes
bghgary 9d1e098
Fix merge issues
bghgary 124def4
Merge with new timeout code with work queue fixes
bghgary 309c4d9
Fix Canvas
bghgary ad88d30
Fix Android build
bghgary 11ca510
Merge remote-tracking branch 'origin/master' into async-fixes
bghgary 31248b6
Merge branch 'async-fixes' of https://github.com/bghgary/BabylonNativ…
bghgary 9322360
Update arcana.cpp
bghgary b4a3c73
Temp fixes for MediaStream
bghgary 9305ca8
Add missing std::forward calls for perfect forwarding
bghgary df246e2
Work queue shutdown fixes
bghgary 7350715
Miscellaneous Windows AppRuntime fixes
bghgary 77dc8f3
Fix typo in AppRuntime for Win32
bghgary def047c
Better fix for work queue shutdown issue
bghgary bdaf1b9
Merge remote-tracking branch 'origin/master' into async-fixes
bghgary 0ae6cf4
Fix build issues from merge
bghgary 71efaf4
Update arcana.cpp to include continuation fix
bghgary a0db419
Minor style fixes
bghgary 6909f55
Update comment
bghgary e9d229d
Update comment 2
bghgary 423ed5e
More style fixes
bghgary 36a4661
Merge AppRuntime and WorkQueue to fix race condition
bghgary File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,36 @@ | ||
#include "AppRuntime.h" | ||
#include "WorkQueue.h" | ||
#include <Babylon/JsRuntime.h> | ||
#include "AppRuntimeImpl.h" | ||
|
||
namespace Babylon | ||
{ | ||
AppRuntime::AppRuntime() | ||
: AppRuntime{DefaultUnhandledExceptionHandler} | ||
: m_impl{std::make_unique<AppRuntimeImpl>()} | ||
{ | ||
} | ||
|
||
AppRuntime::AppRuntime(std::function<void(const std::exception&)> unhandledExceptionHandler) | ||
: m_workQueue{std::make_unique<WorkQueue>([this] { RunPlatformTier(); })} | ||
, m_unhandledExceptionHandler{unhandledExceptionHandler} | ||
: m_impl{std::make_unique<AppRuntimeImpl>(unhandledExceptionHandler)} | ||
{ | ||
Dispatch([this](Napi::Env env) { | ||
JsRuntime::CreateForJavaScript(env, [this](auto func) { Dispatch(std::move(func)); }); | ||
}); | ||
} | ||
|
||
AppRuntime::~AppRuntime() | ||
{ | ||
// Notify the JsRuntime on the JavaScript thread that the JavaScript | ||
// runtime shutdown sequence has begun. The JsRuntimeScheduler will | ||
// use this signal to gracefully cancel asynchronous operations. | ||
Dispatch([](Napi::Env env) { | ||
JsRuntime::NotifyDisposing(JsRuntime::GetFromJavaScript(env)); | ||
}); | ||
} | ||
AppRuntime::~AppRuntime() = default; | ||
|
||
void AppRuntime::Run(Napi::Env env) | ||
{ | ||
m_workQueue->Run(env); | ||
} | ||
// Move semantics | ||
AppRuntime::AppRuntime(AppRuntime&&) noexcept = default; | ||
AppRuntime& AppRuntime::operator=(AppRuntime&&) noexcept = default; | ||
|
||
void AppRuntime::Suspend() | ||
{ | ||
m_workQueue->Suspend(); | ||
m_impl->Suspend(); | ||
} | ||
|
||
void AppRuntime::Resume() | ||
{ | ||
m_workQueue->Resume(); | ||
m_impl->Resume(); | ||
} | ||
|
||
void AppRuntime::Dispatch(Dispatchable<void(Napi::Env)> func) | ||
void AppRuntime::Dispatch(Dispatchable<void(Napi::Env)> callback) | ||
{ | ||
m_workQueue->Append([this, func{std::move(func)}](Napi::Env env) mutable { | ||
Execute([this, env, func{std::move(func)}]() mutable { | ||
try | ||
{ | ||
func(env); | ||
} | ||
catch (const std::exception& error) | ||
{ | ||
m_unhandledExceptionHandler(error); | ||
} | ||
catch (...) | ||
{ | ||
std::abort(); | ||
} | ||
}); | ||
}); | ||
m_impl->Dispatch(std::move(callback)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
#include "AppRuntimeImpl.h" | ||
#include <Babylon/JsRuntime.h> | ||
|
||
namespace Babylon | ||
{ | ||
AppRuntimeImpl::AppRuntimeImpl(std::function<void(const std::exception&)> unhandledExceptionHandler) | ||
: m_unhandledExceptionHandler{std::move(unhandledExceptionHandler)} | ||
, m_thread{[this] { RunPlatformTier(); }} | ||
{ | ||
Dispatch([this](Napi::Env env) { | ||
JsRuntime::CreateForJavaScript(env, [this](auto func) { Dispatch(std::move(func)); }); | ||
}); | ||
} | ||
|
||
AppRuntimeImpl::~AppRuntimeImpl() | ||
{ | ||
if (m_suspensionLock.has_value()) | ||
{ | ||
m_suspensionLock.reset(); | ||
} | ||
|
||
Dispatch([this](Napi::Env env) { | ||
// Notify the JsRuntime on the JavaScript thread that the JavaScript runtime shutdown sequence has | ||
// begun. The JsRuntimeScheduler will use this signal to gracefully cancel asynchronous operations. | ||
JsRuntime::NotifyDisposing(JsRuntime::GetFromJavaScript(env)); | ||
|
||
// Cancel on the JavaScript thread to signal the Run function to gracefully end. It must be | ||
// dispatched and not canceled directly to ensure that existing work is executed and executed in | ||
// the correct order. | ||
m_cancellationSource.cancel(); | ||
}); | ||
|
||
m_thread.join(); | ||
} | ||
|
||
void AppRuntimeImpl::Suspend() | ||
{ | ||
auto suspensionMutex = std::make_shared<std::mutex>(); | ||
m_suspensionLock.emplace(*suspensionMutex); | ||
Append([suspensionMutex = std::move(suspensionMutex)](Napi::Env) { | ||
std::scoped_lock lock{*suspensionMutex}; | ||
}); | ||
} | ||
|
||
void AppRuntimeImpl::Resume() | ||
{ | ||
m_suspensionLock.reset(); | ||
} | ||
|
||
void AppRuntimeImpl::Dispatch(Dispatchable<void(Napi::Env)> func) | ||
{ | ||
Append([this, func{std::move(func)}](Napi::Env env) mutable { | ||
Execute([this, env, func{std::move(func)}]() mutable { | ||
try | ||
{ | ||
func(env); | ||
} | ||
catch (const std::exception& error) | ||
{ | ||
m_unhandledExceptionHandler(error); | ||
} | ||
catch (...) | ||
{ | ||
std::abort(); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
void AppRuntimeImpl::Run(Napi::Env env) | ||
{ | ||
m_env = std::make_optional(env); | ||
|
||
m_dispatcher.set_affinity(std::this_thread::get_id()); | ||
|
||
while (!m_cancellationSource.cancelled()) | ||
{ | ||
m_dispatcher.blocking_tick(m_cancellationSource); | ||
} | ||
|
||
// The dispatcher can be non-empty if something is dispatched after cancellation. | ||
// For example, Chakra's JsSetPromiseContinuationCallback may potentially dispatch | ||
// a continuation after cancellation. | ||
m_dispatcher.clear(); | ||
|
||
m_env.reset(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
#pragma once | ||
|
||
#include "AppRuntime.h" | ||
#include <optional> | ||
#include <mutex> | ||
#include <arcana/threading/dispatcher.h> | ||
|
||
namespace Babylon | ||
{ | ||
class AppRuntimeImpl | ||
{ | ||
public: | ||
AppRuntimeImpl(std::function<void(const std::exception&)> unhandledExceptionHandler = DefaultUnhandledExceptionHandler); | ||
~AppRuntimeImpl(); | ||
|
||
void Suspend(); | ||
void Resume(); | ||
|
||
void Dispatch(Dispatchable<void(Napi::Env)> func); | ||
|
||
private: | ||
static void DefaultUnhandledExceptionHandler(const std::exception& error); | ||
|
||
template<typename CallableT> | ||
void Append(CallableT callable) | ||
{ | ||
// Manual dispatcher queueing requires a copyable CallableT, we use a shared pointer trick to make a | ||
// copyable callable if necessary. | ||
if constexpr (std::is_copy_constructible<CallableT>::value) | ||
{ | ||
m_dispatcher.queue([this, callable = std::move(callable)]() { | ||
callable(m_env.value()); | ||
}); | ||
} | ||
else | ||
{ | ||
m_dispatcher.queue([this, callablePtr = std::make_shared<CallableT>(std::move(callable))]() { | ||
(*callablePtr)(m_env.value()); | ||
}); | ||
} | ||
} | ||
|
||
// These three methods are the mechanism by which platform- and JavaScript-specific | ||
// code can be "injected" into the execution of the JavaScript thread. These three | ||
// functions are implemented in separate files, thus allowing implementations to be | ||
// mixed and matched by the build system based on the platform and JavaScript engine | ||
// being targeted, without resorting to virtuality. An important nuance of these | ||
// functions is that they are all intended to call each other: RunPlatformTier MUST | ||
// call RunEnvironmentTier, which MUST create the initial Napi::Env and pass it to | ||
// Run. This arrangement allows not only for an arbitrary assemblage of platforms, | ||
// but it also allows us to respect the requirement by certain platforms (notably V8) | ||
// that certain program state be allocated and stored only on the stack. | ||
void RunPlatformTier(); | ||
void RunEnvironmentTier(const char* executablePath = "."); | ||
void Run(Napi::Env); | ||
|
||
// This method is called from Dispatch to allow platform-specific code to add | ||
// extra logic around the invocation of a dispatched callback. | ||
void Execute(Dispatchable<void()> callback); | ||
|
||
std::function<void(const std::exception&)> m_unhandledExceptionHandler{}; | ||
std::optional<Napi::Env> m_env{}; | ||
std::optional<std::scoped_lock<std::mutex>> m_suspensionLock{}; | ||
arcana::cancellation_source m_cancellationSource{}; | ||
arcana::manual_dispatcher<128> m_dispatcher{}; | ||
std::thread m_thread{}; | ||
}; | ||
} |
8 changes: 4 additions & 4 deletions
8
.../AppRuntime/Source/AppRuntime_Android.cpp → ...Runtime/Source/AppRuntimeImpl_Android.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This this be:
: m_impl{std::make_unique<AppRuntimeImpl>(std::move(unhandledExceptionHandler))}