void EventDispatcherBase<TDerived>::runToCompletion(bool changedConfiguration) { // We are in microstepping mode: follow all eventless transitions. while (1) { clearTransientStateFlags(); selectTransitions(true, event_type()); if (!m_enabledTransitions) break; changedConfiguration |= microstep(event_type()); clearEnabledTransitionsSet(); } // Synchronize the visible state active flag with the internal // state active flag. for (auto iter = derived().begin(); iter != derived().end(); ++iter) iter->m_visibleActive = (iter->m_flags & state_type::Active) != 0; // Call the invoke() methods of all currently active states. for (auto iter = derived().begin(); iter != derived().end(); ++iter) { if (iter->m_flags & state_type::StartInvoke) { iter->enterInvoke(); iter->m_flags &= ~state_type::StartInvoke; iter->m_flags |= state_type::Invoked; } } // If we followed at least one transition, which was not target-less, // invoke the configuration change callback. if (changedConfiguration) { ++m_numConfigurationChanges; derived().invokeConfigurationChangeCallback(); } }
void InterpreterDraft6::mainEventLoop() { monIter_t monIter; while(_running) { NodeSet<std::string> enabledTransitions; _stable = false; // Here we handle eventless transitions and transitions // triggered by internal events until machine is stable while(_running && !_stable) { #if VERBOSE std::cout << "Configuration: "; for (int i = 0; i < _configuration.size(); i++) { std::cout << ATTR(_configuration[i], "id") << ", "; } std::cout << std::endl; #endif enabledTransitions = selectEventlessTransitions(); if (enabledTransitions.size() == 0) { if (_internalQueue.size() == 0) { _stable = true; } else { _currEvent = _internalQueue.front(); _internalQueue.pop_front(); #if VERBOSE std::cout << "Received internal event " << _currEvent.name << std::endl; #endif // --- MONITOR: beforeProcessingEvent ------------------------------ for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); } USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) } if (_dataModel) _dataModel.setEvent(_currEvent); enabledTransitions = selectTransitions(_currEvent.name); } } if (!enabledTransitions.empty()) { // test 403b enabledTransitions.to_document_order(); microstep(enabledTransitions); } } for (unsigned int i = 0; i < _statesToInvoke.size(); i++) { NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]); for (unsigned int j = 0; j < invokes.size(); j++) { if (!HAS_ATTR(invokes[j], "persist") || !DOMUtils::attributeIsTrue(ATTR(invokes[j], "persist"))) { invoke(invokes[j]); } } } _statesToInvoke = NodeSet<std::string>(); if (!_internalQueue.empty()) continue; // assume that we have a legal configuration as soon as the internal queue is empty assert(hasLegalConfiguration()); monIter = _monitors.begin(); // --- MONITOR: onStableConfiguration ------------------------------ for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->onStableConfiguration(shared_from_this()); } USCXML_MONITOR_CATCH_BLOCK(onStableConfiguration) } _mutex.unlock(); // whenever we have a stable configuration, run the mainThread hooks with 200fps while(_externalQueue.isEmpty() && _thread == NULL) { runOnMainThread(200); } _mutex.lock(); while(_externalQueue.isEmpty()) { _condVar.wait(_mutex); } _currEvent = _externalQueue.pop(); #if VERBOSE std::cout << "Received externalEvent event " << _currEvent.name << std::endl; if (_running && _currEvent.name == "unblock.and.die") { std::cout << "Still running " << this << std::endl; } else { std::cout << "Aborting " << this << std::endl; } #endif _currEvent.eventType = Event::EXTERNAL; // make sure it is set to external if (!_running) goto EXIT_INTERPRETER; // --- MONITOR: beforeProcessingEvent ------------------------------ for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); } USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) } if (_dataModel && iequals(_currEvent.name, "cancel.invoke." + _sessionId)) break; if (_dataModel) { try { _dataModel.setEvent(_currEvent); } catch (Event e) { LOG(ERROR) << "Syntax error while setting external event:" << std::endl << e << std::endl << _currEvent; } } for (std::map<std::string, Invoker>::iterator invokeIter = _invokers.begin(); invokeIter != _invokers.end(); invokeIter++) { if (iequals(invokeIter->first, _currEvent.invokeid)) { Arabica::XPath::NodeSet<std::string> finalizes = filterChildElements(_nsInfo.xmlNSPrefix + "finalize", invokeIter->second.getElement()); for (int k = 0; k < finalizes.size(); k++) { Element<std::string> finalizeElem = Element<std::string>(finalizes[k]); executeContent(finalizeElem); } } if (HAS_ATTR(invokeIter->second.getElement(), "autoforward") && DOMUtils::attributeIsTrue(ATTR(invokeIter->second.getElement(), "autoforward"))) { try { // do not autoforward to invokers that send to #_parent from the SCXML IO Processor! // Yes do so, see test229! // if (!boost::equals(_currEvent.getOriginType(), "http://www.w3.org/TR/scxml/#SCXMLEventProcessor")) invokeIter->second.send(_currEvent); } catch(...) { LOG(ERROR) << "Exception caught while sending event to invoker " << invokeIter->first; } } } enabledTransitions = selectTransitions(_currEvent.name); if (!enabledTransitions.empty()) { // test 403b enabledTransitions.to_document_order(); microstep(enabledTransitions); } } EXIT_INTERPRETER: // --- MONITOR: beforeCompletion ------------------------------ for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->beforeCompletion(shared_from_this()); } USCXML_MONITOR_CATCH_BLOCK(beforeCompletion) } exitInterpreter(); if (_sendQueue) { std::map<std::string, std::pair<InterpreterImpl*, SendRequest> >::iterator sendIter = _sendIds.begin(); while(sendIter != _sendIds.end()) { _sendQueue->cancelEvent(sendIter->first); sendIter++; } } // --- MONITOR: afterCompletion ------------------------------ for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->afterCompletion(shared_from_this()); } USCXML_MONITOR_CATCH_BLOCK(afterCompletion) } }
// process transitions until we are in a stable configuration again void InterpreterDraft6::stabilize() { monIter_t monIter; NodeSet<std::string> enabledTransitions; _stable = false; if (_configuration.size() == 0) { // goto initial configuration NodeSet<std::string> initialTransitions = getDocumentInitialTransitions(); assert(initialTransitions.size() > 0); enterStates(initialTransitions); } do { // process microsteps for enabled transitions until there are no more left enabledTransitions = selectEventlessTransitions(); if (enabledTransitions.size() == 0) { if (_internalQueue.size() == 0) { _stable = true; } else { _currEvent = _internalQueue.front(); _internalQueue.pop_front(); #if VERBOSE std::cout << "Received internal event " << _currEvent.name << std::endl; #endif // --- MONITOR: beforeProcessingEvent ------------------------------ for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); } USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) } if (_dataModel) _dataModel.setEvent(_currEvent); enabledTransitions = selectTransitions(_currEvent.name); } } if (!enabledTransitions.empty()) { // test 403b enabledTransitions.to_document_order(); microstep(enabledTransitions); } } while(!_internalQueue.empty() || !_stable); monIter = _monitors.begin(); // --- MONITOR: onStableConfiguration ------------------------------ for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->onStableConfiguration(shared_from_this()); } USCXML_MONITOR_CATCH_BLOCK(onStableConfiguration) } // when we reach a stable configuration, invoke for (unsigned int i = 0; i < _statesToInvoke.size(); i++) { NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _statesToInvoke[i]); for (unsigned int j = 0; j < invokes.size(); j++) { if (!HAS_ATTR(invokes[j], "persist") || !DOMUtils::attributeIsTrue(ATTR(invokes[j], "persist"))) { invoke(invokes[j]); } } } _statesToInvoke = NodeSet<std::string>(); }
// a macrostep InterpreterState InterpreterDraft6::step(bool blocking) { try { monIter_t monIter; NodeSet<std::string> enabledTransitions; // setup document and interpreter if (!_isInitialized) init(); // if we failed return false if (!_isInitialized) return INIT_FAILED; // run initial transitions if (!_stable) { stabilize(); // we might only need a single step if (!_running) goto EXIT_INTERPRETER; return INITIALIZED; } if (!_running) return FINISHED; // read an external event and react if (blocking) { // wait until an event becomes available while(_externalQueue.isEmpty()) { _condVar.wait(_mutex); } } else { // return immediately if external queue is empty if (_externalQueue.isEmpty()) return NOTHING_TODO; } _currEvent = _externalQueue.pop(); #if VERBOSE std::cout << "Received externalEvent event " << _currEvent.name << std::endl; if (_running && _currEvent.name == "unblock.and.die") { std::cout << "Still running " << this << std::endl; } else { std::cout << "Aborting " << this << std::endl; } #endif _currEvent.eventType = Event::EXTERNAL; // make sure it is set to external // when we were blocking on destructor invocation if (!_running) { goto EXIT_INTERPRETER; return INTERRUPTED; } // --- MONITOR: beforeProcessingEvent ------------------------------ for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->beforeProcessingEvent(shared_from_this(), _currEvent); } USCXML_MONITOR_CATCH_BLOCK(beforeProcessingEvent) } if (iequals(_currEvent.name, "cancel.invoke." + _sessionId)) return INTERRUPTED; try { _dataModel.setEvent(_currEvent); } catch (Event e) { LOG(ERROR) << "Syntax error while setting external event:" << std::endl << e << std::endl << _currEvent; } for (std::map<std::string, Invoker>::iterator invokeIter = _invokers.begin(); invokeIter != _invokers.end(); invokeIter++) { if (iequals(invokeIter->first, _currEvent.invokeid)) { Arabica::XPath::NodeSet<std::string> finalizes = filterChildElements(_nsInfo.xmlNSPrefix + "finalize", invokeIter->second.getElement()); for (int k = 0; k < finalizes.size(); k++) { Element<std::string> finalizeElem = Element<std::string>(finalizes[k]); executeContent(finalizeElem); } } if (HAS_ATTR(invokeIter->second.getElement(), "autoforward") && DOMUtils::attributeIsTrue(ATTR(invokeIter->second.getElement(), "autoforward"))) { try { // do not autoforward to invokers that send to #_parent from the SCXML IO Processor! // Yes do so, see test229! // if (!boost::equals(_currEvent.getOriginType(), "http://www.w3.org/TR/scxml/#SCXMLEventProcessor")) invokeIter->second.send(_currEvent); } catch(...) { LOG(ERROR) << "Exception caught while sending event to invoker " << invokeIter->first; } } } // run internal processing until we reach a stable configuration again enabledTransitions = selectTransitions(_currEvent.name); if (!enabledTransitions.empty()) { // test 403b enabledTransitions.to_document_order(); microstep(enabledTransitions); } stabilize(); return PROCESSED; EXIT_INTERPRETER: if (!_running) { // --- MONITOR: beforeCompletion ------------------------------ for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->beforeCompletion(shared_from_this()); } USCXML_MONITOR_CATCH_BLOCK(beforeCompletion) } exitInterpreter(); if (_sendQueue) { std::map<std::string, std::pair<InterpreterImpl*, SendRequest> >::iterator sendIter = _sendIds.begin(); while(sendIter != _sendIds.end()) { _sendQueue->cancelEvent(sendIter->first); sendIter++; } } // --- MONITOR: afterCompletion ------------------------------ for(monIter_t monIter = _monitors.begin(); monIter != _monitors.end(); monIter++) { try { (*monIter)->afterCompletion(shared_from_this()); } USCXML_MONITOR_CATCH_BLOCK(afterCompletion) } return FINISHED; } assert(hasLegalConfiguration()); _mutex.unlock(); // remove datamodel if(_dataModel) _dataModel = DataModel(); return PROCESSED; } catch (boost::bad_weak_ptr e) { LOG(ERROR) << "Unclean shutdown " << std::endl << std::endl; return INTERRUPTED; } // set datamodel to null from this thread if(_dataModel) _dataModel = DataModel(); }