From 3ac5f621cda8c168e5d3e18391fc058f16e065a4 Mon Sep 17 00:00:00 2001 From: Jan Oravec Date: Thu, 13 Jun 2013 12:40:52 -0700 Subject: [PATCH] Recover from C++ exceptions Recover from C++ exceptions so that PSP does not think ASIO is running. Fixes a segfault when ASIO fails on internal invariant violation in runUntil() with m_current == nullptr. --- hphp/runtime/ext/asio/asio_context.cpp | 11 +++++++++-- hphp/runtime/ext/asio/asio_session.cpp | 6 ++++++ hphp/runtime/ext/asio/asio_session.h | 13 +++++++++++++ hphp/runtime/ext/asio/continuation_wait_handle.cpp | 4 ++++ hphp/runtime/ext/asio/waitable_wait_handle.cpp | 20 ++++++-------------- 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/hphp/runtime/ext/asio/asio_context.cpp b/hphp/runtime/ext/asio/asio_context.cpp index b7e7ed19e41..8e93bb50e09 100644 --- a/hphp/runtime/ext/asio/asio_context.cpp +++ b/hphp/runtime/ext/asio/asio_context.cpp @@ -96,6 +96,10 @@ void AsioContext::runUntil(c_WaitableWaitHandle* wait_handle) { auto session = AsioSession::Get(); uint8_t check_ete_counter = 0; + if (!session->hasAbruptInterruptException()) { + session->initAbruptInterruptException(); + } + while (!wait_handle->isFinished()) { // process ready external thread events once per 256 other events // (when 8-bit check_ete_counter overflows) @@ -113,9 +117,12 @@ void AsioContext::runUntil(c_WaitableWaitHandle* wait_handle) { auto current = m_runnableQueue.front(); m_runnableQueue.pop(); m_current = current; + auto exit_guard = folly::makeGuard([&] { + m_current = nullptr; + decRefObj(current); + }); + m_current->run(); - m_current = nullptr; - decRefObj(current); continue; } diff --git a/hphp/runtime/ext/asio/asio_session.cpp b/hphp/runtime/ext/asio/asio_session.cpp index a0021c4f35c..7441d087159 100644 --- a/hphp/runtime/ext/asio/asio_session.cpp +++ b/hphp/runtime/ext/asio/asio_session.cpp @@ -118,6 +118,12 @@ void AsioSession::enqueueExternalThreadEvent(c_ExternalThreadEventWaitHandle* wa } } +void AsioSession::initAbruptInterruptException() { + assert(!hasAbruptInterruptException()); + m_abruptInterruptException = SystemLib::AllocInvalidOperationExceptionObject( + "The request was abruptly interrupted."); +} + void AsioSession::onFailed(CObjRef exception) { if (m_onFailedCallback.get()) { try { diff --git a/hphp/runtime/ext/asio/asio_session.h b/hphp/runtime/ext/asio/asio_session.h index 7d606bab0a2..74bd8bd9136 100644 --- a/hphp/runtime/ext/asio/asio_session.h +++ b/hphp/runtime/ext/asio/asio_session.h @@ -83,6 +83,17 @@ class AsioSession { c_ExternalThreadEventWaitHandle* waitForExternalThreadEvents(); void enqueueExternalThreadEvent(c_ExternalThreadEventWaitHandle* wait_handle); + // abrupt interrupt exception + CObjRef getAbruptInterruptException() { + return m_abruptInterruptException; + } + + bool hasAbruptInterruptException() { + return m_abruptInterruptException.get(); + } + + void initAbruptInterruptException(); + // callback: on failed void setOnFailedCallback(ObjectData* on_failed_callback) { assert(!on_failed_callback || on_failed_callback->instanceof(c_Closure::s_cls)); @@ -162,6 +173,8 @@ class AsioSession { std::mutex m_readyExternalThreadEventsMutex; std::condition_variable m_readyExternalThreadEventsCondition; + Object m_abruptInterruptException; + Object m_onContinuationCreateCallback; Object m_onContinuationYieldCallback; Object m_onContinuationSuccessCallback; diff --git a/hphp/runtime/ext/asio/continuation_wait_handle.cpp b/hphp/runtime/ext/asio/continuation_wait_handle.cpp index bdb795d5b37..3e8de6d513c 100644 --- a/hphp/runtime/ext/asio/continuation_wait_handle.cpp +++ b/hphp/runtime/ext/asio/continuation_wait_handle.cpp @@ -192,6 +192,10 @@ void c_ContinuationWaitHandle::run() { } catch (const Object& exception) { // process exception thrown by generator or blockOn cycle detection markAsFailed(exception); + } catch (...) { + // process C++ exception + markAsFailed(AsioSession::Get()->getAbruptInterruptException()); + throw; } } diff --git a/hphp/runtime/ext/asio/waitable_wait_handle.cpp b/hphp/runtime/ext/asio/waitable_wait_handle.cpp index e40ae8b5f3b..6e7b2f30937 100644 --- a/hphp/runtime/ext/asio/waitable_wait_handle.cpp +++ b/hphp/runtime/ext/asio/waitable_wait_handle.cpp @@ -140,25 +140,17 @@ void c_WaitableWaitHandle::join() { // enter new asio context and set up guard that will exit once we are done session->enterContext(); + auto exit_guard = folly::makeGuard([&] { session->exitContext(); }); assert(session->isInContext()); assert(!session->getCurrentContext()->isRunning()); - try { - // import this wait handle to the newly created context - // throws if cross-context cycle found - enterContext(session->getCurrentContextIdx()); - - // run queues until we are finished - session->getCurrentContext()->runUntil(this); - } catch (const Object& exception) { - // recover from PHP exceptions; HPHP internal exceptions are deliberately - // ignored as there is no easy way to recover from them - session->exitContext(); - throw; - } - session->exitContext(); + // import this wait handle to the newly created context + // throws if cross-context cycle found + enterContext(session->getCurrentContextIdx()); + // run queues until we are finished + session->getCurrentContext()->runUntil(this); assert(isFinished()); } -- 2.11.4.GIT