add hooks to ASIO to allow building of dependency graphs in PHP
[hiphop-php.git] / hphp / runtime / ext / asio / waitable_wait_handle.cpp
blob5a7c97448119f9564eac25e986b3ec0b2b5dbdac
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include <runtime/ext/ext_asio.h>
19 #include <runtime/ext/asio/asio_context.h>
20 #include <runtime/ext/asio/asio_session.h>
21 #include <system/lib/systemlib.h>
23 namespace HPHP {
24 ///////////////////////////////////////////////////////////////////////////////
26 c_WaitableWaitHandle::c_WaitableWaitHandle(VM::Class* cb)
27 : c_WaitHandle(cb)
28 , m_creator(AsioSession::Get()->getCurrentWaitHandle())
29 , m_firstParent(nullptr) {
30 setState(STATE_NEW);
31 setContextIdx(AsioSession::Get()->getCurrentContextIdx());
33 // ref creator
34 if (m_creator) {
35 m_creator->incRefCount();
39 c_WaitableWaitHandle::~c_WaitableWaitHandle() {
40 switch (getState()) {
41 case STATE_SUCCEEDED:
42 tvRefcountedDecRefCell(&m_resultOrException);
43 break;
45 case STATE_FAILED:
46 tvDecRefObj(&m_resultOrException);
47 break;
50 // unref creator
51 if (m_creator) {
52 decRefObj(m_creator);
53 m_creator = nullptr;
57 void c_WaitableWaitHandle::t___construct() {
58 throw NotSupportedException(__func__, "WTF? This is an abstract class");
61 int c_WaitableWaitHandle::t_getcontextidx() {
62 return getContextIdx();
65 Object c_WaitableWaitHandle::t_getcreator() {
66 return m_creator;
69 Array c_WaitableWaitHandle::t_getparents() {
70 // no parent data available if finished
71 if (isFinished()) {
72 return Array::Create();
75 Array result = Array::Create();
76 c_BlockableWaitHandle* curr = m_firstParent;
78 while (curr) {
79 result.append(curr);
80 curr = curr->getNextParent();
83 return result;
86 c_BlockableWaitHandle* c_WaitableWaitHandle::addParent(c_BlockableWaitHandle* parent) {
87 c_BlockableWaitHandle* prev = m_firstParent;
88 m_firstParent = parent;
89 return prev;
92 void c_WaitableWaitHandle::setResult(const TypedValue* result) {
93 assert(result);
94 assert(result->m_type != KindOfRef);
96 setState(STATE_SUCCEEDED);
97 tvDupCell(result, &m_resultOrException);
99 // unref creator
100 if (m_creator) {
101 decRefObj(m_creator);
102 m_creator = nullptr;
105 // unblock parents
106 while (m_firstParent) {
107 m_firstParent = m_firstParent->unblock();
111 void c_WaitableWaitHandle::setException(ObjectData* exception) {
112 assert(exception);
113 assert(exception->instanceof(SystemLib::s_ExceptionClass));
115 setState(STATE_FAILED);
116 tvWriteObject(exception, &m_resultOrException);
118 // unref creator
119 if (m_creator) {
120 decRefObj(m_creator);
121 m_creator = nullptr;
124 // unblock parents
125 while (m_firstParent) {
126 m_firstParent = m_firstParent->unblock();
130 // throws on context depth level overflows and cross-context cycles
131 void c_WaitableWaitHandle::join() {
132 AsioSession* session = AsioSession::Get();
134 assert(!isFinished());
135 assert(!session->isInContext() || session->getCurrentContext()->isRunning());
137 if (UNLIKELY(session->hasOnJoinCallback())) {
138 session->onJoin(this);
141 // enter new asio context and set up guard that will exit once we are done
142 session->enterContext();
144 assert(session->isInContext());
145 assert(!session->getCurrentContext()->isRunning());
147 try {
148 // import this wait handle to the newly created context
149 // throws if cross-context cycle found
150 enterContext(session->getCurrentContextIdx());
152 // run queues until we are finished
153 session->getCurrentContext()->runUntil(this);
154 } catch (const Object& exception) {
155 // recover from PHP exceptions; HPHP internal exceptions are deliberately
156 // ignored as there is no easy way to recover from them
157 session->exitContext();
158 throw;
160 session->exitContext();
162 assert(isFinished());
165 c_WaitableWaitHandle* c_WaitableWaitHandle::getChild() {
166 assert(!isFinished());
168 // waitable wait handle does not have any child
169 return nullptr;
172 bool c_WaitableWaitHandle::hasCycle(c_WaitableWaitHandle* start) {
173 assert(start);
175 while (start != this && start && !start->isFinished()) {
176 start = start->getChild();
179 return start == this;
182 ///////////////////////////////////////////////////////////////////////////////