c6f4e6b3c7
AsioExternalThreadEvent::unserialize() may legally throw PHP exceptions. When that happens, construction of PHP exceptions reenters VM, surprised flag is checked and pending C++ exceptions may be thrown. Let's make sure the queue of received events in runUntil() is not lost. Move this responsibility to the AsioExternalThreadEventQueue.
178 linhas
5.3 KiB
C++
178 linhas
5.3 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
|
|
| Copyright (c) 1997-2010 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "hphp/runtime/ext/ext_asio.h"
|
|
#include "hphp/runtime/ext/asio/asio_external_thread_event.h"
|
|
#include "hphp/runtime/ext/asio/asio_external_thread_event_queue.h"
|
|
#include "hphp/runtime/ext/asio/asio_session.h"
|
|
#include "hphp/system/systemlib.h"
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace {
|
|
StaticString s_externalThreadEvent("<external-thread-event>");
|
|
}
|
|
|
|
c_ExternalThreadEventWaitHandle::c_ExternalThreadEventWaitHandle(Class *cb)
|
|
: c_WaitableWaitHandle(cb) {
|
|
}
|
|
|
|
c_ExternalThreadEventWaitHandle::~c_ExternalThreadEventWaitHandle() {
|
|
}
|
|
|
|
void c_ExternalThreadEventWaitHandle::sweep() {
|
|
assert(getState() == STATE_WAITING);
|
|
|
|
if (m_event->cancel()) {
|
|
// canceled; the processing thread will take care of cleanup
|
|
return;
|
|
}
|
|
|
|
// event has finished, but process() was not called yet
|
|
auto queue = AsioSession::Get()->getExternalThreadEventQueue();
|
|
bool done = queue->hasReceived() && queue->abandonAllReceived(this);
|
|
while (!done) {
|
|
queue->receiveSome();
|
|
done = queue->abandonAllReceived(this);
|
|
}
|
|
}
|
|
|
|
void c_ExternalThreadEventWaitHandle::t___construct() {
|
|
Object e(SystemLib::AllocInvalidOperationExceptionObject(
|
|
"ExternalThreadEventWaitHandle can be constructed only from extension"));
|
|
throw e;
|
|
}
|
|
|
|
c_ExternalThreadEventWaitHandle* c_ExternalThreadEventWaitHandle::Create(AsioExternalThreadEvent* event, ObjectData* priv_data) {
|
|
c_ExternalThreadEventWaitHandle* wh = NEWOBJ(c_ExternalThreadEventWaitHandle);
|
|
wh->initialize(event, priv_data);
|
|
return wh;
|
|
}
|
|
|
|
void c_ExternalThreadEventWaitHandle::initialize(AsioExternalThreadEvent* event, ObjectData* priv_data) {
|
|
// this wait handle is owned by existence of unprocessed event
|
|
incRefCount();
|
|
m_event = event;
|
|
m_privData = priv_data;
|
|
|
|
setState(STATE_WAITING);
|
|
if (isInContext()) {
|
|
m_index = getContext()->registerExternalThreadEvent(this);
|
|
}
|
|
}
|
|
|
|
void c_ExternalThreadEventWaitHandle::destroyEvent() {
|
|
// destroy event and its private data
|
|
m_event->release();
|
|
m_event = nullptr;
|
|
m_privData = nullptr;
|
|
|
|
// unregister from sweep()
|
|
unregister();
|
|
|
|
// drop ownership by pending event (see initialize())
|
|
decRefObj(this);
|
|
}
|
|
|
|
void c_ExternalThreadEventWaitHandle::abandon(bool sweeping) {
|
|
assert(getState() == STATE_WAITING);
|
|
assert(getCount() == 1 || sweeping);
|
|
|
|
if (isInContext()) {
|
|
getContext()->unregisterExternalThreadEvent(m_index);
|
|
}
|
|
|
|
// clean up
|
|
destroyEvent();
|
|
}
|
|
|
|
void c_ExternalThreadEventWaitHandle::process() {
|
|
assert(getState() == STATE_WAITING);
|
|
|
|
if (isInContext()) {
|
|
getContext()->unregisterExternalThreadEvent(m_index);
|
|
}
|
|
|
|
// clean up once event is processed
|
|
auto exit_guard = folly::makeGuard([&] { destroyEvent(); });
|
|
|
|
TypedValue result;
|
|
try {
|
|
m_event->unserialize(&result);
|
|
} catch (const Object& exception) {
|
|
setException(exception.get());
|
|
return;
|
|
} catch (...) {
|
|
setException(AsioSession::Get()->getAbruptInterruptException().get());
|
|
throw;
|
|
}
|
|
|
|
assert(tvIsPlausible(&result));
|
|
setResult(&result);
|
|
tvRefcountedDecRefCell(&result);
|
|
}
|
|
|
|
String c_ExternalThreadEventWaitHandle::getName() {
|
|
return s_externalThreadEvent;
|
|
}
|
|
|
|
void c_ExternalThreadEventWaitHandle::enterContext(context_idx_t ctx_idx) {
|
|
assert(AsioSession::Get()->getContext(ctx_idx));
|
|
|
|
// stop before corrupting unioned data
|
|
if (isFinished()) {
|
|
return;
|
|
}
|
|
|
|
// already in the more specific context?
|
|
if (LIKELY(getContextIdx() >= ctx_idx)) {
|
|
return;
|
|
}
|
|
|
|
assert(getState() == STATE_WAITING);
|
|
|
|
if (isInContext()) {
|
|
getContext()->unregisterExternalThreadEvent(m_index);
|
|
}
|
|
|
|
setContextIdx(ctx_idx);
|
|
m_index = getContext()->registerExternalThreadEvent(this);
|
|
}
|
|
|
|
void c_ExternalThreadEventWaitHandle::exitContext(context_idx_t ctx_idx) {
|
|
assert(AsioSession::Get()->getContext(ctx_idx));
|
|
assert(getContextIdx() == ctx_idx);
|
|
assert(getState() == STATE_WAITING);
|
|
|
|
// move us to the parent context
|
|
setContextIdx(getContextIdx() - 1);
|
|
|
|
// re-register if still in a context
|
|
if (isInContext()) {
|
|
getContext()->registerExternalThreadEvent(this);
|
|
}
|
|
|
|
// recursively move all wait handles blocked by us
|
|
for (auto pwh = getFirstParent(); pwh; pwh = pwh->getNextParent()) {
|
|
pwh->exitContextBlocked(ctx_idx);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|