Recover from C++ exceptions
[hiphop-php.git] / hphp / runtime / ext / asio / asio_context.cpp
blob8e93bb50e09b05e855ce067a83d109249e711732
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/ext/asio/asio_context.h"
19 #include "hphp/runtime/ext/ext_asio.h"
20 #include "hphp/runtime/ext/asio/asio_session.h"
21 #include "hphp/system/lib/systemlib.h"
23 namespace HPHP {
24 ///////////////////////////////////////////////////////////////////////////////
26 namespace {
27 template<class TWaitHandle>
28 void exitContextQueue(context_idx_t ctx_idx, smart::queue<TWaitHandle*> &queue) {
29 while (!queue.empty()) {
30 auto wait_handle = queue.front();
31 queue.pop();
32 wait_handle->exitContext(ctx_idx);
33 decRefObj(wait_handle);
38 void AsioContext::exit(context_idx_t ctx_idx) {
39 assert(AsioSession::Get()->getContext(ctx_idx) == this);
40 assert(!m_current);
42 exitContextQueue(ctx_idx, m_runnableQueue);
44 for (auto it : m_priorityQueueDefault) {
45 exitContextQueue(ctx_idx, it.second);
48 for (auto it : m_priorityQueueNoPendingIO) {
49 exitContextQueue(ctx_idx, it.second);
52 while (!m_externalThreadEvents.empty()) {
53 auto ete_wh = m_externalThreadEvents.back();
54 m_externalThreadEvents.pop_back();
55 ete_wh->exitContext(ctx_idx);
59 void AsioContext::schedule(c_ContinuationWaitHandle* wait_handle) {
60 m_runnableQueue.push(wait_handle);
61 wait_handle->incRefCount();
64 void AsioContext::schedule(c_RescheduleWaitHandle* wait_handle, uint32_t queue, uint32_t priority) {
65 assert(queue == QUEUE_DEFAULT || queue == QUEUE_NO_PENDING_IO);
67 reschedule_priority_queue_t& dst_queue =
68 (queue == QUEUE_DEFAULT)
69 ? m_priorityQueueDefault
70 : m_priorityQueueNoPendingIO;
72 // creates a new per-prio queue if necessary
73 dst_queue[priority].push(wait_handle);
74 wait_handle->incRefCount();
77 uint32_t AsioContext::registerExternalThreadEvent(c_ExternalThreadEventWaitHandle* wait_handle) {
78 m_externalThreadEvents.push_back(wait_handle);
79 return m_externalThreadEvents.size() - 1;
82 void AsioContext::unregisterExternalThreadEvent(uint32_t ete_idx) {
83 assert(ete_idx < m_externalThreadEvents.size());
84 if (ete_idx != m_externalThreadEvents.size() - 1) {
85 m_externalThreadEvents[ete_idx] = m_externalThreadEvents.back();
86 m_externalThreadEvents[ete_idx]->setIndex(ete_idx);
88 m_externalThreadEvents.pop_back();
91 void AsioContext::runUntil(c_WaitableWaitHandle* wait_handle) {
92 assert(!m_current);
93 assert(wait_handle);
94 assert(wait_handle->getContext() == this);
96 auto session = AsioSession::Get();
97 uint8_t check_ete_counter = 0;
99 if (!session->hasAbruptInterruptException()) {
100 session->initAbruptInterruptException();
103 while (!wait_handle->isFinished()) {
104 // process ready external thread events once per 256 other events
105 // (when 8-bit check_ete_counter overflows)
106 if (!++check_ete_counter) {
107 auto ete_wh = session->getReadyExternalThreadEvents();
108 while (ete_wh) {
109 auto next_wh = ete_wh->getNextToProcess();
110 ete_wh->process();
111 ete_wh = next_wh;
115 // run queue of ready continuations once
116 if (!m_runnableQueue.empty()) {
117 auto current = m_runnableQueue.front();
118 m_runnableQueue.pop();
119 m_current = current;
120 auto exit_guard = folly::makeGuard([&] {
121 m_current = nullptr;
122 decRefObj(current);
125 m_current->run();
126 continue;
129 // run default priority queue once
130 if (runSingle(m_priorityQueueDefault)) {
131 continue;
134 // pending external thread events? wait for at least one to become ready
135 if (!m_externalThreadEvents.empty()) {
136 // all your wait time are belong to us
137 auto ete_wh = session->waitForExternalThreadEvents();
138 while (ete_wh) {
139 auto next_wh = ete_wh->getNextToProcess();
140 ete_wh->process();
141 ete_wh = next_wh;
143 continue;
146 // run no-pending-io priority queue once
147 if (runSingle(m_priorityQueueNoPendingIO)) {
148 continue;
151 // What? The wait handle did not finish? We know it is part of the current
152 // context and since there is nothing else to run, it cannot be in RUNNING
153 // or SCHEDULED state. So it must be BLOCKED on something. Apparently, the
154 // same logic can be used recursively on the something, so there is an
155 // infinite chain of blocked wait handles. But our memory is not infinite.
156 // What could it possibly mean? I think we are in a deep sh^H^Hcycle.
157 // But we can't, the cycles are detected and avoided at blockOn() time.
158 // So, looks like it's not cycle, but the word I started typing first.
159 assert(false);
160 throw FatalErrorException(
161 "Invariant violation: queues are empty, but wait handle did not finish");
166 * Try to run single RescheduleWaitHandle from the queue.
168 bool AsioContext::runSingle(reschedule_priority_queue_t& queue) {
169 if (queue.empty()) {
170 // nothing to run
171 return false;
174 auto top_queue_iter = queue.begin();
175 auto& top_queue = top_queue_iter->second;
176 auto reschedule_wait_handle = top_queue.front();
177 top_queue.pop();
178 reschedule_wait_handle->run();
179 decRefObj(reschedule_wait_handle);
181 if (top_queue.empty()) {
182 queue.erase(top_queue_iter);
185 return true;
188 ///////////////////////////////////////////////////////////////////////////////