@@ -108,15 +108,6 @@ enum class PropCacheID {
108108 _COUNT
109109};
110110
111- #ifdef HERMESVM_TIMELIMIT
112- // / A std::runtime_error wrapper class for execution timeout.
113- class JSTimeoutError : public std ::runtime_error {
114- public:
115- JSTimeoutError (const std::string &what_arg) : std::runtime_error(what_arg) {}
116- JSTimeoutError (const char *what_arg) : std::runtime_error(what_arg) {}
117- };
118- #endif
119-
120111// / The Runtime encapsulates the entire context of a VM. Multiple instances can
121112// / exist and are completely independent from each other.
122113class Runtime : public HandleRootOwner ,
@@ -468,14 +459,22 @@ class Runtime : public HandleRootOwner,
468459 return commonStorage_.get ();
469460 }
470461
471- #if defined(HERMES_ENABLE_DEBUGGER) || defined(HERMESVM_TIMELIMIT)
462+ #if defined(HERMES_ENABLE_DEBUGGER)
472463 // / Request the interpreter loop to take an asynchronous break at a convenient
473- // / point. This may be called from any thread, or a signal handler.
474- void triggerAsyncBreak () {
475- asyncBreakRequestFlag_.store (1 , std::memory_order_relaxed);
464+ // / point due to debugger UI request. This may be called from any thread, or a
465+ // / signal handler.
466+ void triggerDebuggerAsyncBreak () {
467+ triggerAsyncBreak (AsyncBreakReasonBits::Debugger);
476468 }
477469#endif
478470
471+ // / Request the interpreter loop to take an asynchronous break at a convenient
472+ // / point due to previous registered timeout. This may be called from any
473+ // / thread, or a signal handler.
474+ void triggerTimeoutAsyncBreak () {
475+ triggerAsyncBreak (AsyncBreakReasonBits::Timeout);
476+ }
477+
479478 // / Register \p callback which will be called
480479 // / during runtime destruction.
481480 void registerDestructionCallback (DestructionCallback callback) {
@@ -560,6 +559,12 @@ class Runtime : public HandleRootOwner,
560559 // / Raise an error for the quit function. This error is not catchable.
561560 ExecutionStatus raiseQuitError ();
562561
562+ // / Raise an error for execution timeout. This error is not catchable.
563+ ExecutionStatus raiseTimeoutError ();
564+
565+ // / Utility function to raise a catchable JS error with \p errMessage.
566+ ExecutionStatus raiseUncatchableError (llvm::StringRef errMessage);
567+
563568 // / Interpret the current function until it returns or throws and return
564569 // / CallResult<HermesValue> or the thrown object in 'thrownObject'.
565570 CallResult<HermesValue> interpretFunction (CodeBlock *newCodeBlock);
@@ -989,44 +994,66 @@ class Runtime : public HandleRootOwner,
989994 // / A list of callbacks to call before runtime destruction.
990995 std::vector<DestructionCallback> destructionCallbacks_;
991996
992- #if defined(HERMES_ENABLE_DEBUGGER) || defined(HERMESVM_TIMELIMIT)
993- // / An atomic boolean set when an async pause is requested.
997+ // / Bit flags for async break request reasons.
998+ enum class AsyncBreakReasonBits : uint8_t {
999+ Debugger = 0x1 ,
1000+ Timeout = 0x2 ,
1001+ };
1002+
1003+ // / An atomic flag set when an async pause is requested.
1004+ // / It is a bits flag with each bit reserved for different clients
1005+ // / defined by AsyncBreakReasonBits.
9941006 // / This may be manipulated from multiple threads.
9951007 std::atomic<uint8_t > asyncBreakRequestFlag_{0 };
9961008
997- // / \return zero if no async pause was requsted, nonzero if an async pause was
998- // / requested. If nonzero is returned, the flag is reset to 0.
999- uint8_t testAndClearAsyncBreakRequest () {
1009+ #if defined(HERMES_ENABLE_DEBUGGER)
1010+ // / \return zero if no debugger async pause was requested, nonzero if an async
1011+ // / pause was requested. If nonzero is returned, the flag is reset to 0.
1012+ bool testAndClearDebuggerAsyncBreakRequest () {
1013+ return testAndClearAsyncBreakRequest (AsyncBreakReasonBits::Debugger);
1014+ }
1015+
1016+ Debugger debugger_{this };
1017+ #endif
1018+
1019+ // / \return whether any async break is requested or not.
1020+ bool hasAsyncBreak () const {
1021+ return asyncBreakRequestFlag_.load (std::memory_order_relaxed) != 0 ;
1022+ }
1023+
1024+ // / \return whether async break was requested or not for \p reasonBit. Clear
1025+ // / \p reasonBit request bit afterward.
1026+ bool testAndClearAsyncBreakRequest (AsyncBreakReasonBits reasonBit) {
1027+ // / Note that while the triggerTimeoutAsyncBreak() function may be called
1028+ // / from any thread, this one may only be called from within the Interpreter
1029+ // / loop.
10001030 uint8_t flag = asyncBreakRequestFlag_.load (std::memory_order_relaxed);
1001- if (LLVM_UNLIKELY (flag)) {
1002- // / Note that while the triggerAsyncBreak() function may be called from
1003- // / any thread, this one may only be called from within the Interpreter
1004- // / loop; thus there is no need for a compare-and-swap. We might race with
1005- // / setting the flag again, but currently we do not allow two callers who
1006- // / both see a true return from a single flag set.
1007- asyncBreakRequestFlag_.store (0 , std::memory_order_relaxed);
1031+ if (LLVM_LIKELY ((flag & (uint8_t )reasonBit) == 0 )) {
1032+ // Fast path.
1033+ return false ;
10081034 }
1009- return flag;
1035+ // Clear the flag using CAS.
1036+ uint8_t oldFlag = asyncBreakRequestFlag_.fetch_and (
1037+ ~(uint8_t )reasonBit, std::memory_order_relaxed);
1038+ assert (oldFlag != 0 && " Why is oldFlag zero?" );
1039+ (void )oldFlag;
1040+ return true ;
10101041 }
1011- #endif
10121042
1013- #ifdef HERMESVM_TIMELIMIT
1014- void notifyTimeout (const Inst *ip) {
1015- char detailBuffer[400 ];
1016- snprintf (
1017- detailBuffer,
1018- sizeof (detailBuffer),
1019- // todo: include time out value.
1020- " Javascript execution has timeout." );
1021- throw JSTimeoutError (
1022- std::string (detailBuffer) + " \n call stack:\n " +
1023- getCallStackNoAlloc (ip));
1043+ // / \return whether timeout async break was requsted or not. Clear the
1044+ // / timeout request bit afterward.
1045+ bool testAndClearTimeoutAsyncBreakRequest () {
1046+ return testAndClearAsyncBreakRequest (AsyncBreakReasonBits::Timeout);
10241047 }
1025- #endif
10261048
1027- #ifdef HERMES_ENABLE_DEBUGGER
1028- Debugger debugger_{this };
1029- #endif
1049+ // / Request the interpreter loop to take an asynchronous break
1050+ // / at a convenient point.
1051+ void triggerAsyncBreak (AsyncBreakReasonBits reason) {
1052+ asyncBreakRequestFlag_.fetch_or ((uint8_t )reason, std::memory_order_relaxed);
1053+ }
1054+
1055+ // / Notify runtime execution has timeout.
1056+ ExecutionStatus notifyTimeout ();
10301057
10311058 // / Holds references to persistent BC providers for the lifetime of the
10321059 // / Runtime. This is needed because the identifier table may contain pointers
0 commit comments