示例#1
0
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();

}