Arquivos
hhvm/hphp/runtime/ext/asio/waitable_wait_handle.cpp
T
Philippe Ajoux e551d3918f add hooks to ASIO to allow building of dependency graphs in PHP
In order to build a dependency graph of continuation execution and data-fetching in PHP-land, we need a few instrumentation points in the asio_ext HHVM extension. There are 4 additions required:
   1. Callback when a continuation finishes successfully.
   2. Callback when a continuation blocks on a wait_handle.
   3. Get array of WaitHandles a GenArrayWaitHandle is waiting on.
   4. Get WaitHandle that the SetResultToRefWaitHandle is waiting on.
I don't think this should really affect performance, as in the normal case, nothing has changed, but you never know... I'm also not sure who should be reviewing this, so I've just added @jan for now. If you could pile other people on, that would be cool.

sandcastle appears to be broken.
2013-05-09 11:33:20 -07:00

184 linhas
5.0 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- 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 <runtime/ext/ext_asio.h>
#include <runtime/ext/asio/asio_context.h>
#include <runtime/ext/asio/asio_session.h>
#include <system/lib/systemlib.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
c_WaitableWaitHandle::c_WaitableWaitHandle(VM::Class* cb)
: c_WaitHandle(cb)
, m_creator(AsioSession::Get()->getCurrentWaitHandle())
, m_firstParent(nullptr) {
setState(STATE_NEW);
setContextIdx(AsioSession::Get()->getCurrentContextIdx());
// ref creator
if (m_creator) {
m_creator->incRefCount();
}
}
c_WaitableWaitHandle::~c_WaitableWaitHandle() {
switch (getState()) {
case STATE_SUCCEEDED:
tvRefcountedDecRefCell(&m_resultOrException);
break;
case STATE_FAILED:
tvDecRefObj(&m_resultOrException);
break;
}
// unref creator
if (m_creator) {
decRefObj(m_creator);
m_creator = nullptr;
}
}
void c_WaitableWaitHandle::t___construct() {
throw NotSupportedException(__func__, "WTF? This is an abstract class");
}
int c_WaitableWaitHandle::t_getcontextidx() {
return getContextIdx();
}
Object c_WaitableWaitHandle::t_getcreator() {
return m_creator;
}
Array c_WaitableWaitHandle::t_getparents() {
// no parent data available if finished
if (isFinished()) {
return Array::Create();
}
Array result = Array::Create();
c_BlockableWaitHandle* curr = m_firstParent;
while (curr) {
result.append(curr);
curr = curr->getNextParent();
}
return result;
}
c_BlockableWaitHandle* c_WaitableWaitHandle::addParent(c_BlockableWaitHandle* parent) {
c_BlockableWaitHandle* prev = m_firstParent;
m_firstParent = parent;
return prev;
}
void c_WaitableWaitHandle::setResult(const TypedValue* result) {
assert(result);
assert(result->m_type != KindOfRef);
setState(STATE_SUCCEEDED);
tvDupCell(result, &m_resultOrException);
// unref creator
if (m_creator) {
decRefObj(m_creator);
m_creator = nullptr;
}
// unblock parents
while (m_firstParent) {
m_firstParent = m_firstParent->unblock();
}
}
void c_WaitableWaitHandle::setException(ObjectData* exception) {
assert(exception);
assert(exception->instanceof(SystemLib::s_ExceptionClass));
setState(STATE_FAILED);
tvWriteObject(exception, &m_resultOrException);
// unref creator
if (m_creator) {
decRefObj(m_creator);
m_creator = nullptr;
}
// unblock parents
while (m_firstParent) {
m_firstParent = m_firstParent->unblock();
}
}
// throws on context depth level overflows and cross-context cycles
void c_WaitableWaitHandle::join() {
AsioSession* session = AsioSession::Get();
assert(!isFinished());
assert(!session->isInContext() || session->getCurrentContext()->isRunning());
if (UNLIKELY(session->hasOnJoinCallback())) {
session->onJoin(this);
}
// enter new asio context and set up guard that will exit once we are done
session->enterContext();
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();
assert(isFinished());
}
c_WaitableWaitHandle* c_WaitableWaitHandle::getChild() {
assert(!isFinished());
// waitable wait handle does not have any child
return nullptr;
}
bool c_WaitableWaitHandle::hasCycle(c_WaitableWaitHandle* start) {
assert(start);
while (start != this && start && !start->isFinished()) {
start = start->getChild();
}
return start == this;
}
///////////////////////////////////////////////////////////////////////////////
}