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 AsioContext::runUntil(c_WaitableWaitHandle* wait_handle) { assert(wait_handle); assert(wait_handle->getContext() == this); auto session = AsioSession::Get(); auto ete_queue = session->getExternalThreadEventQueue(); auto& sleep_queue = session->getSleepEventQueue(); if (!session->hasAbruptInterruptException()) { session->initAbruptInterruptException(); } while (!wait_handle->isFinished()) { // Run queue of ready async functions once. if (!m_runnableQueue.empty()) { auto current = m_runnableQueue.back(); m_runnableQueue.pop_back(); current->resume(); continue; } // Process all sleep handles that have completed their sleep. if (session->processSleepEvents()) { continue; } // Process all external thread events that have completed their operation. // Queue may contain received unprocessed events from failed runUntil(). if (UNLIKELY(ete_queue->hasReceived()) || ete_queue->tryReceiveSome()) { ete_queue->processAllReceived(); continue; } // Run default priority queue once. if (runSingle(m_priorityQueueDefault)) { continue; } // Wait for pending external thread events... if (!m_externalThreadEvents.empty()) { // ...but only until the next sleeper (from any context) finishes. AsioSession::TimePoint waketime; if (sleep_queue.empty()) { waketime = AsioSession::getLatestWakeTime(); } else { waketime = sleep_queue.top()->getWakeTime(); } // Wait if necessary. if (LIKELY(!ete_queue->hasReceived())) { onIOWaitEnter(session); ete_queue->receiveSomeUntil(waketime); onIOWaitExit(session); } if (ete_queue->hasReceived()) { // Either we didn't have to wait, or we waited but no sleeper timed us // out, so just handle the ETEs. ete_queue->processAllReceived(); } else { // No received events means the next-to-wake sleeper timed us out. session->processSleepEvents(); } continue; } // If we're here, then the only things left are sleepers. Wait for one to // be ready (in any context). if (!m_sleepEvents.empty()) { onIOWaitEnter(session); std::this_thread::sleep_until(sleep_queue.top()->getWakeTime()); onIOWaitExit(session); session->processSleepEvents(); continue; } // Run no-pending-io priority queue once. if (runSingle(m_priorityQueueNoPendingIO)) { continue; } // What? The wait handle did not finish? We know it is part of the current // context and since there is nothing else to run, it cannot be in RUNNING // or SCHEDULED state. So it must be BLOCKED on something. Apparently, the // same logic can be used recursively on the something, so there is an // infinite chain of blocked wait handles. But our memory is not infinite. // What could it possibly mean? I think we are in a deep sh^H^Hcycle. // But we can't, the cycles are detected and avoided at blockOn() time. // So, looks like it's not cycle, but the word I started typing first. assert(false); throw FatalErrorException( "Invariant violation: queues are empty, but wait handle did not finish"); } }
void AsioContext::runUntil(c_WaitableWaitHandle* wait_handle) { assert(!m_current); assert(wait_handle); assert(wait_handle->getContext() == this); auto session = AsioSession::Get(); uint8_t check_ete_counter = 0; if (!session->hasAbruptInterruptException()) { session->initAbruptInterruptException(); } while (!wait_handle->isFinished()) { // process ready external thread events once per 256 other events // (when 8-bit check_ete_counter overflows) if (!++check_ete_counter) { // queue may contain received unprocessed events from failed runUntil() auto queue = session->getExternalThreadEventQueue(); if (UNLIKELY(queue->hasReceived()) || queue->tryReceiveSome()) { queue->processAllReceived(); } } // run queue of ready continuations once if (!m_runnableQueue.empty()) { auto current = m_runnableQueue.front(); m_runnableQueue.pop(); m_current = current; auto exit_guard = folly::makeGuard([&] { m_current = nullptr; decRefObj(current); }); m_current->run(); continue; } // run default priority queue once if (runSingle(m_priorityQueueDefault)) { continue; } // pending external thread events? wait for at least one to become ready if (!m_externalThreadEvents.empty()) { // queue may contain received unprocessed events from failed runUntil() auto queue = session->getExternalThreadEventQueue(); if (LIKELY(!queue->hasReceived())) { // all your wait time are belong to us queue->receiveSome(); } queue->processAllReceived(); continue; } // run no-pending-io priority queue once if (runSingle(m_priorityQueueNoPendingIO)) { continue; } // What? The wait handle did not finish? We know it is part of the current // context and since there is nothing else to run, it cannot be in RUNNING // or SCHEDULED state. So it must be BLOCKED on something. Apparently, the // same logic can be used recursively on the something, so there is an // infinite chain of blocked wait handles. But our memory is not infinite. // What could it possibly mean? I think we are in a deep sh^H^Hcycle. // But we can't, the cycles are detected and avoided at blockOn() time. // So, looks like it's not cycle, but the word I started typing first. assert(false); throw FatalErrorException( "Invariant violation: queues are empty, but wait handle did not finish"); } }
// observation void NewConditionController::visit_observation(observation *r) { static const bool verbose = false; if (verbose) cout << "REV:" << r->receivers->size() << ": "; VecInt hasReceived(proc->nRoles, 0); // flag gets set to true as soon as at least one message is set to this role int i = 0; for (recipients::const_iterator itr = r->receivers->begin(); itr != r->receivers->end(); ++itr, ++i) { //cout << (*itr)->receivers->size() << " "; FunctionKey fk = getGroundedProp((*itr)->prop, this->args); if (verbose) cout << asIntString(fk) << " "; for (parameter_symbol_list::const_iterator pitr = (*itr)->receivers->begin(); pitr != (*itr)->receivers->end(); ++pitr) { //cout << ":"; const string& name = (*pitr)->getName(); if ((*pitr)->isConstant()) { if (name == "all") { if (verbose) cout << "msg " << i << " to all; "; hasReceived = VecInt(proc->nRoles, 1); for (unsigned k = 0; k < proc->nRoles; k++) { ws.obsListByPlyr[k].push_back(fk); } break; } else if (name != "others") { // Message is targeted to a player mentioned as a constant // Note that this is independent of args and could be computed in advance if (verbose) cout << "Msg " << i << " to " << (-1 - r->recipientMapper[name]) << " (" << proc->invObjectTbl["role"][-1 - r->recipientMapper[name]] << ");"; hasReceived[-1 - r->recipientMapper[name]] = 1; ws.obsListByPlyr[-1 - r->recipientMapper[name]].push_back(fk); } } else { // message is targeted to a player that is bound to a variable if (verbose) cout << "msg " << i << " to " << args[r->recipientMapper[name]] << " (" << proc->invObjectTbl["role"][args[r->recipientMapper[name]]] << ");"; hasReceived[args[r->recipientMapper[name]]] = 1; ws.obsListByPlyr[args[r->recipientMapper[name]]].push_back(fk); } //if (name != "all" && name != "others") cout << "Mapping " << name << " to " << r->recipientMapper[name] << "\n"; } } i = 0; for (recipients::const_iterator itr = r->receivers->begin(); itr != r->receivers->end(); ++itr, ++i) { //cout << (*itr)->receivers->size() << " "; if ((*itr)->receivers->size() == 1 && (*(*itr)->receivers->begin())->getName() == "others") { FunctionKey fk = getGroundedProp((*itr)->prop, this->args); if (verbose) cout << asIntString(fk) << " "; if (verbose) cout << "Sending message to OTHERS: "; for (unsigned j = 1; j < hasReceived.size(); j++) { if (!hasReceived[j]) { if (verbose) cout << j << " "; ws.obsListByPlyr[j].push_back(fk); } } //cout << "\n"; } } //cout << "\n"; }