Arquivos
hhvm/hphp/runtime/ext/asio/external_thread_event_wait_handle.cpp
T
Jan Oravec 797a6822d0 Move external thread event queue logic from AsioSession to AsioExternalThreadEventQueue
External thread event queueing logic is big enough that it deserves its
own class. Move the code and rename members to better reflect the
underlying operations. Implementation is otherwise unchanged.
2013-06-17 13:02:11 -07:00

183 linhas
5.4 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 = false;
do {
auto ete_wh = queue->consumeMulti();
while (ete_wh) {
done |= ete_wh == this;
auto next_wh = ete_wh->getNextToProcess();
ete_wh->abandon(true);
ete_wh = next_wh;
}
} while (!done);
}
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);
}
}
///////////////////////////////////////////////////////////////////////////////
}