From bfefa5fd44b9ed1491612f26b099db8ad624247b Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Wed, 5 Jul 2017 10:05:55 +0200 Subject: More performant Monitors Breaks InterpreterMonitor API, Interpreter argument is substituted by sessionId. Use Intererpreter::fromSessionId to retrieve actual session when required. --- contrib/benchmarks/LCCA.pdf | Bin 27216 -> 30518 bytes contrib/benchmarks/Transitions.pdf | Bin 30761 -> 30783 bytes contrib/benchmarks/uscxml/statesPerSecond.cpp | 8 +- .../swig/wrapped/WrappedInterpreterMonitor.cpp | 32 +- .../swig/wrapped/WrappedInterpreterMonitor.h | 44 +- src/uscxml/Interpreter.cpp | 33 +- src/uscxml/Interpreter.h | 34 +- src/uscxml/debug/Benchmark.cpp | 42 +- src/uscxml/debug/Benchmark.h | 10 +- src/uscxml/debug/DebugSession.cpp | 8 +- src/uscxml/debug/Debugger.cpp | 124 +- src/uscxml/debug/Debugger.h | 72 +- src/uscxml/debug/DebuggerServlet.cpp | 2 +- src/uscxml/interpreter/BasicContentExecutor.cpp | 10 +- src/uscxml/interpreter/ContentExecutorImpl.h | 2 +- src/uscxml/interpreter/FastMicroStep.cpp | 11 +- src/uscxml/interpreter/FastMicroStep.h | 13 +- src/uscxml/interpreter/InterpreterImpl.cpp | 20 +- src/uscxml/interpreter/InterpreterMonitor.h | 58 +- src/uscxml/interpreter/LargeMicroStep.cpp | 2285 ++++++++++---------- src/uscxml/interpreter/LargeMicroStep.h | 178 +- src/uscxml/interpreter/MicroStepImpl.h | 8 +- src/uscxml/plugins/Factory.cpp | 48 +- src/uscxml/plugins/Factory.h | 32 +- src/uscxml/transform/ChartToC.cpp | 22 +- src/uscxml/util/DOM.h | 4 + test/src/test-performance.cpp | 70 +- test/src/test-state-pass.cpp | 4 +- 28 files changed, 1628 insertions(+), 1546 deletions(-) diff --git a/contrib/benchmarks/LCCA.pdf b/contrib/benchmarks/LCCA.pdf index 4d5c051..dc7bd7d 100644 Binary files a/contrib/benchmarks/LCCA.pdf and b/contrib/benchmarks/LCCA.pdf differ diff --git a/contrib/benchmarks/Transitions.pdf b/contrib/benchmarks/Transitions.pdf index 34b0ef7..06acbcc 100644 Binary files a/contrib/benchmarks/Transitions.pdf and b/contrib/benchmarks/Transitions.pdf differ diff --git a/contrib/benchmarks/uscxml/statesPerSecond.cpp b/contrib/benchmarks/uscxml/statesPerSecond.cpp index f6e62e6..6b81bbd 100644 --- a/contrib/benchmarks/uscxml/statesPerSecond.cpp +++ b/contrib/benchmarks/uscxml/statesPerSecond.cpp @@ -18,8 +18,8 @@ system_clock::time_point endTime; class PerfMon : public InterpreterMonitor { public: - virtual void beforeEnteringState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) { - if (HAS_ATTR(state, X("id")) && ATTR(state, X("id")) == "mark") { + virtual void beforeEnteringState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state) { + if (stateName == "mark") { iterations++; now = system_clock::now(); if (now > report) { @@ -42,9 +42,9 @@ int main(int argc, char *argv[]) ActionLanguage al; if (argc > 2) { if (std::string(argv[2]) == "large") { - al.microStepper = Factory::getInstance()->createMicroStepper("large", (MicroStepCallbacks*)sc); + al.microStepper = Factory::getInstance()->createMicroStepper("large", (MicroStepCallbacks*)(sc.getImpl().get())); } else { - al.microStepper = Factory::getInstance()->createMicroStepper("fast", (MicroStepCallbacks*)sc); + al.microStepper = Factory::getInstance()->createMicroStepper("fast", (MicroStepCallbacks*)(sc.getImpl().get())); } } sc.setActionLanguage(al); diff --git a/src/bindings/swig/wrapped/WrappedInterpreterMonitor.cpp b/src/bindings/swig/wrapped/WrappedInterpreterMonitor.cpp index 03b52bc..2c501a1 100644 --- a/src/bindings/swig/wrapped/WrappedInterpreterMonitor.cpp +++ b/src/bindings/swig/wrapped/WrappedInterpreterMonitor.cpp @@ -30,31 +30,31 @@ using namespace XERCESC_NS; WrappedInterpreterMonitor::WrappedInterpreterMonitor() {} WrappedInterpreterMonitor::~WrappedInterpreterMonitor() {} -void WrappedInterpreterMonitor::beforeExitingState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) { +void WrappedInterpreterMonitor::beforeExitingState(const std::string& sessionId, const std::string& stateId, const XERCESC_NS::DOMElement* state) { std::stringstream ss; ss << *state; - beforeExitingState(ATTR(state, kXMLCharId), DOMUtils::xPathForNode(state), ss.str()); + beforeExitingState(stateId, DOMUtils::xPathForNode(state), ss.str()); } -void WrappedInterpreterMonitor::afterExitingState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) { +void WrappedInterpreterMonitor::afterExitingState(const std::string& sessionId, const std::string& stateId, const XERCESC_NS::DOMElement* state) { std::stringstream ss; ss << *state; - afterExitingState(ATTR(state, kXMLCharId), DOMUtils::xPathForNode(state), ss.str()); + afterExitingState(stateId, DOMUtils::xPathForNode(state), ss.str()); } -void WrappedInterpreterMonitor::beforeExecutingContent(Interpreter& interpreter, const XERCESC_NS::DOMElement* content) { +void WrappedInterpreterMonitor::beforeExecutingContent(const std::string& sessionId, const XERCESC_NS::DOMElement* content) { std::stringstream ss; ss << *content; beforeExecutingContent(TAGNAME(content), DOMUtils::xPathForNode(content), ss.str()); } -void WrappedInterpreterMonitor::afterExecutingContent(Interpreter& interpreter, const XERCESC_NS::DOMElement* content) { +void WrappedInterpreterMonitor::afterExecutingContent(const std::string& sessionId, const XERCESC_NS::DOMElement* content) { std::stringstream ss; ss << *content; afterExecutingContent(TAGNAME(content), DOMUtils::xPathForNode(content), ss.str()); } -void WrappedInterpreterMonitor::beforeUninvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { +void WrappedInterpreterMonitor::beforeUninvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { std::stringstream ss; ss << *invoker; std::string invokeId; @@ -65,7 +65,7 @@ void WrappedInterpreterMonitor::beforeUninvoking(Interpreter& interpreter, const beforeUninvoking(DOMUtils::xPathForNode(invoker), invokeId, ss.str()); } -void WrappedInterpreterMonitor::afterUninvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { +void WrappedInterpreterMonitor::afterUninvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { std::stringstream ss; ss << *invoker; std::string invokeId; @@ -76,7 +76,7 @@ void WrappedInterpreterMonitor::afterUninvoking(Interpreter& interpreter, const afterUninvoking(DOMUtils::xPathForNode(invoker), invokeId, ss.str()); } -void WrappedInterpreterMonitor::beforeTakingTransition(Interpreter& interpreter, const XERCESC_NS::DOMElement* transition) { +void WrappedInterpreterMonitor::beforeTakingTransition(const std::string& sessionId, const XERCESC_NS::DOMElement* transition) { XERCESC_NS::DOMElement* sourceState = getSourceState(transition); const XERCESC_NS::DOMElement* root = DOMUtils::getNearestAncestor(transition, "scxml"); @@ -93,7 +93,7 @@ void WrappedInterpreterMonitor::beforeTakingTransition(Interpreter& interpreter, beforeTakingTransition(DOMUtils::xPathForNode(transition), ATTR_CAST(sourceState, kXMLCharId), targets, ss.str()); } -void WrappedInterpreterMonitor::afterTakingTransition(Interpreter& interpreter, const XERCESC_NS::DOMElement* transition) { +void WrappedInterpreterMonitor::afterTakingTransition(const std::string& sessionId, const XERCESC_NS::DOMElement* transition) { XERCESC_NS::DOMElement* sourceState = getSourceState(transition); const XERCESC_NS::DOMElement* root = DOMUtils::getNearestAncestor(transition, "scxml"); @@ -110,19 +110,19 @@ void WrappedInterpreterMonitor::afterTakingTransition(Interpreter& interpreter, afterTakingTransition(DOMUtils::xPathForNode(transition), ATTR_CAST(sourceState, kXMLCharId), targets, ss.str()); } -void WrappedInterpreterMonitor::beforeEnteringState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) { +void WrappedInterpreterMonitor::beforeEnteringState(const std::string& sessionId, const std::string& stateId, const XERCESC_NS::DOMElement* state) { std::stringstream ss; ss << *state; - beforeEnteringState(ATTR(state, kXMLCharId), DOMUtils::xPathForNode(state), ss.str()); + beforeEnteringState(stateId, DOMUtils::xPathForNode(state), ss.str()); } -void WrappedInterpreterMonitor::afterEnteringState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) { +void WrappedInterpreterMonitor::afterEnteringState(const std::string& sessionId, const std::string& stateId, const XERCESC_NS::DOMElement* state) { std::stringstream ss; ss << *state; - afterEnteringState(ATTR(state, kXMLCharId), DOMUtils::xPathForNode(state), ss.str()); + afterEnteringState(stateId, DOMUtils::xPathForNode(state), ss.str()); } -void WrappedInterpreterMonitor::beforeInvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { +void WrappedInterpreterMonitor::beforeInvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { std::stringstream ss; ss << *invoker; std::string invokeId; @@ -133,7 +133,7 @@ void WrappedInterpreterMonitor::beforeInvoking(Interpreter& interpreter, const X beforeInvoking(DOMUtils::xPathForNode(invoker), invokeId, ss.str()); } -void WrappedInterpreterMonitor::afterInvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { +void WrappedInterpreterMonitor::afterInvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { std::stringstream ss; ss << *invoker; std::string invokeId; diff --git a/src/bindings/swig/wrapped/WrappedInterpreterMonitor.h b/src/bindings/swig/wrapped/WrappedInterpreterMonitor.h index 697a420..cd4404b 100644 --- a/src/bindings/swig/wrapped/WrappedInterpreterMonitor.h +++ b/src/bindings/swig/wrapped/WrappedInterpreterMonitor.h @@ -44,34 +44,38 @@ public: WrappedInterpreterMonitor(); virtual ~WrappedInterpreterMonitor(); - virtual void beforeProcessingEvent(Interpreter& interpreter, const Event& event) {} - virtual void beforeMicroStep(Interpreter& interpreter) {} + virtual void beforeProcessingEvent(const std::string& sessionId, const Event& event) {} + virtual void beforeMicroStep(const std::string& sessionId) {} - void beforeExitingState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state); + void beforeExitingState(const std::string& sessionId, + const std::string& stateId, + const XERCESC_NS::DOMElement* state); virtual void beforeExitingState(const std::string& stateId, const std::string& xpath, const std::string& stateXML) {} - void afterExitingState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state); + void afterExitingState(const std::string& sessionId, + const std::string& stateId, + const XERCESC_NS::DOMElement* state); virtual void afterExitingState(const std::string& stateId, const std::string& xpath, const std::string& stateXML) {} - void beforeExecutingContent(Interpreter& interpreter, const XERCESC_NS::DOMElement* content); + void beforeExecutingContent(const std::string& sessionId, const XERCESC_NS::DOMElement* content); virtual void beforeExecutingContent(const std::string& tagName, const std::string& xpath, const std::string& contentXML) {} - void afterExecutingContent(Interpreter& interpreter, const XERCESC_NS::DOMElement* content); + void afterExecutingContent(const std::string& sessionId, const XERCESC_NS::DOMElement* content); virtual void afterExecutingContent(const std::string& tagName, const std::string& xpath, const std::string& contentXML) {} - void beforeUninvoking(Interpreter& interpreter, + void beforeUninvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid); virtual void beforeUninvoking(const std::string& xpath, @@ -79,7 +83,7 @@ public: const std::string& invokerXML) {} - void afterUninvoking(Interpreter& interpreter, + void afterUninvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid); virtual void afterUninvoking(const std::string& xpath, @@ -87,14 +91,14 @@ public: const std::string& invokerXML) {} - void beforeTakingTransition(Interpreter& interpreter, + void beforeTakingTransition(const std::string& sessionId, const XERCESC_NS::DOMElement* transition); virtual void beforeTakingTransition(const std::string& xpath, const std::string& source, const std::list& targets, const std::string& transitionXML) {} - void afterTakingTransition(Interpreter& interpreter, + void afterTakingTransition(const std::string& sessionId, const XERCESC_NS::DOMElement* transition); virtual void afterTakingTransition(const std::string& xpath, const std::string& source, @@ -102,41 +106,43 @@ public: const std::string& transitionXML) {} - void beforeEnteringState(Interpreter& interpreter, + void beforeEnteringState(const std::string& sessionId, + const std::string& stateId, const XERCESC_NS::DOMElement* state); virtual void beforeEnteringState(const std::string& stateId, const std::string& xpath, const std::string& stateXML) {} - void afterEnteringState(Interpreter& interpreter, + void afterEnteringState(const std::string& sessionId, + const std::string& stateId, const XERCESC_NS::DOMElement* state); virtual void afterEnteringState(const std::string& stateId, const std::string& xpath, const std::string& stateXML) {} - void beforeInvoking(Interpreter& interpreter, + void beforeInvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid); virtual void beforeInvoking(const std::string& xpath, const std::string& invokeid, const std::string& invokerXML) {} - void afterInvoking(Interpreter& interpreter, + void afterInvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid); virtual void afterInvoking(const std::string& xpath, const std::string& invokeid, const std::string& invokerXML) {} - virtual void afterMicroStep(Interpreter& interpreter) {} - virtual void onStableConfiguration(Interpreter& interpreter) {} + virtual void afterMicroStep(const std::string& sessionId) {} + virtual void onStableConfiguration(const std::string& sessionId) {} - virtual void beforeCompletion(Interpreter& interpreter) {} - virtual void afterCompletion(Interpreter& interpreter) {} + virtual void beforeCompletion(const std::string& sessionId) {} + virtual void afterCompletion(const std::string& sessionId) {} - virtual void reportIssue(Interpreter& interpreter, + virtual void reportIssue(const std::string& sessionId, const InterpreterIssue& issue) {} }; diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index b20013f..c4025ba 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -82,7 +82,7 @@ Interpreter Interpreter::fromXML(const std::string& xml, const std::string& base interpreterImpl->_document = parser->adoptDocument(); interpreterImpl->_baseURL = absUrl; - interpreterImpl->_md5 = md5(xml); + interpreterImpl->_md5 = md5(xml); InterpreterImpl::addInstance(interpreterImpl); } catch (const XERCESC_NS::SAXParseException& toCatch) { @@ -151,7 +151,7 @@ Interpreter Interpreter::fromURL(const std::string& url) { #if 1 // Xercesc is hard to build with SSL on windows, whereas curl uses winssl - return fromXML(absUrl.getInContent(), absUrl); + return fromXML(absUrl.getInContent(), absUrl); #else std::shared_ptr interpreterImpl(new InterpreterImpl()); @@ -191,6 +191,15 @@ Interpreter Interpreter::fromURL(const std::string& url) { } +Interpreter Interpreter::fromSessionId(const std::string& sessionId) { + std::lock_guard lock(InterpreterImpl::_instanceMutex); + std::map > instances = InterpreterImpl::getInstances(); + if (instances.find(sessionId) != instances.end()) { + return Interpreter(instances[sessionId].lock()); + } + return Interpreter(); +} + void Interpreter::reset() { return _impl->reset(); } @@ -274,19 +283,20 @@ static void printNodeSet(Logger& logger, const std::list lock(_mutex); LOG(_logger, USCXML_VERBATIM) << "Transition: " << uscxml::DOMUtils::xPathForNode(transition) << std::endl; } -void StateTransitionMonitor::onStableConfiguration(Interpreter& interpreter) { +void StateTransitionMonitor::onStableConfiguration(const std::string& sessionId) { std::lock_guard lock(_mutex); + Interpreter interpreter = Interpreter::fromSessionId(sessionId); LOG(_logger, USCXML_VERBATIM) << "Stable Config: { "; printNodeSet(_logger, interpreter.getConfiguration()); LOG(_logger, USCXML_VERBATIM) << " }" << std::endl; } -void StateTransitionMonitor::beforeProcessingEvent(Interpreter& interpreter, const uscxml::Event& event) { +void StateTransitionMonitor::beforeProcessingEvent(const std::string& sessionId, const uscxml::Event& event) { std::lock_guard lock(_mutex); switch (event.eventType) { case uscxml::Event::INTERNAL: @@ -301,24 +311,29 @@ void StateTransitionMonitor::beforeProcessingEvent(Interpreter& interpreter, con } } -void StateTransitionMonitor::beforeExecutingContent(Interpreter& interpreter, const XERCESC_NS::DOMElement* element) { +void StateTransitionMonitor::beforeExecutingContent(const std::string& sessionId, const XERCESC_NS::DOMElement* element) { std::lock_guard lock(_mutex); LOG(_logger, USCXML_VERBATIM) << "Executable Content: " << DOMUtils::xPathForNode(element) << std::endl; } -void StateTransitionMonitor::beforeExitingState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) { +void StateTransitionMonitor::beforeExitingState(const std::string& sessionId, + const std::string& stateName, + const XERCESC_NS::DOMElement* state) { std::lock_guard lock(_mutex); LOG(_logger, USCXML_VERBATIM) << "Exiting: " << (HAS_ATTR(state, kXMLCharId) ? ATTR(state, kXMLCharId) : DOMUtils::xPathForNode(state)) << std::endl; } -void StateTransitionMonitor::beforeEnteringState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) { +void StateTransitionMonitor::beforeEnteringState(const std::string& sessionId, + const std::string& stateName, + const XERCESC_NS::DOMElement* state) { std::lock_guard lock(_mutex); LOG(_logger, USCXML_VERBATIM) << "Entering: " << (HAS_ATTR(state, kXMLCharId) ? ATTR(state, kXMLCharId) : DOMUtils::xPathForNode(state)) << std::endl; } -void StateTransitionMonitor::beforeMicroStep(Interpreter& interpreter) { +void StateTransitionMonitor::beforeMicroStep(const std::string& sessionId) { std::lock_guard lock(_mutex); + Interpreter interpreter = Interpreter::fromSessionId(sessionId); LOG(_logger, USCXML_VERBATIM) << "Microstep in config: {"; printNodeSet(_logger, interpreter.getConfiguration()); LOG(_logger, USCXML_VERBATIM) << "}" << std::endl; diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index 6075dbc..3c44400 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -122,6 +122,12 @@ public: static Interpreter fromClone(const Interpreter& other); /** + * Get the instance of an interpreter with a given sessionId. + * @param other The session ID. + */ + static Interpreter fromSessionId(const std::string& sessionId); + + /** * See PIMPL_OPERATORS macro in Common.h */ PIMPL_OPERATORS(Interpreter); @@ -229,15 +235,27 @@ public: return _impl; } #if 0 - // "Ambiguous user-defined-conversion" with operator bool() on MSVC from Visual Studio 12 - explicit operator MicroStepCallbacks*() { return (MicroStepCallbacks*)(_impl.get()); } - explicit operator DataModelCallbacks*() { return (DataModelCallbacks*)(_impl.get()); } - explicit operator IOProcessorCallbacks*() { return (IOProcessorCallbacks*)(_impl.get()); } - explicit operator ContentExecutorCallbacks*() { return (ContentExecutorCallbacks*)(_impl.get()); } - explicit operator DelayedEventQueueCallbacks*() { return (DelayedEventQueueCallbacks*)(_impl.get()); } - explicit operator InvokerCallbacks*() { return (InvokerCallbacks*)(_impl.get()); } + // "Ambiguous user-defined-conversion" with operator bool() on MSVC from Visual Studio 12 + explicit operator MicroStepCallbacks*() { + return (MicroStepCallbacks*)(_impl.get()); + } + explicit operator DataModelCallbacks*() { + return (DataModelCallbacks*)(_impl.get()); + } + explicit operator IOProcessorCallbacks*() { + return (IOProcessorCallbacks*)(_impl.get()); + } + explicit operator ContentExecutorCallbacks*() { + return (ContentExecutorCallbacks*)(_impl.get()); + } + explicit operator DelayedEventQueueCallbacks*() { + return (DelayedEventQueueCallbacks*)(_impl.get()); + } + explicit operator InvokerCallbacks*() { + return (InvokerCallbacks*)(_impl.get()); + } #endif - + protected: std::shared_ptr _impl; diff --git a/src/uscxml/debug/Benchmark.cpp b/src/uscxml/debug/Benchmark.cpp index 0d2d789..b529bb4 100644 --- a/src/uscxml/debug/Benchmark.cpp +++ b/src/uscxml/debug/Benchmark.cpp @@ -23,33 +23,33 @@ namespace uscxml { std::mutex Benchmark::benchMutex; std::map Benchmark::benchmarks; - + Benchmark::Benchmark(const std::string& domain) : domain(domain), started(std::chrono::system_clock::now()) { } Benchmark::~Benchmark() { - std::lock_guard lock(benchMutex); - benchmarks[domain] += std::chrono::duration_cast(std::chrono::system_clock::now() - started).count(); + std::lock_guard lock(benchMutex); + benchmarks[domain] += std::chrono::duration_cast(std::chrono::system_clock::now() - started).count(); } - + std::ostream& Benchmark::report(std::ostream& stream) { - std::lock_guard lock(benchMutex); - - size_t longestDomain = 0; - for (auto benchmark : benchmarks) { - longestDomain = (benchmark.first.size() > longestDomain ? benchmark.first.size() : longestDomain); - } - longestDomain += 8; - - for (auto benchmark : benchmarks) { - std::string padding; - for (int i = benchmark.first.size(); i < (longestDomain - log10(benchmark.second)); i++) { - padding += " "; - } - - stream << benchmark.first << ":" << padding << (double)benchmark.second / 1000.0 << "ms" << std::endl; - } - return stream; + std::lock_guard lock(benchMutex); + + size_t longestDomain = 0; + for (auto benchmark : benchmarks) { + longestDomain = (benchmark.first.size() > longestDomain ? benchmark.first.size() : longestDomain); + } + longestDomain += 8; + + for (auto benchmark : benchmarks) { + std::string padding; + for (int i = benchmark.first.size(); i < (longestDomain - log10(benchmark.second)); i++) { + padding += " "; + } + + stream << benchmark.first << ":" << padding << (double)benchmark.second / 1000.0 << "ms" << std::endl; + } + return stream; } } diff --git a/src/uscxml/debug/Benchmark.h b/src/uscxml/debug/Benchmark.h index 82ef583..6548ff8 100644 --- a/src/uscxml/debug/Benchmark.h +++ b/src/uscxml/debug/Benchmark.h @@ -34,12 +34,12 @@ class USCXML_API Benchmark { public: Benchmark(const std::string& domain); ~Benchmark(); - - static std::ostream& report(std::ostream& stream); + + static std::ostream& report(std::ostream& stream); protected: - std::string domain; - std::chrono::time_point started; - + std::string domain; + std::chrono::time_point started; + static std::map benchmarks; static std::mutex benchMutex; }; diff --git a/src/uscxml/debug/DebugSession.cpp b/src/uscxml/debug/DebugSession.cpp index 802b355..42973fc 100644 --- a/src/uscxml/debug/DebugSession.cpp +++ b/src/uscxml/debug/DebugSession.cpp @@ -119,7 +119,7 @@ Data DebugSession::debugPrepare(const Data& data) { if (_interpreter) { // register ourself as a monitor _interpreter.addMonitor(_debugger); - _debugger->attachSession(_interpreter.getImpl().get(), shared_from_this()); + _debugger->attachSession(_interpreter.getImpl()->getSessionId(), shared_from_this()); replyData.compound["status"] = Data("success", Data::VERBATIM); } else { @@ -149,7 +149,7 @@ Data DebugSession::debugAttach(const Data& data) { std::shared_ptr instance = weakInstance.second.lock(); if (instance && instance->getSessionId() == interpreterId) { _interpreter = instance; - _debugger->attachSession(_interpreter.getImpl().get(), shared_from_this()); + _debugger->attachSession(_interpreter.getImpl()->getSessionId(), shared_from_this()); interpreterFound = true; break; } @@ -170,7 +170,7 @@ Data DebugSession::debugDetach(const Data& data) { Data replyData; _isAttached = false; - _debugger->detachSession(_interpreter.getImpl().get()); + _debugger->detachSession(_interpreter.getImpl()->getSessionId()); replyData.compound["status"] = Data("success", Data::VERBATIM); return replyData; } @@ -223,7 +223,7 @@ Data DebugSession::debugStop(const Data& data) { if (_interpreter) { // detach from old intepreter - _debugger->detachSession(_interpreter.getImpl().get()); + _debugger->detachSession(_interpreter.getImpl()->getSessionId()); } if (_isRunning && _interpreterThread != NULL) { diff --git a/src/uscxml/debug/Debugger.cpp b/src/uscxml/debug/Debugger.cpp index e1ff74b..cd7f0fe 100644 --- a/src/uscxml/debug/Debugger.cpp +++ b/src/uscxml/debug/Debugger.cpp @@ -24,9 +24,8 @@ namespace uscxml { -void Debugger::afterCompletion(Interpreter& interpreter) { - InterpreterImpl* impl = interpreter.getImpl().get(); - std::shared_ptr session = getSession(impl); +void Debugger::afterCompletion(const std::string& sessionId) { + std::shared_ptr session = getSession(sessionId); if (!session) return; @@ -35,9 +34,9 @@ void Debugger::afterCompletion(Interpreter& interpreter) { pushData(session, msg); } -void Debugger::beforeCompletion(Interpreter& interpreter) {} +void Debugger::beforeCompletion(const std::string& sessionId) {} -std::list Debugger::getQualifiedStateBreakpoints(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state, Breakpoint breakpointTemplate) { +std::list Debugger::getQualifiedStateBreakpoints(const std::string& sessionId, const XERCESC_NS::DOMElement* state, Breakpoint breakpointTemplate) { std::list breakpoints; Breakpoint bp = breakpointTemplate; // copy base as template @@ -49,7 +48,7 @@ std::list Debugger::getQualifiedStateBreakpoints(InterpreterImpl* im return breakpoints; } -std::list Debugger::getQualifiedInvokeBreakpoints(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string invokeId, Breakpoint breakpointTemplate) { +std::list Debugger::getQualifiedInvokeBreakpoints(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string invokeId, Breakpoint breakpointTemplate) { std::list breakpoints; Breakpoint bp = breakpointTemplate; // copy base as template @@ -60,7 +59,8 @@ std::list Debugger::getQualifiedInvokeBreakpoints(InterpreterImpl* i if (HAS_ATTR(invokeElem, kXMLCharType)) { bp.invokeType = ATTR(invokeElem, kXMLCharType); } else if (HAS_ATTR(invokeElem, kXMLCharTypeExpr)) { - bp.invokeType = impl->evalAsData(ATTR(invokeElem, kXMLCharTypeExpr)).atom; + Interpreter intptr = Interpreter::fromSessionId(sessionId); + bp.invokeType = intptr.getImpl()->evalAsData(ATTR(invokeElem, kXMLCharTypeExpr)).atom; } breakpoints.push_back(bp); @@ -68,11 +68,13 @@ std::list Debugger::getQualifiedInvokeBreakpoints(InterpreterImpl* i return breakpoints; } -std::list Debugger::getQualifiedTransBreakpoints(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition, Breakpoint breakpointTemplate) { +std::list Debugger::getQualifiedTransBreakpoints(const std::string& sessionId, const XERCESC_NS::DOMElement* transition, Breakpoint breakpointTemplate) { std::list breakpoints; XERCESC_NS::DOMElement* source = getSourceState(transition); - std::list targets = getTargetStates(transition, impl->_scxml); + Interpreter intptr = Interpreter::fromSessionId(sessionId); + + std::list targets = getTargetStates(transition, intptr.getImpl()->_scxml); for (auto target : targets) { @@ -88,59 +90,59 @@ std::list Debugger::getQualifiedTransBreakpoints(InterpreterImpl* im return breakpoints; } -void Debugger::beforeTakingTransition(Interpreter& interpreter, const XERCESC_NS::DOMElement* transition) { - handleTransition(interpreter, transition, Breakpoint::BEFORE); +void Debugger::beforeTakingTransition(const std::string& sessionId, const XERCESC_NS::DOMElement* transition) { + handleTransition(sessionId, transition, Breakpoint::BEFORE); } -void Debugger::afterTakingTransition(Interpreter& interpreter, const XERCESC_NS::DOMElement* transition) { - handleTransition(interpreter, transition, Breakpoint::AFTER); +void Debugger::afterTakingTransition(const std::string& sessionId, const XERCESC_NS::DOMElement* transition) { + handleTransition(sessionId, transition, Breakpoint::AFTER); } -void Debugger::beforeExecutingContent(Interpreter& interpreter, const XERCESC_NS::DOMElement* execContent) { - handleExecutable(interpreter, execContent, Breakpoint::BEFORE); +void Debugger::beforeExecutingContent(const std::string& sessionId, const XERCESC_NS::DOMElement* execContent) { + handleExecutable(sessionId, execContent, Breakpoint::BEFORE); } -void Debugger::afterExecutingContent(Interpreter& interpreter, const XERCESC_NS::DOMElement* execContent) { - handleExecutable(interpreter, execContent, Breakpoint::AFTER); +void Debugger::afterExecutingContent(const std::string& sessionId, const XERCESC_NS::DOMElement* execContent) { + handleExecutable(sessionId, execContent, Breakpoint::AFTER); } -void Debugger::beforeExitingState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) { - handleState(interpreter, state, Breakpoint::BEFORE, Breakpoint::EXIT); +void Debugger::beforeExitingState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state) { + handleState(sessionId, state, Breakpoint::BEFORE, Breakpoint::EXIT); } -void Debugger::afterExitingState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) { - handleState(interpreter, state, Breakpoint::AFTER, Breakpoint::EXIT); +void Debugger::afterExitingState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state) { + handleState(sessionId, state, Breakpoint::AFTER, Breakpoint::EXIT); } -void Debugger::beforeEnteringState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) { - handleState(interpreter, state, Breakpoint::BEFORE, Breakpoint::ENTER); +void Debugger::beforeEnteringState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state) { + handleState(sessionId, state, Breakpoint::BEFORE, Breakpoint::ENTER); } -void Debugger::afterEnteringState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) { - handleState(interpreter, state, Breakpoint::AFTER, Breakpoint::ENTER); +void Debugger::afterEnteringState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state) { + handleState(sessionId, state, Breakpoint::AFTER, Breakpoint::ENTER); } -void Debugger::beforeUninvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { - handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::BEFORE, Breakpoint::UNINVOKE); +void Debugger::beforeUninvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { + handleInvoke(sessionId, invokeElem, invokeid, Breakpoint::BEFORE, Breakpoint::UNINVOKE); } -void Debugger::afterUninvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { - handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::AFTER, Breakpoint::UNINVOKE); +void Debugger::afterUninvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { + handleInvoke(sessionId, invokeElem, invokeid, Breakpoint::AFTER, Breakpoint::UNINVOKE); } -void Debugger::beforeInvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { - handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::BEFORE, Breakpoint::INVOKE); +void Debugger::beforeInvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { + handleInvoke(sessionId, invokeElem, invokeid, Breakpoint::BEFORE, Breakpoint::INVOKE); } -void Debugger::afterInvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { - handleInvoke(interpreter, invokeElem, invokeid, Breakpoint::AFTER, Breakpoint::INVOKE); +void Debugger::afterInvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { + handleInvoke(sessionId, invokeElem, invokeid, Breakpoint::AFTER, Breakpoint::INVOKE); } -void Debugger::onStableConfiguration(Interpreter& interpreter) { - handleStable(interpreter, Breakpoint::ON); +void Debugger::onStableConfiguration(const std::string& sessionId) { + handleStable(sessionId, Breakpoint::ON); } -void Debugger::beforeMicroStep(Interpreter& interpreter) { - handleMicrostep(interpreter, Breakpoint::BEFORE); +void Debugger::beforeMicroStep(const std::string& sessionId) { + handleMicrostep(sessionId, Breakpoint::BEFORE); } -void Debugger::afterMicroStep(Interpreter& interpreter) { - handleMicrostep(interpreter, Breakpoint::AFTER); +void Debugger::afterMicroStep(const std::string& sessionId) { + handleMicrostep(sessionId, Breakpoint::AFTER); } -void Debugger::beforeProcessingEvent(Interpreter& interpreter, const Event& event) { - handleEvent(interpreter, event, Breakpoint::BEFORE); +void Debugger::beforeProcessingEvent(const std::string& sessionId, const Event& event) { + handleEvent(sessionId, event, Breakpoint::BEFORE); } -void Debugger::handleExecutable(Interpreter& interpreter, +void Debugger::handleExecutable(const std::string& sessionId, const XERCESC_NS::DOMElement* execContentElem, Breakpoint::When when) { - std::shared_ptr session = getSession(interpreter.getImpl().get()); + std::shared_ptr session = getSession(sessionId); if (!session) return; if (!session->_isRunning) @@ -159,9 +161,8 @@ void Debugger::handleExecutable(Interpreter& interpreter, } -void Debugger::handleEvent(Interpreter& interpreter, const Event& event, Breakpoint::When when) { - InterpreterImpl* impl = interpreter.getImpl().get(); - std::shared_ptr session = getSession(impl); +void Debugger::handleEvent(const std::string& sessionId, const Event& event, Breakpoint::When when) { + std::shared_ptr session = getSession(sessionId); if (!session) return; if (!session->_isRunning) @@ -179,9 +180,8 @@ void Debugger::handleEvent(Interpreter& interpreter, const Event& event, Breakpo } -void Debugger::handleStable(Interpreter& interpreter, Breakpoint::When when) { - InterpreterImpl* impl = interpreter.getImpl().get(); - std::shared_ptr session = getSession(impl); +void Debugger::handleStable(const std::string& sessionId, Breakpoint::When when) { + std::shared_ptr session = getSession(sessionId); if (!session) return; if (!session->_isRunning) @@ -197,9 +197,8 @@ void Debugger::handleStable(Interpreter& interpreter, Breakpoint::When when) { session->checkBreakpoints(breakpoints); } -void Debugger::handleMicrostep(Interpreter& interpreter, Breakpoint::When when) { - InterpreterImpl* impl = interpreter.getImpl().get(); - std::shared_ptr session = getSession(impl); +void Debugger::handleMicrostep(const std::string& sessionId, Breakpoint::When when) { + std::shared_ptr session = getSession(sessionId); if (!session) return; if (!session->_isRunning) @@ -215,9 +214,8 @@ void Debugger::handleMicrostep(Interpreter& interpreter, Breakpoint::When when) session->checkBreakpoints(breakpoints); } -void Debugger::handleTransition(Interpreter& interpreter, const XERCESC_NS::DOMElement* transition, Breakpoint::When when) { - InterpreterImpl* impl = interpreter.getImpl().get(); - std::shared_ptr session = getSession(impl); +void Debugger::handleTransition(const std::string& sessionId, const XERCESC_NS::DOMElement* transition, Breakpoint::When when) { + std::shared_ptr session = getSession(sessionId); if (!session) return; if (!session->_isRunning) @@ -225,13 +223,12 @@ void Debugger::handleTransition(Interpreter& interpreter, const XERCESC_NS::DOME Breakpoint breakpointTemplate; breakpointTemplate.when = when; - std::list qualifiedBreakpoints = getQualifiedTransBreakpoints(impl, transition, breakpointTemplate); + std::list qualifiedBreakpoints = getQualifiedTransBreakpoints(sessionId, transition, breakpointTemplate); session->checkBreakpoints(qualifiedBreakpoints); } -void Debugger::handleState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state, Breakpoint::When when, Breakpoint::Action action) { - InterpreterImpl* impl = interpreter.getImpl().get(); - std::shared_ptr session = getSession(impl); +void Debugger::handleState(const std::string& sessionId, const XERCESC_NS::DOMElement* state, Breakpoint::When when, Breakpoint::Action action) { + std::shared_ptr session = getSession(sessionId); if (!session) return; if (!session->_isRunning) @@ -240,14 +237,13 @@ void Debugger::handleState(Interpreter& interpreter, const XERCESC_NS::DOMElemen Breakpoint breakpointTemplate; breakpointTemplate.when = when; breakpointTemplate.action = action; - std::list qualifiedBreakpoints = getQualifiedStateBreakpoints(impl, state, breakpointTemplate); + std::list qualifiedBreakpoints = getQualifiedStateBreakpoints(sessionId, state, breakpointTemplate); session->checkBreakpoints(qualifiedBreakpoints); } -void Debugger::handleInvoke(Interpreter& interpreter, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeId, Breakpoint::When when, Breakpoint::Action action) { - InterpreterImpl* impl = interpreter.getImpl().get(); - std::shared_ptr session = getSession(impl); +void Debugger::handleInvoke(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeId, Breakpoint::When when, Breakpoint::Action action) { + std::shared_ptr session = getSession(sessionId); if (!session) return; if (!session->_isRunning) @@ -256,7 +252,7 @@ void Debugger::handleInvoke(Interpreter& interpreter, const XERCESC_NS::DOMEleme Breakpoint breakpointTemplate; breakpointTemplate.when = when; breakpointTemplate.action = action; - std::list qualifiedBreakpoints = getQualifiedInvokeBreakpoints(impl, invokeElem, invokeId, breakpointTemplate); + std::list qualifiedBreakpoints = getQualifiedInvokeBreakpoints(sessionId, invokeElem, invokeId, breakpointTemplate); session->checkBreakpoints(qualifiedBreakpoints); } diff --git a/src/uscxml/debug/Debugger.h b/src/uscxml/debug/Debugger.h index d69e6f6..ec00a95 100644 --- a/src/uscxml/debug/Debugger.h +++ b/src/uscxml/debug/Debugger.h @@ -35,80 +35,80 @@ public: } virtual ~Debugger() {} - virtual void attachSession(InterpreterImpl* impl, std::shared_ptr session) { + virtual void attachSession(const std::string& sessionId, std::shared_ptr session) { std::lock_guard lock(_sessionMutex); - _sessionForInterpreter[impl] = session; + _sessionForInterpreter[sessionId] = session; } - virtual void detachSession(InterpreterImpl* impl) { + virtual void detachSession(const std::string& sessionId) { std::lock_guard lock(_sessionMutex); - _sessionForInterpreter.erase(impl); + _sessionForInterpreter.erase(sessionId); } - virtual std::shared_ptr getSession(InterpreterImpl* impl) { + virtual std::shared_ptr getSession(const std::string& sessionId) { std::lock_guard lock(_sessionMutex); - if (_sessionForInterpreter.find(impl) != _sessionForInterpreter.end()) - return _sessionForInterpreter[impl]; + if (_sessionForInterpreter.find(sessionId) != _sessionForInterpreter.end()) + return _sessionForInterpreter[sessionId]; return std::shared_ptr(); } virtual void pushData(std::shared_ptr session, Data pushData) = 0; // InterpreterMonitor - virtual void beforeProcessingEvent(Interpreter& interpreter, const Event& event); - virtual void beforeMicroStep(Interpreter& interpreter); - virtual void beforeExitingState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state); - virtual void afterExitingState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state); - virtual void beforeExecutingContent(Interpreter& interpreter, const XERCESC_NS::DOMElement* execContent); - virtual void afterExecutingContent(Interpreter& interpreter, const XERCESC_NS::DOMElement* execContent); - virtual void beforeUninvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid); - virtual void afterUninvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid); - virtual void beforeTakingTransition(Interpreter& interpreter, const XERCESC_NS::DOMElement* transition); - virtual void afterTakingTransition(Interpreter& interpreter, const XERCESC_NS::DOMElement* transition); - virtual void beforeEnteringState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state); - virtual void afterEnteringState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state); - virtual void beforeInvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid); - virtual void afterInvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid); - virtual void afterMicroStep(Interpreter& interpreter); - virtual void onStableConfiguration(Interpreter& interpreter); - virtual void beforeCompletion(Interpreter& interpreter); - virtual void afterCompletion(Interpreter& interpreter); + virtual void beforeProcessingEvent(const std::string& sessionId, const Event& event); + virtual void beforeMicroStep(const std::string& sessionId); + virtual void beforeExitingState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state); + virtual void afterExitingState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state); + virtual void beforeExecutingContent(const std::string& sessionId, const XERCESC_NS::DOMElement* execContent); + virtual void afterExecutingContent(const std::string& sessionId, const XERCESC_NS::DOMElement* execContent); + virtual void beforeUninvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid); + virtual void afterUninvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid); + virtual void beforeTakingTransition(const std::string& sessionId, const XERCESC_NS::DOMElement* transition); + virtual void afterTakingTransition(const std::string& sessionId, const XERCESC_NS::DOMElement* transition); + virtual void beforeEnteringState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state); + virtual void afterEnteringState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state); + virtual void beforeInvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid); + virtual void afterInvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid); + virtual void afterMicroStep(const std::string& sessionId); + virtual void onStableConfiguration(const std::string& sessionId); + virtual void beforeCompletion(const std::string& sessionId); + virtual void afterCompletion(const std::string& sessionId); protected: - void handleTransition(Interpreter& interpreter, + void handleTransition(const std::string& sessionId, const XERCESC_NS::DOMElement* transition, Breakpoint::When when); - void handleState(Interpreter& interpreter, + void handleState(const std::string& sessionId, const XERCESC_NS::DOMElement* state, Breakpoint::When when, Breakpoint::Action action); - void handleInvoke(Interpreter& interpreter, + void handleInvoke(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeId, Breakpoint::When when, Breakpoint::Action action); - void handleExecutable(Interpreter& interpreter, + void handleExecutable(const std::string& sessionId, const XERCESC_NS::DOMElement* execContentElem, Breakpoint::When when); - void handleStable(Interpreter& interpreter, Breakpoint::When when); - void handleMicrostep(Interpreter& interpreter, Breakpoint::When when); - void handleEvent(Interpreter& interpreter, const Event& event, Breakpoint::When when); + void handleStable(const std::string& sessionId, Breakpoint::When when); + void handleMicrostep(const std::string& sessionId, Breakpoint::When when); + void handleEvent(const std::string& sessionId, const Event& event, Breakpoint::When when); - std::list getQualifiedTransBreakpoints(InterpreterImpl* impl, + std::list getQualifiedTransBreakpoints(const std::string& sessionId, const XERCESC_NS::DOMElement* transition, Breakpoint breakpointTemplate); - std::list getQualifiedStateBreakpoints(InterpreterImpl* impl, + std::list getQualifiedStateBreakpoints(const std::string& sessionId, const XERCESC_NS::DOMElement* state, Breakpoint breakpointTemplate); - std::list getQualifiedInvokeBreakpoints(InterpreterImpl* impl, + std::list getQualifiedInvokeBreakpoints(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string invokeId, Breakpoint breakpointTemplate); std::recursive_mutex _sessionMutex; /// @todo: We ought to change form InterpreterImpl to Interpreter everywhere - std::map > _sessionForInterpreter; + std::map > _sessionForInterpreter; }; } diff --git a/src/uscxml/debug/DebuggerServlet.cpp b/src/uscxml/debug/DebuggerServlet.cpp index 1d2df6e..74853f4 100644 --- a/src/uscxml/debug/DebuggerServlet.cpp +++ b/src/uscxml/debug/DebuggerServlet.cpp @@ -210,7 +210,7 @@ void DebuggerServlet::processDisconnect(const HTTPServer::Request& request) { replyData.compound["reason"] = Data("No such session", Data::VERBATIM); } else { replyData.compound["status"] = Data("success", Data::VERBATIM); - detachSession(_sessionForId[sessionId]->getInterpreter().getImpl().get()); + detachSession(sessionId); _sessionForId[sessionId]->debugStop(request.data["content"]); _clientConns.erase(_sessionForId[sessionId]); _sendQueues.erase(_sessionForId[sessionId]); diff --git a/src/uscxml/interpreter/BasicContentExecutor.cpp b/src/uscxml/interpreter/BasicContentExecutor.cpp index 56e3d10..004eea9 100644 --- a/src/uscxml/interpreter/BasicContentExecutor.cpp +++ b/src/uscxml/interpreter/BasicContentExecutor.cpp @@ -729,11 +729,11 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element, bool a return Data(contentSS.str(), Data::INTERPRETED); // test153, we need to throw for test150 in promela, but we need to space normalize for test558 - try { - Data d = _callbacks->getAsData(contentSS.str()); - if (!d.empty()) - return d; - } catch ( ... ) {} + try { + Data d = _callbacks->getAsData(contentSS.str()); + if (!d.empty()) + return d; + } catch ( ... ) {} // test558 return Data(spaceNormalize(contentSS.str()), Data::VERBATIM); diff --git a/src/uscxml/interpreter/ContentExecutorImpl.h b/src/uscxml/interpreter/ContentExecutorImpl.h index d837273..c85d9a0 100644 --- a/src/uscxml/interpreter/ContentExecutorImpl.h +++ b/src/uscxml/interpreter/ContentExecutorImpl.h @@ -76,7 +76,7 @@ public: /** Monitoring */ virtual std::set getMonitors() = 0; - virtual Interpreter getInterpreter() = 0; + virtual const std::string& getSessionId() = 0; virtual Logger getLogger() = 0; virtual ExecutableContent createExecutableContent(const std::string& localName, const std::string& nameSpace) = 0; diff --git a/src/uscxml/interpreter/FastMicroStep.cpp b/src/uscxml/interpreter/FastMicroStep.cpp index 8b91191..d6ad298 100644 --- a/src/uscxml/interpreter/FastMicroStep.cpp +++ b/src/uscxml/interpreter/FastMicroStep.cpp @@ -269,6 +269,9 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { _states[i] = new State(); _states[i]->documentOrder = i; _states[i]->element = tmp.front(); + if (HAS_ATTR(_states[i]->element, kXMLCharId)) { + _states[i]->name = ATTR(_states[i]->element, kXMLCharId); + } _states[i]->element->setUserData(X("uscxmlState"), _states[i], NULL); _states[i]->completion.resize(_states.size()); _states[i]->ancestors.resize(_states.size()); @@ -995,7 +998,7 @@ ESTABLISH_ENTRYSET: while(i-- > 0) { if (BIT_HAS(i, _exitSet) && BIT_HAS(i, _configuration)) { - USCXML_MONITOR_CALLBACK1(monitors, beforeExitingState, USCXML_GET_STATE(i).element); + USCXML_MONITOR_CALLBACK2(monitors, beforeExitingState, USCXML_GET_STATE(i).name, USCXML_GET_STATE(i).element); /* call all on exit handlers */ for (auto exitIter = USCXML_GET_STATE(i).onExit.begin(); exitIter != USCXML_GET_STATE(i).onExit.end(); exitIter++) { @@ -1007,7 +1010,7 @@ ESTABLISH_ENTRYSET: } BIT_CLEAR(i, _configuration); - USCXML_MONITOR_CALLBACK1(monitors, afterExitingState, USCXML_GET_STATE(i).element); + USCXML_MONITOR_CALLBACK2(monitors, afterExitingState, USCXML_GET_STATE(i).name, USCXML_GET_STATE(i).element); } } @@ -1058,7 +1061,7 @@ ESTABLISH_ENTRYSET: continue; } - USCXML_MONITOR_CALLBACK1(monitors, beforeEnteringState, USCXML_GET_STATE(i).element); + USCXML_MONITOR_CALLBACK2(monitors, beforeEnteringState, USCXML_GET_STATE(i).name, USCXML_GET_STATE(i).element); BIT_SET_AT(i, _configuration); @@ -1079,7 +1082,7 @@ ESTABLISH_ENTRYSET: } } - USCXML_MONITOR_CALLBACK1(monitors, afterEnteringState, USCXML_GET_STATE(i).element); + USCXML_MONITOR_CALLBACK2(monitors, afterEnteringState, USCXML_GET_STATE(i).name, USCXML_GET_STATE(i).element); /* take history and initial transitions */ j = _transSet.find_first(); diff --git a/src/uscxml/interpreter/FastMicroStep.h b/src/uscxml/interpreter/FastMicroStep.h index 1b743ef..5ff1117 100644 --- a/src/uscxml/interpreter/FastMicroStep.h +++ b/src/uscxml/interpreter/FastMicroStep.h @@ -54,8 +54,10 @@ public: virtual ~FastMicroStep(); virtual std::shared_ptr create(MicroStepCallbacks* callbacks); - std::string getName() { return "fast"; } - + std::string getName() { + return "fast"; + } + virtual InterpreterState step(size_t blockMs); virtual void reset(); virtual bool isInState(const std::string& stateId); @@ -66,8 +68,8 @@ public: virtual Data serialize(); protected: - FastMicroStep() {}; // only for factory - + FastMicroStep() {}; // only for factory + class Transition { public: Transition() : element(NULL), source(0), onTrans(NULL), type(0) {} @@ -104,6 +106,7 @@ protected: std::list onEntry; std::list onExit; XERCESC_NS::DOMElement* doneData; + std::string name; unsigned char type; }; @@ -166,7 +169,7 @@ private: void printStateNames(const boost::dynamic_bitset& bitset); #endif - friend class Factory; + friend class Factory; }; } diff --git a/src/uscxml/interpreter/InterpreterImpl.cpp b/src/uscxml/interpreter/InterpreterImpl.cpp index 00fbf41..9e59cea 100644 --- a/src/uscxml/interpreter/InterpreterImpl.cpp +++ b/src/uscxml/interpreter/InterpreterImpl.cpp @@ -338,15 +338,15 @@ void InterpreterImpl::init() { // try to open chached data from resource directory std::string sharedTemp = URL::getTempDir(true); std::ifstream dataFS(sharedTemp + PATH_SEPERATOR + md5(_baseURL) + ".uscxml.cache"); - try { - if (dataFS.is_open()) { - std::string cacheStr((std::istreambuf_iterator(dataFS)), - std::istreambuf_iterator()); - _cache = Data::fromJSON(cacheStr); - } - } catch (...) { - remove(std::string(sharedTemp + PATH_SEPERATOR + md5(_baseURL) + ".uscxml.cache").c_str()); - } + try { + if (dataFS.is_open()) { + std::string cacheStr((std::istreambuf_iterator(dataFS)), + std::istreambuf_iterator()); + _cache = Data::fromJSON(cacheStr); + } + } catch (...) { + remove(std::string(sharedTemp + PATH_SEPERATOR + md5(_baseURL) + ".uscxml.cache").c_str()); + } // get md5 of current document std::stringstream ss; @@ -390,7 +390,7 @@ void InterpreterImpl::init() { } if (!_microStepper) { - _microStepper = MicroStep(std::shared_ptr(new LargeMicroStep(this))); + _microStepper = MicroStep(std::shared_ptr(new FastMicroStep(this))); } _microStepper.init(_scxml); diff --git a/src/uscxml/interpreter/InterpreterMonitor.h b/src/uscxml/interpreter/InterpreterMonitor.h index 70cac0c..43b7f53 100644 --- a/src/uscxml/interpreter/InterpreterMonitor.h +++ b/src/uscxml/interpreter/InterpreterMonitor.h @@ -35,17 +35,17 @@ if (_state == USCXML_DESTROYED) { throw std::bad_weak_ptr(); } #define USCXML_MONITOR_CALLBACK(callbacks, function) { \ if (callbacks.size() > 0) {\ -Interpreter inptr = _callbacks->getInterpreter(); \ +const std::string& inptr = _callbacks->getSessionId(); \ for (auto callback : callbacks) { callback->function(inptr); } } } #define USCXML_MONITOR_CALLBACK1(callbacks, function, arg1) { \ if (callbacks.size() > 0) {\ -Interpreter inptr = _callbacks->getInterpreter(); \ +const std::string& inptr = _callbacks->getSessionId(); \ for (auto callback : callbacks) { callback->function(inptr, arg1); } } } #define USCXML_MONITOR_CALLBACK2(callbacks, function, arg1, arg2) { \ if (callbacks.size() > 0) {\ -Interpreter inptr = _callbacks->getInterpreter(); \ +const std::string& inptr = _callbacks->getSessionId(); \ for (auto callback : callbacks) { callback->function(inptr, arg1, arg2); } } } // forward declare @@ -65,34 +65,34 @@ public: InterpreterMonitor(Logger logger) : _copyToInvokers(false), _logger(logger) {} virtual ~InterpreterMonitor() {} - virtual void beforeProcessingEvent(Interpreter& interpreter, const Event& event) {} - virtual void beforeMicroStep(Interpreter& interpreter) {} + virtual void beforeProcessingEvent(const std::string& sessionId, const Event& event) {} + virtual void beforeMicroStep(const std::string& sessionId) {} - virtual void beforeExitingState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) {} - virtual void afterExitingState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) {} + virtual void beforeExitingState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state) {} + virtual void afterExitingState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state) {} - virtual void beforeExecutingContent(Interpreter& interpreter, const XERCESC_NS::DOMElement* execContent) {} - virtual void afterExecutingContent(Interpreter& interpreter, const XERCESC_NS::DOMElement* execContent) {} + virtual void beforeExecutingContent(const std::string& sessionId, const XERCESC_NS::DOMElement* execContent) {} + virtual void afterExecutingContent(const std::string& sessionId, const XERCESC_NS::DOMElement* execContent) {} - virtual void beforeUninvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} - virtual void afterUninvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} + virtual void beforeUninvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} + virtual void afterUninvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} - virtual void beforeTakingTransition(Interpreter& interpreter, const XERCESC_NS::DOMElement* transition) {} - virtual void afterTakingTransition(Interpreter& interpreter, const XERCESC_NS::DOMElement* transition) {} + virtual void beforeTakingTransition(const std::string& sessionId, const XERCESC_NS::DOMElement* transition) {} + virtual void afterTakingTransition(const std::string& sessionId, const XERCESC_NS::DOMElement* transition) {} - virtual void beforeEnteringState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) {} - virtual void afterEnteringState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) {} + virtual void beforeEnteringState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state) {} + virtual void afterEnteringState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state) {} - virtual void beforeInvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} - virtual void afterInvoking(Interpreter& interpreter, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} + virtual void beforeInvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} + virtual void afterInvoking(const std::string& sessionId, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} - virtual void afterMicroStep(Interpreter& interpreter) {} - virtual void onStableConfiguration(Interpreter& interpreter) {} + virtual void afterMicroStep(const std::string& sessionId) {} + virtual void onStableConfiguration(const std::string& sessionId) {} - virtual void beforeCompletion(Interpreter& interpreter) {} - virtual void afterCompletion(Interpreter& interpreter) {} + virtual void beforeCompletion(const std::string& sessionId) {} + virtual void afterCompletion(const std::string& sessionId) {} - virtual void reportIssue(Interpreter& interpreter, const InterpreterIssue& issue) {} + virtual void reportIssue(const std::string& sessionId, const InterpreterIssue& issue) {} void copyToInvokers(bool copy) { _copyToInvokers = copy; @@ -112,13 +112,13 @@ public: StateTransitionMonitor(std::string prefix = "") : _logPrefix(prefix) {} virtual ~StateTransitionMonitor() {} - virtual void beforeTakingTransition(Interpreter& interpreter, const XERCESC_NS::DOMElement* transition); - virtual void beforeExecutingContent(Interpreter& interpreter, const XERCESC_NS::DOMElement* element); - virtual void onStableConfiguration(Interpreter& interpreter); - virtual void beforeProcessingEvent(Interpreter& interpreter, const uscxml::Event& event); - virtual void beforeExitingState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state); - virtual void beforeEnteringState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state); - virtual void beforeMicroStep(Interpreter& interpreter); + virtual void beforeTakingTransition(const std::string& sessionId, const XERCESC_NS::DOMElement* transition); + virtual void beforeExecutingContent(const std::string& sessionId, const XERCESC_NS::DOMElement* element); + virtual void onStableConfiguration(const std::string& sessionId); + virtual void beforeProcessingEvent(const std::string& sessionId, const uscxml::Event& event); + virtual void beforeExitingState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state); + virtual void beforeEnteringState(const std::string& sessionId, const std::string& stateName, const XERCESC_NS::DOMElement* state); + virtual void beforeMicroStep(const std::string& sessionId); protected: static std::recursive_mutex _mutex; diff --git a/src/uscxml/interpreter/LargeMicroStep.cpp b/src/uscxml/interpreter/LargeMicroStep.cpp index 83900c8..eab303c 100644 --- a/src/uscxml/interpreter/LargeMicroStep.cpp +++ b/src/uscxml/interpreter/LargeMicroStep.cpp @@ -74,12 +74,12 @@ using namespace XERCESC_NS; * Print name of states contained in a (debugging). */ void LargeMicroStep::printStateNames(const boost::container::flat_set& a) { - const char* seperator = ""; - for (auto state : a) { - std::cerr << seperator << (HAS_ATTR(state->element, X("id")) ? ATTR(state->element, X("id")) : "UNK"); - seperator = ", "; - } - std::cerr << std::endl; + const char* seperator = ""; + for (auto state : a) { + std::cerr << seperator << (HAS_ATTR(state->element, X("id")) ? ATTR(state->element, X("id")) : "UNK"); + seperator = ", "; + } + std::cerr << std::endl; } #endif @@ -87,1220 +87,1225 @@ LargeMicroStep::LargeMicroStep(MicroStepCallbacks* callbacks) : MicroStepImpl(ca } std::shared_ptr LargeMicroStep::create(MicroStepCallbacks* callbacks) { - return std::shared_ptr(new LargeMicroStep(callbacks)); + return std::shared_ptr(new LargeMicroStep(callbacks)); } LargeMicroStep::~LargeMicroStep() { - for (size_t i = 0; i < _states.size(); i++) { - delete(_states[i]); - } - for (size_t i = 0; i < _transitions.size(); i++) { - delete(_transitions[i]); - } + for (size_t i = 0; i < _states.size(); i++) { + delete(_states[i]); + } + for (size_t i = 0; i < _transitions.size(); i++) { + delete(_transitions[i]); + } } std::list LargeMicroStep::getConfiguration() { - std::list config; - for (auto state : _configuration) { - config.push_back(state->element); - } - return config; + std::list config; + for (auto state : _configuration) { + config.push_back(state->element); + } + return config; } void LargeMicroStep::markAsCancelled() { - _isCancelled = true; + _isCancelled = true; } void LargeMicroStep::reset() { - _isCancelled = false; - _flags = USCXML_CTX_PRISTINE; - _configuration.clear(); - _configurationPostFix.clear(); - _history.clear(); - _initializedData.clear(); - _invocations.clear(); - + _isCancelled = false; + _flags = USCXML_CTX_PRISTINE; + _configuration.clear(); + _configurationPostFix.clear(); + _history.clear(); + _initializedData.clear(); + _invocations.clear(); + } bool LargeMicroStep::isInState(const std::string& stateId) { #ifdef USCXML_VERBOSE - printStateNames(_configuration); + printStateNames(_configuration); #endif - for (auto state : _configuration) { - if (HAS_ATTR(state->element, kXMLCharId) && ATTR(state->element, kXMLCharId) == stateId) - return true; - } - return false; + for (auto state : _configuration) { + if (HAS_ATTR(state->element, kXMLCharId) && ATTR(state->element, kXMLCharId) == stateId) + return true; + } + return false; } std::list LargeMicroStep::getHistoryCompletion(const DOMElement* history) { - std::set elements; - elements.insert(_xmlPrefix.str() + "history"); - std::list histories = DOMUtils::inPostFixOrder(elements, _scxml); - - std::list covered; - std::list perParentcovered; - const DOMNode* parent = NULL; - - std::list completion; - - if (parent != history->getParentNode()) { - covered.insert(covered.end(), perParentcovered.begin(), perParentcovered.end()); - perParentcovered.clear(); - parent = history->getParentNode(); - } - - bool deep = (HAS_ATTR(history, kXMLCharType) && iequals(ATTR(history, kXMLCharType), "deep")); - - for (size_t j = 0; j < _states.size(); j++) { - if (_states[j]->element == history) - continue; - - if (DOMUtils::isDescendant((DOMNode*)_states[j]->element, history->getParentNode()) && isHistory(_states[j]->element)) { - ((DOMElement*)history)->setUserData(X("hasHistoryChild"), _states[j], NULL); - } - - if (DOMUtils::isMember(_states[j]->element, covered)) - continue; - - if (deep) { - if (DOMUtils::isDescendant(_states[j]->element, history->getParentNode()) && !isHistory(_states[j]->element)) { - completion.push_back(_states[j]->element); - } - } else { - if (_states[j]->element->getParentNode() == history->getParentNode() && !isHistory(_states[j]->element)) { - completion.push_back(_states[j]->element); - } - } - } - - return completion; + std::set elements; + elements.insert(_xmlPrefix.str() + "history"); + std::list histories = DOMUtils::inPostFixOrder(elements, _scxml); + + std::list covered; + std::list perParentcovered; + const DOMNode* parent = NULL; + + std::list completion; + + if (parent != history->getParentNode()) { + covered.insert(covered.end(), perParentcovered.begin(), perParentcovered.end()); + perParentcovered.clear(); + parent = history->getParentNode(); + } + + bool deep = (HAS_ATTR(history, kXMLCharType) && iequals(ATTR(history, kXMLCharType), "deep")); + + for (size_t j = 0; j < _states.size(); j++) { + if (_states[j]->element == history) + continue; + + if (DOMUtils::isDescendant((DOMNode*)_states[j]->element, history->getParentNode()) && isHistory(_states[j]->element)) { + ((DOMElement*)history)->setUserData(X("hasHistoryChild"), _states[j], NULL); + } + + if (DOMUtils::isMember(_states[j]->element, covered)) + continue; + + if (deep) { + if (DOMUtils::isDescendant(_states[j]->element, history->getParentNode()) && !isHistory(_states[j]->element)) { + completion.push_back(_states[j]->element); + } + } else { + if (_states[j]->element->getParentNode() == history->getParentNode() && !isHistory(_states[j]->element)) { + completion.push_back(_states[j]->element); + } + } + } + + return completion; } std::list LargeMicroStep::getCompletion(const DOMElement* state) { - if (isHistory(state)) { - // we already did in setHistoryCompletion - return getHistoryCompletion(state); - - } else if (isParallel(state)) { - return getChildStates(state); - - } else if (HAS_ATTR(state, kXMLCharInitial)) { - return getStates(tokenize(ATTR(state, kXMLCharInitial)), _scxml); - - } else { - std::list completion; - - std::list initElems = DOMUtils::filterChildElements(_xmlPrefix.str() + "initial", state); - if(initElems.size() > 0) { - // initial element is first child - completion.push_back(initElems.front()); - } else { - // first child state - for (auto childElem = state->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { - if (isState(childElem)) { - completion.push_back(childElem); - break; - } - } - } - return completion; - } + if (isHistory(state)) { + // we already did in setHistoryCompletion + return getHistoryCompletion(state); + + } else if (isParallel(state)) { + return getChildStates(state); + + } else if (HAS_ATTR(state, kXMLCharInitial)) { + return getStates(tokenize(ATTR(state, kXMLCharInitial)), _scxml); + + } else { + std::list completion; + + std::list initElems = DOMUtils::filterChildElements(_xmlPrefix.str() + "initial", state); + if(initElems.size() > 0) { + // initial element is first child + completion.push_back(initElems.front()); + } else { + // first child state + for (auto childElem = state->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { + if (isState(childElem)) { + completion.push_back(childElem); + break; + } + } + } + return completion; + } } void LargeMicroStep::init(XERCESC_NS::DOMElement* scxml) { - _scxml = scxml; - _binding = (HAS_ATTR(_scxml, kXMLCharBinding) && iequals(ATTR(_scxml, kXMLCharBinding), "late") ? LATE : EARLY); - _xmlPrefix = _scxml->getPrefix(); - _xmlNS = _scxml->getNamespaceURI(); - if (_xmlPrefix) { - _xmlPrefix = std::string(_xmlPrefix) + ":"; - } - - { - BENCHMARK("init resort states") - resortStates(_scxml, _xmlPrefix); - } - - std::set stateIds; - - /** -- All things states -- */ - - std::list tmp; - size_t i, j; - - tmp = DOMUtils::inDocumentOrder({ - _xmlPrefix.str() + "state", - _xmlPrefix.str() + "parallel", - _xmlPrefix.str() + "scxml", - _xmlPrefix.str() + "initial", - _xmlPrefix.str() + "final", - _xmlPrefix.str() + "history" - }, _scxml); - - _states.resize(tmp.size()); - - for (i = 0; i < _states.size(); i++) { - _states[i] = new State(i); - _states[i]->element = tmp.front(); - _states[i]->element->setUserData(X("uscxmlState"), _states[i], NULL); - tmp.pop_front(); - } - assert(tmp.size() == 0); - - if (_binding == Binding::EARLY && _states.size() > 0) { - // add all data elements to the first state - std::list dataModels = DOMUtils::filterChildElements(_xmlPrefix.str() + "datamodel", _states[0]->element, true); - dataModels.erase(std::remove_if(dataModels.begin(), - dataModels.end(), - [this](DOMElement* elem) { - return !areFromSameMachine(elem, _scxml); - }), - dataModels.end()); - - std::list dataList = DOMUtils::filterChildElements(_xmlPrefix.str() + "data", dataModels, false); - _states[0]->data = { std::make_move_iterator(std::begin(dataList)), std::make_move_iterator(std::end(dataList))}; - } - - for (i = 0; i < _states.size(); i++) { - // collect states with an id attribute - if (HAS_ATTR(_states[i]->element, kXMLCharId)) { - stateIds.insert(ATTR(_states[i]->element, kXMLCharId)); - } - - // TODO: Reserve space for ancestors? => Measure performance! - - // check for executable content and datamodels - if (_states[i]->element->getChildElementCount() > 0) { - // not every child element will be a child state, but we can shrink later - _states[i]->children.reserve(_states[i]->element->getChildElementCount()); - - std::list entryList = DOMUtils::filterChildElements(_xmlPrefix.str() + "onentry", _states[i]->element); - std::list exitList = DOMUtils::filterChildElements(_xmlPrefix.str() + "onexit", _states[i]->element); - std::list invokeList = DOMUtils::filterChildElements(_xmlPrefix.str() + "invoke", _states[i]->element); - _states[i]->onEntry = { std::make_move_iterator(std::begin(entryList)), std::make_move_iterator(std::end(entryList))}; - _states[i]->onExit = { std::make_move_iterator(std::begin(exitList)), std::make_move_iterator(std::end(exitList))}; - _states[i]->invoke = { std::make_move_iterator(std::begin(invokeList)), std::make_move_iterator(std::end(invokeList))}; - - if (i == 0) { - // have global scripts as onentry of - std::list scriptList = DOMUtils::filterChildElements(_xmlPrefix.str() + "script", _states[i]->element, false); - _states[i]->onEntry = { std::make_move_iterator(std::begin(scriptList)), std::make_move_iterator(std::end(scriptList))}; - } - - std::list doneDatas = DOMUtils::filterChildElements(_xmlPrefix.str() + "donedata", _states[i]->element); - if (doneDatas.size() > 0) { - _states[i]->doneData = doneDatas.front(); - } - - if (_binding == Binding::LATE) { - std::list dataModels = DOMUtils::filterChildElements(_xmlPrefix.str() + "datamodel", _states[i]->element); - if (dataModels.size() > 0) { - std::list dataList = DOMUtils::filterChildElements(_xmlPrefix.str() + "data", dataModels, false); - _states[i]->data = { std::make_move_iterator(std::begin(dataList)), std::make_move_iterator(std::end(dataList))}; - } - } - - } - - // set the states type - if (false) { - } else if (iequals(TAGNAME(_states[i]->element), _xmlPrefix.str() + "initial")) { - _states[i]->type = USCXML_STATE_INITIAL; - } else if (isFinal(_states[i]->element)) { - _states[i]->type = USCXML_STATE_FINAL; - } else if (isHistory(_states[i]->element)) { - if (HAS_ATTR(_states[i]->element, kXMLCharType) && iequals(ATTR(_states[i]->element, kXMLCharType), "deep")) { - _states[i]->type = USCXML_STATE_HISTORY_DEEP; - } else { - _states[i]->type = USCXML_STATE_HISTORY_SHALLOW; - } - } else if (isAtomic(_states[i]->element)) { - _states[i]->type = USCXML_STATE_ATOMIC; - } else if (isParallel(_states[i]->element)) { - _states[i]->type = USCXML_STATE_PARALLEL; - } else { // and any other state - _states[i]->type = USCXML_STATE_COMPOUND; - } - - // establish the states' completion - std::list completionList = getCompletion(_states[i]->element); - for (j = 0; completionList.size() > 0; j++) { - _states[i]->completion.insert((State*)completionList.front()->getUserData(X("uscxmlState"))); - completionList.pop_front(); - } - assert(completionList.size() == 0); - - // this is set when establishing the completion - if (_states[i]->element->getUserData(X("hasHistoryChild")) == _states[i]) { - _states[i]->type |= USCXML_STATE_HAS_HISTORY; - } - - // set the states parent and add us as a children - DOMNode* parent = _states[i]->element->getParentNode(); - if (parent && parent->getNodeType() == DOMNode::ELEMENT_NODE) { - _states[i]->parent = (State*)parent->getUserData(X("uscxmlState")); - if (_states[i]->parent != NULL) { - _states[i]->parent->children.push_back(_states[i]); - _states[i]->ancestors.insert(_states[i]->parent); - _states[i]->ancestors.insert(_states[i]->parent->ancestors.begin(), _states[i]->parent->ancestors.end()); - } - } - } - - /** -- All things transitions -- */ - - tmp = DOMUtils::inPostFixOrder({ - XML_PREFIX(_scxml).str() + "scxml", - XML_PREFIX(_scxml).str() + "state", - XML_PREFIX(_scxml).str() + "final", - XML_PREFIX(_scxml).str() + "history", - XML_PREFIX(_scxml).str() + "initial", - XML_PREFIX(_scxml).str() + "parallel" - }, _scxml); - tmp = DOMUtils::filterChildElements(XML_PREFIX(_scxml).str() + "transition", tmp); - - _transitions.resize(tmp.size()); - _conflicting.resize(tmp.size()); - _compatible.resize(tmp.size()); - - for (i = 0; i < _transitions.size(); i++) { - _transitions[i] = new Transition(i); - _transitions[i]->element = tmp.front(); - _transitions[i]->element->setUserData(X("uscxmlTrans"), _transitions[i], NULL); - tmp.pop_front(); - } - assert(tmp.size() == 0); - - - for (i = 0; i < _transitions.size(); i++) { - // establish the transitions' target set - { - std::list targets = tokenize(ATTR(_transitions[i]->element, kXMLCharTarget)); - _transitions[i]->target.reserve(targets.size()); - - for (auto tIter = targets.begin(); tIter != targets.end(); tIter++) { - if (stateIds.find(*tIter) != stateIds.end()) { - _transitions[i]->target.push_back((State*)getState(*tIter, _scxml)->getUserData(X("uscxmlState"))); - } - } - } - - // the transition's type - if (!HAS_ATTR(_transitions[i]->element, kXMLCharTarget)) { - _transitions[i]->type |= USCXML_TRANS_TARGETLESS; - } - - if (HAS_ATTR(_transitions[i]->element, kXMLCharType) && iequals(ATTR(_transitions[i]->element, kXMLCharType), "internal")) { - _transitions[i]->type |= USCXML_TRANS_INTERNAL; - } - - if (!HAS_ATTR(_transitions[i]->element, kXMLCharEvent)) { - _transitions[i]->type |= USCXML_TRANS_SPONTANEOUS; - } - - if (iequals(TAGNAME_CAST(_transitions[i]->element->getParentNode()), _xmlPrefix.str() + "history")) { - _transitions[i]->type |= USCXML_TRANS_HISTORY; - } - - if (iequals(TAGNAME_CAST(_transitions[i]->element->getParentNode()), _xmlPrefix.str() + "initial")) { - _transitions[i]->type |= USCXML_TRANS_INITIAL; - } - - // the transitions event and condition - _transitions[i]->event = (HAS_ATTR(_transitions[i]->element, kXMLCharEvent) ? - ATTR(_transitions[i]->element, kXMLCharEvent) : ""); - _transitions[i]->cond = (HAS_ATTR(_transitions[i]->element, kXMLCharCond) ? - ATTR(_transitions[i]->element, kXMLCharCond) : ""); - - // is there executable content? - if (_transitions[i]->element->getChildElementCount() > 0) { - _transitions[i]->onTrans = _transitions[i]->element; - } - - } - - /* Connect states and transitions */ - for (auto state : _states) { - std::list transList = DOMUtils::filterChildElements(_xmlPrefix.str() + "transition", state->element); - if (transList.size() > 0) { - state->transitions.resize(transList.size()); - for (auto i = 0; transList.size() > 0; i++) { - auto trans = transList.front(); - transList.pop_front(); - Transition* uscxmlTrans = ((Transition*)trans->getUserData(X("uscxmlTrans"))); - uscxmlTrans->source = state; - // save some memory? => Measure performance! + _scxml = scxml; + _binding = (HAS_ATTR(_scxml, kXMLCharBinding) && iequals(ATTR(_scxml, kXMLCharBinding), "late") ? LATE : EARLY); + _xmlPrefix = _scxml->getPrefix(); + _xmlNS = _scxml->getNamespaceURI(); + if (_xmlPrefix) { + _xmlPrefix = std::string(_xmlPrefix) + ":"; + } + + { + BENCHMARK("init resort states") + resortStates(_scxml, _xmlPrefix); + } + + std::set stateIds; + + /** -- All things states -- */ + + std::list tmp; + size_t i, j; + + tmp = DOMUtils::inDocumentOrder({ + _xmlPrefix.str() + "state", + _xmlPrefix.str() + "parallel", + _xmlPrefix.str() + "scxml", + _xmlPrefix.str() + "initial", + _xmlPrefix.str() + "final", + _xmlPrefix.str() + "history" + }, _scxml); + + _states.resize(tmp.size()); + + for (i = 0; i < _states.size(); i++) { + _states[i] = new State(i); + _states[i]->element = tmp.front(); + if (HAS_ATTR(_states[i]->element, kXMLCharId)) { + _states[i]->name = ATTR(_states[i]->element, kXMLCharId); + } + _states[i]->element->setUserData(X("uscxmlState"), _states[i], NULL); + tmp.pop_front(); + } + assert(tmp.size() == 0); + + if (_binding == Binding::EARLY && _states.size() > 0) { + // add all data elements to the first state + std::list dataModels = DOMUtils::filterChildElements(_xmlPrefix.str() + "datamodel", _states[0]->element, true); + dataModels.erase(std::remove_if(dataModels.begin(), + dataModels.end(), + [this](DOMElement* elem) { + return !areFromSameMachine(elem, _scxml); + }), + dataModels.end()); + + std::list dataList = DOMUtils::filterChildElements(_xmlPrefix.str() + "data", dataModels, false); + _states[0]->data = { std::make_move_iterator(std::begin(dataList)), std::make_move_iterator(std::end(dataList))}; + } + + for (i = 0; i < _states.size(); i++) { + // collect states with an id attribute + if (HAS_ATTR(_states[i]->element, kXMLCharId)) { + stateIds.insert(ATTR(_states[i]->element, kXMLCharId)); + } + + // TODO: Reserve space for ancestors? => Measure performance! + + // check for executable content and datamodels + if (_states[i]->element->getChildElementCount() > 0) { + // not every child element will be a child state, but we can shrink later + _states[i]->children.reserve(_states[i]->element->getChildElementCount()); + + std::list entryList = DOMUtils::filterChildElements(_xmlPrefix.str() + "onentry", _states[i]->element); + std::list exitList = DOMUtils::filterChildElements(_xmlPrefix.str() + "onexit", _states[i]->element); + std::list invokeList = DOMUtils::filterChildElements(_xmlPrefix.str() + "invoke", _states[i]->element); + _states[i]->onEntry = { std::make_move_iterator(std::begin(entryList)), std::make_move_iterator(std::end(entryList))}; + _states[i]->onExit = { std::make_move_iterator(std::begin(exitList)), std::make_move_iterator(std::end(exitList))}; + _states[i]->invoke = { std::make_move_iterator(std::begin(invokeList)), std::make_move_iterator(std::end(invokeList))}; + + if (i == 0) { + // have global scripts as onentry of + std::list scriptList = DOMUtils::filterChildElements(_xmlPrefix.str() + "script", _states[i]->element, false); + _states[i]->onEntry = { std::make_move_iterator(std::begin(scriptList)), std::make_move_iterator(std::end(scriptList))}; + } + + std::list doneDatas = DOMUtils::filterChildElements(_xmlPrefix.str() + "donedata", _states[i]->element); + if (doneDatas.size() > 0) { + _states[i]->doneData = doneDatas.front(); + } + + if (_binding == Binding::LATE) { + std::list dataModels = DOMUtils::filterChildElements(_xmlPrefix.str() + "datamodel", _states[i]->element); + if (dataModels.size() > 0) { + std::list dataList = DOMUtils::filterChildElements(_xmlPrefix.str() + "data", dataModels, false); + _states[i]->data = { std::make_move_iterator(std::begin(dataList)), std::make_move_iterator(std::end(dataList))}; + } + } + + } + + // set the states type + if (false) { + } else if (iequals(TAGNAME(_states[i]->element), _xmlPrefix.str() + "initial")) { + _states[i]->type = USCXML_STATE_INITIAL; + } else if (isFinal(_states[i]->element)) { + _states[i]->type = USCXML_STATE_FINAL; + } else if (isHistory(_states[i]->element)) { + if (HAS_ATTR(_states[i]->element, kXMLCharType) && iequals(ATTR(_states[i]->element, kXMLCharType), "deep")) { + _states[i]->type = USCXML_STATE_HISTORY_DEEP; + } else { + _states[i]->type = USCXML_STATE_HISTORY_SHALLOW; + } + } else if (isAtomic(_states[i]->element)) { + _states[i]->type = USCXML_STATE_ATOMIC; + } else if (isParallel(_states[i]->element)) { + _states[i]->type = USCXML_STATE_PARALLEL; + } else { // and any other state + _states[i]->type = USCXML_STATE_COMPOUND; + } + + // establish the states' completion + std::list completionList = getCompletion(_states[i]->element); + for (j = 0; completionList.size() > 0; j++) { + _states[i]->completion.insert((State*)completionList.front()->getUserData(X("uscxmlState"))); + completionList.pop_front(); + } + assert(completionList.size() == 0); + + // this is set when establishing the completion + if (_states[i]->element->getUserData(X("hasHistoryChild")) == _states[i]) { + _states[i]->type |= USCXML_STATE_HAS_HISTORY; + } + + // set the states parent and add us as a children + DOMNode* parent = _states[i]->element->getParentNode(); + if (parent && parent->getNodeType() == DOMNode::ELEMENT_NODE) { + _states[i]->parent = (State*)parent->getUserData(X("uscxmlState")); + if (_states[i]->parent != NULL) { + _states[i]->parent->children.push_back(_states[i]); + _states[i]->ancestors.insert(_states[i]->parent); + _states[i]->ancestors.insert(_states[i]->parent->ancestors.begin(), _states[i]->parent->ancestors.end()); + } + } + } + + /** -- All things transitions -- */ + + tmp = DOMUtils::inPostFixOrder({ + XML_PREFIX(_scxml).str() + "scxml", + XML_PREFIX(_scxml).str() + "state", + XML_PREFIX(_scxml).str() + "final", + XML_PREFIX(_scxml).str() + "history", + XML_PREFIX(_scxml).str() + "initial", + XML_PREFIX(_scxml).str() + "parallel" + }, _scxml); + tmp = DOMUtils::filterChildElements(XML_PREFIX(_scxml).str() + "transition", tmp); + + _transitions.resize(tmp.size()); + _conflicting.resize(tmp.size()); + _compatible.resize(tmp.size()); + + for (i = 0; i < _transitions.size(); i++) { + _transitions[i] = new Transition(i); + _transitions[i]->element = tmp.front(); + _transitions[i]->element->setUserData(X("uscxmlTrans"), _transitions[i], NULL); + tmp.pop_front(); + } + assert(tmp.size() == 0); + + + for (i = 0; i < _transitions.size(); i++) { + // establish the transitions' target set + { + std::list targets = tokenize(ATTR(_transitions[i]->element, kXMLCharTarget)); + _transitions[i]->target.reserve(targets.size()); + + for (auto tIter = targets.begin(); tIter != targets.end(); tIter++) { + if (stateIds.find(*tIter) != stateIds.end()) { + _transitions[i]->target.push_back((State*)getState(*tIter, _scxml)->getUserData(X("uscxmlState"))); + } + } + } + + // the transition's type + if (!HAS_ATTR(_transitions[i]->element, kXMLCharTarget)) { + _transitions[i]->type |= USCXML_TRANS_TARGETLESS; + } + + if (HAS_ATTR(_transitions[i]->element, kXMLCharType) && iequals(ATTR(_transitions[i]->element, kXMLCharType), "internal")) { + _transitions[i]->type |= USCXML_TRANS_INTERNAL; + } + + if (!HAS_ATTR(_transitions[i]->element, kXMLCharEvent)) { + _transitions[i]->type |= USCXML_TRANS_SPONTANEOUS; + } + + if (iequals(TAGNAME_CAST(_transitions[i]->element->getParentNode()), _xmlPrefix.str() + "history")) { + _transitions[i]->type |= USCXML_TRANS_HISTORY; + } + + if (iequals(TAGNAME_CAST(_transitions[i]->element->getParentNode()), _xmlPrefix.str() + "initial")) { + _transitions[i]->type |= USCXML_TRANS_INITIAL; + } + + // the transitions event and condition + _transitions[i]->event = (HAS_ATTR(_transitions[i]->element, kXMLCharEvent) ? + ATTR(_transitions[i]->element, kXMLCharEvent) : ""); + _transitions[i]->cond = (HAS_ATTR(_transitions[i]->element, kXMLCharCond) ? + ATTR(_transitions[i]->element, kXMLCharCond) : ""); + + // is there executable content? + if (_transitions[i]->element->getChildElementCount() > 0) { + _transitions[i]->onTrans = _transitions[i]->element; + } + + } + + /* Connect states and transitions */ + for (auto state : _states) { + std::list transList = DOMUtils::filterChildElements(_xmlPrefix.str() + "transition", state->element); + if (transList.size() > 0) { + state->transitions.resize(transList.size()); + for (auto i = 0; transList.size() > 0; i++) { + auto trans = transList.front(); + transList.pop_front(); + Transition* uscxmlTrans = ((Transition*)trans->getUserData(X("uscxmlTrans"))); + uscxmlTrans->source = state; + // save some memory? => Measure performance! // uscxmlTrans->compatible.shrink_to_fit(); // uscxmlTrans->exitSet.shrink_to_fit(); - - state->transitions[i] = uscxmlTrans; - } - // we need the postfix order for iterating transitions of active states - state->postFixOrder = (*(state->transitions.begin()))->postFixOrder; - } else { - state->postFixOrder = std::numeric_limits::max(); - } - - // save some memory? => Measure performance! + + state->transitions[i] = uscxmlTrans; + } + // we need the postfix order for iterating transitions of active states + state->postFixOrder = (*(state->transitions.begin()))->postFixOrder; + } else { + state->postFixOrder = std::numeric_limits::max(); + } + + // save some memory? => Measure performance! // state->ancestors.shrink_to_fit(); - state->children.shrink_to_fit(); + state->children.shrink_to_fit(); // state->completion.shrink_to_fit(); - - assert(transList.size() == 0); - } - - _isInitialized = true; + + assert(transList.size() == 0); + } + + _isInitialized = true; } InterpreterState LargeMicroStep::step(size_t blockMs) { - if (!_isInitialized) { - init(_scxml); - return USCXML_INITIALIZED; - } - - std::set monitors = _callbacks->getMonitors(); - - _exitSet.clear(); - _entrySet.clear(); - _targetSet.clear(); - _tmpStates.clear(); - - _compatible.clear(); - _conflicting.clear(); - _transSet.clear(); - - if (_flags & USCXML_CTX_FINISHED) - return USCXML_FINISHED; - - if (_flags & USCXML_CTX_TOP_LEVEL_FINAL) { - USCXML_MONITOR_CALLBACK(monitors, beforeCompletion); - - /* exit all remaining states */ - for (auto stateIter = _configuration.end() ; stateIter != _configuration.begin() ; /* Do nothing */ ) { - --stateIter; - /* call all on exit handlers */ - for (auto onExit : (*stateIter)->onExit) { - try { - _callbacks->process(onExit); - } catch (...) { - // do nothing and continue with next block - } - } - if (_invocations.find(*stateIter) != _invocations.end()) { - /* cancel all invokers */ - for (auto invoke : (*stateIter)->invoke) { - _callbacks->uninvoke(invoke); - } - _invocations.clear(); - } - } - - _flags |= USCXML_CTX_FINISHED; - - USCXML_MONITOR_CALLBACK(monitors, afterCompletion); - - return USCXML_FINISHED; - } - - - if (_flags == USCXML_CTX_PRISTINE) { - // TODO: Is this working? - _targetSet = _states.front()->completion; - _flags |= USCXML_CTX_SPONTANEOUS | USCXML_CTX_INITIALIZED; - USCXML_MONITOR_CALLBACK(monitors, beforeMicroStep); - - goto ESTABLISH_ENTRYSET; - } - - if (_flags & USCXML_CTX_SPONTANEOUS) { - _event = Event(); - goto SELECT_TRANSITIONS; - } - - - if ((_event = _callbacks->dequeueInternal())) { - USCXML_MONITOR_CALLBACK1(monitors, beforeProcessingEvent, _event); - goto SELECT_TRANSITIONS; - } - - /* manage uninvocations */ - for (auto invokeIter = _invocations.begin(); invokeIter != _invocations.end();) { - if (_configuration.find(*invokeIter) == _configuration.end()) { - /* uninvoke */ - for (auto invoke : (*invokeIter)->invoke) { - _callbacks->uninvoke(invoke); - } - invokeIter = _invocations.erase(invokeIter); - } else { - invokeIter++; - } - } - - /* manage invocations */ - for (auto config : _configuration) { - if (_invocations.find(config) == _invocations.end()) { - for (auto invoke : config->invoke) { - try { - /* invoke */ - _callbacks->invoke(invoke); - } catch (ErrorEvent e) { - LOG(_callbacks->getLogger(), USCXML_WARN) << e; - // TODO: Shall we deliver the event into the interpreter runtime? - } catch (...) { - } - } - } - _invocations.insert(config); - } - - // we dequeued all internal events and ought to signal stable configuration - if (!(_flags & USCXML_CTX_STABLE)) { - USCXML_MONITOR_CALLBACK(monitors, onStableConfiguration); - _microstepConfigurations.clear(); - _flags |= USCXML_CTX_STABLE; - return USCXML_MACROSTEPPED; - } - - if ((_event = _callbacks->dequeueExternal(blockMs))) { - USCXML_MONITOR_CALLBACK1(monitors, beforeProcessingEvent, _event); - goto SELECT_TRANSITIONS; - } - - if (_isCancelled) { - // finalize and exit - _flags |= USCXML_CTX_TOP_LEVEL_FINAL; - return USCXML_CANCELLED; - } - - // if (blocking) // we received the empty event to unblock - // return USCXML_IDLE; // we return IDLE nevertheless - - return USCXML_IDLE; - + if (!_isInitialized) { + init(_scxml); + return USCXML_INITIALIZED; + } + + std::set monitors = _callbacks->getMonitors(); + + _exitSet.clear(); + _entrySet.clear(); + _targetSet.clear(); + _tmpStates.clear(); + + _compatible.clear(); + _conflicting.clear(); + _transSet.clear(); + + if (_flags & USCXML_CTX_FINISHED) + return USCXML_FINISHED; + + if (_flags & USCXML_CTX_TOP_LEVEL_FINAL) { + USCXML_MONITOR_CALLBACK(monitors, beforeCompletion); + + /* exit all remaining states */ + for (auto stateIter = _configuration.end() ; stateIter != _configuration.begin() ; /* Do nothing */ ) { + --stateIter; + /* call all on exit handlers */ + for (auto onExit : (*stateIter)->onExit) { + try { + _callbacks->process(onExit); + } catch (...) { + // do nothing and continue with next block + } + } + if (_invocations.find(*stateIter) != _invocations.end()) { + /* cancel all invokers */ + for (auto invoke : (*stateIter)->invoke) { + _callbacks->uninvoke(invoke); + } + _invocations.clear(); + } + } + + _flags |= USCXML_CTX_FINISHED; + + USCXML_MONITOR_CALLBACK(monitors, afterCompletion); + + return USCXML_FINISHED; + } + + + if (_flags == USCXML_CTX_PRISTINE) { + // TODO: Is this working? + _targetSet = _states.front()->completion; + _flags |= USCXML_CTX_SPONTANEOUS | USCXML_CTX_INITIALIZED; + USCXML_MONITOR_CALLBACK(monitors, beforeMicroStep); + + goto ESTABLISH_ENTRYSET; + } + + if (_flags & USCXML_CTX_SPONTANEOUS) { + _event = Event(); + goto SELECT_TRANSITIONS; + } + + + if ((_event = _callbacks->dequeueInternal())) { + USCXML_MONITOR_CALLBACK1(monitors, beforeProcessingEvent, _event); + goto SELECT_TRANSITIONS; + } + + /* manage uninvocations */ + for (auto invokeIter = _invocations.begin(); invokeIter != _invocations.end();) { + if (_configuration.find(*invokeIter) == _configuration.end()) { + /* uninvoke */ + for (auto invoke : (*invokeIter)->invoke) { + _callbacks->uninvoke(invoke); + } + invokeIter = _invocations.erase(invokeIter); + } else { + invokeIter++; + } + } + + /* manage invocations */ + for (auto config : _configuration) { + if (_invocations.find(config) == _invocations.end()) { + for (auto invoke : config->invoke) { + try { + /* invoke */ + _callbacks->invoke(invoke); + } catch (ErrorEvent e) { + LOG(_callbacks->getLogger(), USCXML_WARN) << e; + // TODO: Shall we deliver the event into the interpreter runtime? + } catch (...) { + } + } + } + _invocations.insert(config); + } + + // we dequeued all internal events and ought to signal stable configuration + if (!(_flags & USCXML_CTX_STABLE)) { + USCXML_MONITOR_CALLBACK(monitors, onStableConfiguration); + _microstepConfigurations.clear(); + _flags |= USCXML_CTX_STABLE; + return USCXML_MACROSTEPPED; + } + + if ((_event = _callbacks->dequeueExternal(blockMs))) { + USCXML_MONITOR_CALLBACK1(monitors, beforeProcessingEvent, _event); + goto SELECT_TRANSITIONS; + } + + if (_isCancelled) { + // finalize and exit + _flags |= USCXML_CTX_TOP_LEVEL_FINAL; + return USCXML_CANCELLED; + } + + // if (blocking) // we received the empty event to unblock + // return USCXML_IDLE; // we return IDLE nevertheless + + return USCXML_IDLE; + SELECT_TRANSITIONS: - // we read an event - unset stable to signal onstable again later - _flags &= ~USCXML_CTX_STABLE; - - { - BENCHMARK("select transitions"); - - // iterate active states in postfix order and find transitions - for (auto stateIter = _configurationPostFix.begin(); stateIter != _configurationPostFix.end();) { - State* state = *stateIter++; + // we read an event - unset stable to signal onstable again later + _flags &= ~USCXML_CTX_STABLE; + + { + BENCHMARK("select transitions"); + + // iterate active states in postfix order and find transitions + for (auto stateIter = _configurationPostFix.begin(); stateIter != _configurationPostFix.end();) { + State* state = *stateIter++; // std::cout << (HAS_ATTR(state->element, kXMLCharId) ? ATTR(state->element, kXMLCharId) : "?"); // std::cout << ": " << state->documentOrder << " - " << state->postFixOrder << std::endl; - - for (auto transIter = state->transitions.begin(); transIter != state->transitions.end();) { - Transition* transition = *transIter++; - - /* never select history or initial transitions automatically */ - if unlikely(transition->type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) - continue; - - /* is it spontaneous with an event or vice versa? */ - if ((transition->event.size() == 0 && _event) || - (transition->event.size() != 0 && !_event)) - continue; - - /* check whether it is explicitly conflicting or compatible, calculate if neither */ - if (_flags & USCXML_CTX_TRANSITION_FOUND) { - BENCHMARK("select transitions conflict & compatible calc"); - - if (_conflicting[transition->postFixOrder]) { - // this transition is explicitly conflicting - continue; - } - if (!_compatible[transition->postFixOrder]) { - // it is not explicitly compatible, we know nothing! - BENCHMARK("select transitions conflict & compatible calc no entry"); - - bool conflicts = false; - for (auto enabledTrans : _transSet) { - if (enabledTrans->compatible.find(transition->postFixOrder) != enabledTrans->compatible.end() || - (enabledTrans->conflicting.find(transition->postFixOrder) != enabledTrans->conflicting.end())) { - continue; - } - - std::pair exit1 = getExitSet(transition); - std::pair exit2 = getExitSet(enabledTrans); - - if (exit1.first != 0 && exit2.first != 0 && // empty domain - ((exit1.first <= exit2.first && exit1.second >= exit2.first) || - (exit2.first <= exit1.first && exit2.second >= exit1.first))) { - // it is conflicting! + + for (auto transIter = state->transitions.begin(); transIter != state->transitions.end();) { + Transition* transition = *transIter++; + + /* never select history or initial transitions automatically */ + if unlikely(transition->type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) + continue; + + /* is it spontaneous with an event or vice versa? */ + if ((transition->event.size() == 0 && _event) || + (transition->event.size() != 0 && !_event)) + continue; + + /* check whether it is explicitly conflicting or compatible, calculate if neither */ + if (_flags & USCXML_CTX_TRANSITION_FOUND) { + BENCHMARK("select transitions conflict & compatible calc"); + + if (_conflicting[transition->postFixOrder]) { + // this transition is explicitly conflicting + continue; + } + if (!_compatible[transition->postFixOrder]) { + // it is not explicitly compatible, we know nothing! + BENCHMARK("select transitions conflict & compatible calc no entry"); + + bool conflicts = false; + for (auto enabledTrans : _transSet) { + if (enabledTrans->compatible.find(transition->postFixOrder) != enabledTrans->compatible.end() || + (enabledTrans->conflicting.find(transition->postFixOrder) != enabledTrans->conflicting.end())) { + continue; + } + + std::pair exit1 = getExitSet(transition); + std::pair exit2 = getExitSet(enabledTrans); + + if (exit1.first != 0 && exit2.first != 0 && // empty domain + ((exit1.first <= exit2.first && exit1.second >= exit2.first) || + (exit2.first <= exit1.first && exit2.second >= exit1.first))) { + // it is conflicting! // assert(uscxml::conflicts(t1, t2, _scxml)); - transition->conflicting.insert(enabledTrans->postFixOrder); - enabledTrans->conflicting.insert(transition->postFixOrder); - conflicts = true; - break; - } else { + transition->conflicting.insert(enabledTrans->postFixOrder); + enabledTrans->conflicting.insert(transition->postFixOrder); + conflicts = true; + break; + } else { // assert(!uscxml::conflicts(t1, t2, _scxml)); - transition->compatible.insert(enabledTrans->postFixOrder); - enabledTrans->compatible.insert(transition->postFixOrder); - } - } - if (conflicts) - continue; - } - } - - /* is it matched? */ - if (_event && !_callbacks->isMatched(_event, transition->event)) - continue; - - /* is it enabled? */ - if (transition->cond.size() > 0 && !_callbacks->isTrue(transition->cond)) - continue; - - // This transition is fine and ought to be taken! - - /* update conflicting and compatible transitions */ - if (_flags & USCXML_CTX_TRANSITION_FOUND) { - BENCHMARK("select transitions conflict & compatible update"); - - /* remove all compatible transitions not listed in ours */ - size_t i = _compatible.find_first(); - while(i != boost::dynamic_bitset::npos) { - if (transition->compatible.find(i) == transition->compatible.end()) { - _compatible[i] = false; - } - i = _compatible.find_next(i); - } - - /* add all conflicting transitions listed in ours */ - for (auto conflict : transition->conflicting) { - _conflicting[conflict] = true; - } - - } else { - /* Very first transition added to optimally transition set */ - for (auto compatible : transition->compatible) { - _compatible[compatible] = true; - } - for (auto conflict : transition->conflicting) { - _conflicting[conflict] = true; - } - } - - /* remember that we found a transition */ - _flags |= USCXML_CTX_TRANSITION_FOUND; - - /* states that are directly targeted (complete as entry-set later) */ - _targetSet.insert(transition->target.begin(), transition->target.end()); - - /* lazily initialize exit set */ - if (transition->exitSet.first == 0 && transition->exitSet.second == 0) { + transition->compatible.insert(enabledTrans->postFixOrder); + enabledTrans->compatible.insert(transition->postFixOrder); + } + } + if (conflicts) + continue; + } + } + + /* is it matched? */ + if (_event && !_callbacks->isMatched(_event, transition->event)) + continue; + + /* is it enabled? */ + if (transition->cond.size() > 0 && !_callbacks->isTrue(transition->cond)) + continue; + + // This transition is fine and ought to be taken! + + /* update conflicting and compatible transitions */ + if (_flags & USCXML_CTX_TRANSITION_FOUND) { + BENCHMARK("select transitions conflict & compatible update"); + + /* remove all compatible transitions not listed in ours */ + size_t i = _compatible.find_first(); + while(i != boost::dynamic_bitset::npos) { + if (transition->compatible.find(i) == transition->compatible.end()) { + _compatible[i] = false; + } + i = _compatible.find_next(i); + } + + /* add all conflicting transitions listed in ours */ + for (auto conflict : transition->conflicting) { + _conflicting[conflict] = true; + } + + } else { + /* Very first transition added to optimally transition set */ + for (auto compatible : transition->compatible) { + _compatible[compatible] = true; + } + for (auto conflict : transition->conflicting) { + _conflicting[conflict] = true; + } + } + + /* remember that we found a transition */ + _flags |= USCXML_CTX_TRANSITION_FOUND; + + /* states that are directly targeted (complete as entry-set later) */ + _targetSet.insert(transition->target.begin(), transition->target.end()); + + /* lazily initialize exit set */ + if (transition->exitSet.first == 0 && transition->exitSet.second == 0) { // DOMElement* tmp = getTransitionDomain(transition->element, _scxml); // State* domain1 = tmp == NULL ? NULL : (State*)tmp->getUserData(X("uscxmlState")); // uint32_t domain2 = getTransitionDomain(transition); // assert(domain1 == NULL ? domain2 == std::numeric_limits::max() : domain1->documentOrder == domain2); - + // std::pair orig = getExitSetCached(transition->element, _scxml); - transition->exitSet = getExitSet(transition); + transition->exitSet = getExitSet(transition); // assert(transition->exitSet == orig); - } - - /* states that will be left */ - for (auto config : _configuration) { - if (config->documentOrder >= transition->exitSet.first && config->documentOrder <= transition->exitSet.second) { - _exitSet.insert(config); - } - } - _transSet.insert(transition); - - // break and exit loop if we are at the end - if (stateIter == _configurationPostFix.end()) - break; - - /* move to next possible state that can have optimally enabled transitions */ - auto nextIter = stateIter; - nextIter++; // advance by one - while(nextIter != _configurationPostFix.end() && *nextIter == (*stateIter)->parent) { - // advance until we found a non-ancestor - nextIter++; - stateIter++; - } - - break; // next state - } - } + } + + /* states that will be left */ + for (auto config : _configuration) { + if (config->documentOrder >= transition->exitSet.first && config->documentOrder <= transition->exitSet.second) { + _exitSet.insert(config); + } + } + _transSet.insert(transition); + + // break and exit loop if we are at the end + if (stateIter == _configurationPostFix.end()) + break; + + /* move to next possible state that can have optimally enabled transitions */ + auto nextIter = stateIter; + nextIter++; // advance by one + while(nextIter != _configurationPostFix.end() && *nextIter == (*stateIter)->parent) { + // advance until we found a non-ancestor + nextIter++; + stateIter++; + } + + break; // next state + } + } // std::cout << "." << iters << std::flush; - } - - if (_flags & USCXML_CTX_TRANSITION_FOUND) { - // trigger more sppontaneuous transitions - _flags |= USCXML_CTX_SPONTANEOUS; - _flags &= ~USCXML_CTX_TRANSITION_FOUND; - } else { - // spontaneuous transitions are exhausted and we will attempt to dequeue an internal event next round - _flags &= ~USCXML_CTX_SPONTANEOUS; - return USCXML_MICROSTEPPED; - } - - USCXML_MONITOR_CALLBACK(monitors, beforeMicroStep); - + } + + if (_flags & USCXML_CTX_TRANSITION_FOUND) { + // trigger more sppontaneuous transitions + _flags |= USCXML_CTX_SPONTANEOUS; + _flags &= ~USCXML_CTX_TRANSITION_FOUND; + } else { + // spontaneuous transitions are exhausted and we will attempt to dequeue an internal event next round + _flags &= ~USCXML_CTX_SPONTANEOUS; + return USCXML_MICROSTEPPED; + } + + USCXML_MONITOR_CALLBACK(monitors, beforeMicroStep); + #ifdef USCXML_VERBOSE - std::cerr << "Targets: "; - printStateNames(_targetSet); + std::cerr << "Targets: "; + printStateNames(_targetSet); #endif - + #ifdef USCXML_VERBOSE - std::cerr << "ExitSet: "; - printStateNames(_exitSet); + std::cerr << "ExitSet: "; + printStateNames(_exitSet); #endif - - - /* REMEMBER_HISTORY: */ - { - BENCHMARK("remember history"); - - for (auto state : _states) { - if likely(USCXML_STATE_MASK(state->type) != USCXML_STATE_HISTORY_SHALLOW && - USCXML_STATE_MASK(state->type) != USCXML_STATE_HISTORY_DEEP) - continue; - - if likely(_exitSet.find(state->parent) == _exitSet.end()) - continue; - - /* a history state whose parent is about to be exited */ - for (auto completion : state->completion) { - if (_configuration.find(completion) != _configuration.end()) { - _history.insert(completion); - } else { - _history.erase(completion); - } - } - } - } - + + + /* REMEMBER_HISTORY: */ + { + BENCHMARK("remember history"); + + for (auto state : _states) { + if likely(USCXML_STATE_MASK(state->type) != USCXML_STATE_HISTORY_SHALLOW && + USCXML_STATE_MASK(state->type) != USCXML_STATE_HISTORY_DEEP) + continue; + + if likely(_exitSet.find(state->parent) == _exitSet.end()) + continue; + + /* a history state whose parent is about to be exited */ + for (auto completion : state->completion) { + if (_configuration.find(completion) != _configuration.end()) { + _history.insert(completion); + } else { + _history.erase(completion); + } + } + } + } + #ifdef USCXML_VERBOSE - std::cerr << "History: "; - printStateNames(_history); + std::cerr << "History: "; + printStateNames(_history); #endif ESTABLISH_ENTRYSET: - /* calculate new entry set */ - _entrySet = _targetSet; - + /* calculate new entry set */ + _entrySet = _targetSet; + #ifdef USCXML_VERBOSE - std::cerr << "Targets: "; - printStateNames(_targetSet); + std::cerr << "Targets: "; + printStateNames(_targetSet); #endif - /* make sure iterators are not invalidated, TODO: we can know the actual maximum number by syntactic analysis */ - _entrySet.reserve(_states.size()); - - /* iterate for ancestors */ - { - BENCHMARK("add ancestors"); - // running from back to front allows us to add parents only due to document order - for (auto stateIter = _entrySet.end() ; stateIter != _entrySet.begin() ; /* Do nothing */ ) { - --stateIter; - if ((*stateIter)->parent) { - stateIter = ++(_entrySet.insert((*stateIter)->parent).first); - } - } - } - - /* iterate for descendants */ - { - BENCHMARK("add descendants"); - // we cannot use the simplified for loop as inserting will invalidate those iterators - for (auto stateIter = _entrySet.begin(); stateIter != _entrySet.end(); stateIter++ ) { - State* state = *stateIter; - - switch (USCXML_STATE_MASK(state->type)) { - case USCXML_STATE_FINAL: - case USCXML_STATE_ATOMIC: - break; - - case USCXML_STATE_PARALLEL: { - BENCHMARK("add descendants parallel"); - _entrySet.insert(state->completion.begin(), state->completion.end()); - break; - } - - case USCXML_STATE_HISTORY_SHALLOW: - case USCXML_STATE_HISTORY_DEEP: { - BENCHMARK("add descendants history"); - if (_configuration.find(state->parent) == _configuration.end() && - !intersects(state->completion.begin(), state->completion.end(), _history.begin(), _history.end())) { - - /* nothing set for history, look for a default transition */ - for (auto transition : state->transitions) { - _entrySet.insert(transition->target.begin(), transition->target.end()); - - if(USCXML_STATE_MASK(state->type) == USCXML_STATE_HISTORY_DEEP && - !intersects(transition->target.begin(), transition->target.end(), state->children.begin(), state->children.end())) { - - // add all the target's ancestors - for (auto target : transition->target) { + /* make sure iterators are not invalidated, TODO: we can know the actual maximum number by syntactic analysis */ + _entrySet.reserve(_states.size()); + + /* iterate for ancestors */ + { + BENCHMARK("add ancestors"); + // running from back to front allows us to add parents only due to document order + for (auto stateIter = _entrySet.end() ; stateIter != _entrySet.begin() ; /* Do nothing */ ) { + --stateIter; + if ((*stateIter)->parent) { + stateIter = ++(_entrySet.insert((*stateIter)->parent).first); + } + } + } + + /* iterate for descendants */ + { + BENCHMARK("add descendants"); + // we cannot use the simplified for loop as inserting will invalidate those iterators + for (auto stateIter = _entrySet.begin(); stateIter != _entrySet.end(); stateIter++ ) { + State* state = *stateIter; + + switch (USCXML_STATE_MASK(state->type)) { + case USCXML_STATE_FINAL: + case USCXML_STATE_ATOMIC: + break; + + case USCXML_STATE_PARALLEL: { + BENCHMARK("add descendants parallel"); + _entrySet.insert(state->completion.begin(), state->completion.end()); + break; + } + + case USCXML_STATE_HISTORY_SHALLOW: + case USCXML_STATE_HISTORY_DEEP: { + BENCHMARK("add descendants history"); + if (_configuration.find(state->parent) == _configuration.end() && + !intersects(state->completion.begin(), state->completion.end(), _history.begin(), _history.end())) { + + /* nothing set for history, look for a default transition */ + for (auto transition : state->transitions) { + _entrySet.insert(transition->target.begin(), transition->target.end()); + + if(USCXML_STATE_MASK(state->type) == USCXML_STATE_HISTORY_DEEP && + !intersects(transition->target.begin(), transition->target.end(), state->children.begin(), state->children.end())) { + + // add all the target's ancestors + for (auto target : transition->target) { #if 1 - _entrySet.insert(target->ancestors.begin(), target->ancestors.end()); + _entrySet.insert(target->ancestors.begin(), target->ancestors.end()); #else - State* anc = target->parent; - while(anc != NULL && _entrySet.find(anc) == _entrySet.end()) { - _entrySet.insert(anc); - anc = anc->parent; - } + State* anc = target->parent; + while(anc != NULL && _entrySet.find(anc) == _entrySet.end()) { + _entrySet.insert(anc); + anc = anc->parent; + } #endif - } - } - _transSet.insert(transition); - break; - } - } else { - _tmpStates = state->completion; - for (auto iter = _tmpStates.begin(); iter != _tmpStates.end();) { - if (_history.find(*iter) == _history.end()) { - iter = _tmpStates.erase(iter); - } else { - iter++; - } - } - _entrySet.insert(_tmpStates.begin(), _tmpStates.end()); - - if (state->type == (USCXML_STATE_HAS_HISTORY | USCXML_STATE_HISTORY_DEEP)) { - /* a deep history state with nested histories -> more completion */ - for (auto histState : state->completion) { - if (!(histState->type & USCXML_STATE_HAS_HISTORY)) - continue; - - if (_entrySet.find(histState) == _entrySet.end()) - continue; - - for (auto histChild : histState->children) { - if ((USCXML_STATE_MASK(histChild->type) == USCXML_STATE_HISTORY_DEEP || - USCXML_STATE_MASK(histChild->type) == USCXML_STATE_HISTORY_SHALLOW)) { - // We can leverage that deep history assignments are already completed and skip a few states in outer loop - stateIter = _entrySet.insert(histChild).first; - } - } - } - } - } - break; - } - - case USCXML_STATE_INITIAL: { - BENCHMARK("add descendants initial"); - for (auto transition : state->transitions) { - _transSet.insert(transition); // remember transition for onentry later - - for (auto target : transition->target) { - _entrySet.insert(target); - // add all states between target and this state + } + } + _transSet.insert(transition); + break; + } + } else { + _tmpStates = state->completion; + for (auto iter = _tmpStates.begin(); iter != _tmpStates.end();) { + if (_history.find(*iter) == _history.end()) { + iter = _tmpStates.erase(iter); + } else { + iter++; + } + } + _entrySet.insert(_tmpStates.begin(), _tmpStates.end()); + + if (state->type == (USCXML_STATE_HAS_HISTORY | USCXML_STATE_HISTORY_DEEP)) { + /* a deep history state with nested histories -> more completion */ + for (auto histState : state->completion) { + if (!(histState->type & USCXML_STATE_HAS_HISTORY)) + continue; + + if (_entrySet.find(histState) == _entrySet.end()) + continue; + + for (auto histChild : histState->children) { + if ((USCXML_STATE_MASK(histChild->type) == USCXML_STATE_HISTORY_DEEP || + USCXML_STATE_MASK(histChild->type) == USCXML_STATE_HISTORY_SHALLOW)) { + // We can leverage that deep history assignments are already completed and skip a few states in outer loop + stateIter = _entrySet.insert(histChild).first; + } + } + } + } + } + break; + } + + case USCXML_STATE_INITIAL: { + BENCHMARK("add descendants initial"); + for (auto transition : state->transitions) { + _transSet.insert(transition); // remember transition for onentry later + + for (auto target : transition->target) { + _entrySet.insert(target); + // add all states between target and this state #if 1 - _entrySet.insert(target->ancestors.begin(), target->ancestors.end()); + _entrySet.insert(target->ancestors.begin(), target->ancestors.end()); #else - State* anc = target->parent; - while(anc != NULL && anc != state) { - _entrySet.insert(anc); - anc = anc->parent; - } + State* anc = target->parent; + while(anc != NULL && anc != state) { + _entrySet.insert(anc); + anc = anc->parent; + } #endif - } - } - break; - } - - case USCXML_STATE_COMPOUND: { - BENCHMARK("add descendants compound"); - - /* Compound state may already be complete */ - { - BENCHMARK("add descendants compound intersect entry/child"); - for (auto child : state->children) { - /* one child is already in entry_set */ - if (_entrySet.find(child) != _entrySet.end()) - goto NEXT_DESCENDANT; - /* one child is already active and not left */ - if (_exitSet.find(child) == _exitSet.end() && _configuration.find(child) != _configuration.end()) - goto NEXT_DESCENDANT; - } - } - - // completion of a compound maybe multiple states via initial attribute - _entrySet.insert(state->completion.begin(), state->completion.end()); - - /* deep completion */ - { - BENCHMARK("add descendants compound deep completion"); - for (auto completion : state->completion) { - - if (std::binary_search(state->children.begin(), state->children.end(), completion)) - continue; - - // add deep completions ancestors + } + } + break; + } + + case USCXML_STATE_COMPOUND: { + BENCHMARK("add descendants compound"); + + /* Compound state may already be complete */ + { + BENCHMARK("add descendants compound intersect entry/child"); + for (auto child : state->children) { + /* one child is already in entry_set */ + if (_entrySet.find(child) != _entrySet.end()) + goto NEXT_DESCENDANT; + /* one child is already active and not left */ + if (_exitSet.find(child) == _exitSet.end() && _configuration.find(child) != _configuration.end()) + goto NEXT_DESCENDANT; + } + } + + // completion of a compound maybe multiple states via initial attribute + _entrySet.insert(state->completion.begin(), state->completion.end()); + + /* deep completion */ + { + BENCHMARK("add descendants compound deep completion"); + for (auto completion : state->completion) { + + if (std::binary_search(state->children.begin(), state->children.end(), completion)) + continue; + + // add deep completions ancestors #if 1 - _entrySet.insert(completion->ancestors.begin(), completion->ancestors.end()); + _entrySet.insert(completion->ancestors.begin(), completion->ancestors.end()); #else - State* anc = completion->parent; - while(anc != NULL && anc != state) { - _entrySet.insert(anc); - anc = anc->parent; - } + State* anc = completion->parent; + while(anc != NULL && anc != state) { + _entrySet.insert(anc); + anc = anc->parent; + } #endif - } - } - break; - } - - } - NEXT_DESCENDANT:; - } - } - + } + } + break; + } + + } +NEXT_DESCENDANT: + ; + } + } + #ifdef USCXML_VERBOSE - std::cerr << "Entering: "; - printStateNames(_entrySet); + std::cerr << "Entering: "; + printStateNames(_entrySet); #endif - /* EXIT_STATES: */ - { - BENCHMARK("exit states"); - for (auto stateIter = _exitSet.end() ; stateIter != _exitSet.begin() ; /* Do nothing */ ) { - State* state = *(--stateIter); - - USCXML_MONITOR_CALLBACK1(monitors, beforeExitingState, state->element); - - /* call all on exit handlers */ - for (auto exitIter = state->onExit.begin(); exitIter != state->onExit.end(); exitIter++) { - try { - _callbacks->process(*exitIter); - } catch (...) { - // do nothing and continue with next block - } - } - - _configuration.erase(state); - _configurationPostFix.erase(state); - USCXML_MONITOR_CALLBACK1(monitors, afterExitingState, state->element); - - } - } - - /* TAKE_TRANSITIONS: */ - { - BENCHMARK("take transitions"); - for (auto transition : _transSet) { - if ((transition->type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) == 0) { - USCXML_MONITOR_CALLBACK1(monitors, beforeTakingTransition, transition->element); - - if (transition->onTrans != NULL) { - - /* call executable content in non-history, non-initial transition */ - try { - _callbacks->process(transition->onTrans); - } catch (...) { - // do nothing and continue with next block - } - } - - USCXML_MONITOR_CALLBACK1(monitors, afterTakingTransition, transition->element); - } - } - } - - // remove active state from enter states - for (auto config : _configuration) { - if (_entrySet.find(config) != _entrySet.end()) - _entrySet.erase(config); - } - - /* ENTER_STATES: */ - { - BENCHMARK("enter states"); - for (auto state : _entrySet) { - - /* these are no proper states */ - if unlikely(USCXML_STATE_MASK(state->type) == USCXML_STATE_HISTORY_DEEP || - USCXML_STATE_MASK(state->type) == USCXML_STATE_HISTORY_SHALLOW || - USCXML_STATE_MASK(state->type) == USCXML_STATE_INITIAL) { - continue; - } - - USCXML_MONITOR_CALLBACK1(monitors, beforeEnteringState, state->element); - - _configuration.insert(state); - _configurationPostFix.insert(state); - - /* initialize data */ - if (state->data.size() > 0) { - if (_initializedData.find(state) == _initializedData.end()) { - for (auto dataIter = state->data.begin(); dataIter != state->data.end(); dataIter++) { - _callbacks->initData(*dataIter); - } - _initializedData.insert(state); - } - } - - /* call all on entry handlers */ - for (auto onEntry : state->onEntry) { - try { - _callbacks->process(onEntry); - } catch (...) { - // do nothing and continue with next block - } - } - - USCXML_MONITOR_CALLBACK1(monitors, afterEnteringState, state->element); - - /* take history and initial transitions */ - for (auto child : state->children) { - if (child->type != USCXML_STATE_INITIAL && - child->type != USCXML_STATE_HISTORY_DEEP && - child->type != USCXML_STATE_HISTORY_SHALLOW) - continue; - - for (auto transition : child->transitions) { - if (!(transition->type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) || - _transSet.find(transition) == _transSet.end()) - continue; - - USCXML_MONITOR_CALLBACK1(monitors, beforeTakingTransition, transition->element); - - /* call executable content in transition */ - if (transition->onTrans != NULL) { - try { - _callbacks->process(transition->onTrans); - } catch (...) { - // do nothing and continue with next block - } - } - - USCXML_MONITOR_CALLBACK1(monitors, afterTakingTransition, transition->element); - } - - } - - /* handle final states */ - if unlikely(USCXML_STATE_MASK(state->type) == USCXML_STATE_FINAL) { - BENCHMARK("enter states final") - if unlikely(state->parent == _states[0]) { - // only the topmost scxml is an ancestor - _flags |= USCXML_CTX_TOP_LEVEL_FINAL; - } else { - /* raise done event */ - _callbacks->raiseDoneEvent(state->parent->element, state->doneData); - } - - /** - * are we the last final state to leave a parallel state?: - * 1. Gather all parallel states in our ancestor chain - * 2. Find all states for which these parallels are ancestors - * 3. Iterate all active final states and remove their ancestors - * 4. If a state remains, not all children of a parallel are final - */ - { - BENCHMARK("enter states final parallel") - - State* anc = state->parent; - while(anc != NULL) { - if (USCXML_STATE_MASK(anc->type) == USCXML_STATE_PARALLEL) { - if (isInFinal(anc)) { - _callbacks->raiseDoneEvent(anc->element, anc->doneData); - } else { - break; // ancestors cannot be final either - } - } - anc = anc->parent; - } - } - } - } - } - - USCXML_MONITOR_CALLBACK(monitors, afterMicroStep); - - // are we running in circles? - if (_microstepConfigurations.find(_configuration) != _microstepConfigurations.end()) { - InterpreterIssue issue("Reentering same configuration during microstep - possible endless loop", - NULL, - InterpreterIssue::USCXML_ISSUE_WARNING); - - USCXML_MONITOR_CALLBACK1(monitors, - reportIssue, - issue); - } - _microstepConfigurations.insert(_configuration); - - return USCXML_MICROSTEPPED; + /* EXIT_STATES: */ + { + BENCHMARK("exit states"); + for (auto stateIter = _exitSet.end() ; stateIter != _exitSet.begin() ; /* Do nothing */ ) { + State* state = *(--stateIter); + + USCXML_MONITOR_CALLBACK2(monitors, beforeExitingState, state->name, state->element); + + /* call all on exit handlers */ + for (auto exitIter = state->onExit.begin(); exitIter != state->onExit.end(); exitIter++) { + try { + _callbacks->process(*exitIter); + } catch (...) { + // do nothing and continue with next block + } + } + + _configuration.erase(state); + _configurationPostFix.erase(state); + USCXML_MONITOR_CALLBACK2(monitors, afterExitingState, state->name, state->element); + + } + } + + /* TAKE_TRANSITIONS: */ + { + BENCHMARK("take transitions"); + for (auto transition : _transSet) { + if ((transition->type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) == 0) { + USCXML_MONITOR_CALLBACK1(monitors, beforeTakingTransition, transition->element); + + if (transition->onTrans != NULL) { + + /* call executable content in non-history, non-initial transition */ + try { + _callbacks->process(transition->onTrans); + } catch (...) { + // do nothing and continue with next block + } + } + + USCXML_MONITOR_CALLBACK1(monitors, afterTakingTransition, transition->element); + } + } + } + + // remove active state from enter states + for (auto config : _configuration) { + if (_entrySet.find(config) != _entrySet.end()) + _entrySet.erase(config); + } + + /* ENTER_STATES: */ + { + BENCHMARK("enter states"); + for (auto state : _entrySet) { + + /* these are no proper states */ + if unlikely(USCXML_STATE_MASK(state->type) == USCXML_STATE_HISTORY_DEEP || + USCXML_STATE_MASK(state->type) == USCXML_STATE_HISTORY_SHALLOW || + USCXML_STATE_MASK(state->type) == USCXML_STATE_INITIAL) { + continue; + } + + USCXML_MONITOR_CALLBACK2(monitors, beforeEnteringState, state->name, state->element); + + _configuration.insert(state); + _configurationPostFix.insert(state); + + /* initialize data */ + if (state->data.size() > 0) { + if (_initializedData.find(state) == _initializedData.end()) { + for (auto dataIter = state->data.begin(); dataIter != state->data.end(); dataIter++) { + _callbacks->initData(*dataIter); + } + _initializedData.insert(state); + } + } + + /* call all on entry handlers */ + for (auto onEntry : state->onEntry) { + try { + _callbacks->process(onEntry); + } catch (...) { + // do nothing and continue with next block + } + } + + USCXML_MONITOR_CALLBACK2(monitors, afterEnteringState, state->name, state->element); + + /* take history and initial transitions */ + for (auto child : state->children) { + if (child->type != USCXML_STATE_INITIAL && + child->type != USCXML_STATE_HISTORY_DEEP && + child->type != USCXML_STATE_HISTORY_SHALLOW) + continue; + + for (auto transition : child->transitions) { + if (!(transition->type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) || + _transSet.find(transition) == _transSet.end()) + continue; + + USCXML_MONITOR_CALLBACK1(monitors, beforeTakingTransition, transition->element); + + /* call executable content in transition */ + if (transition->onTrans != NULL) { + try { + _callbacks->process(transition->onTrans); + } catch (...) { + // do nothing and continue with next block + } + } + + USCXML_MONITOR_CALLBACK1(monitors, afterTakingTransition, transition->element); + } + + } + + /* handle final states */ + if unlikely(USCXML_STATE_MASK(state->type) == USCXML_STATE_FINAL) { + BENCHMARK("enter states final") + if unlikely(state->parent == _states[0]) { + // only the topmost scxml is an ancestor + _flags |= USCXML_CTX_TOP_LEVEL_FINAL; + } else { + /* raise done event */ + _callbacks->raiseDoneEvent(state->parent->element, state->doneData); + } + + /** + * are we the last final state to leave a parallel state?: + * 1. Gather all parallel states in our ancestor chain + * 2. Find all states for which these parallels are ancestors + * 3. Iterate all active final states and remove their ancestors + * 4. If a state remains, not all children of a parallel are final + */ + { + BENCHMARK("enter states final parallel") + + State* anc = state->parent; + while(anc != NULL) { + if (USCXML_STATE_MASK(anc->type) == USCXML_STATE_PARALLEL) { + if (isInFinal(anc)) { + _callbacks->raiseDoneEvent(anc->element, anc->doneData); + } else { + break; // ancestors cannot be final either + } + } + anc = anc->parent; + } + } + } + } + } + + USCXML_MONITOR_CALLBACK(monitors, afterMicroStep); + + // are we running in circles? + if (_microstepConfigurations.find(_configuration) != _microstepConfigurations.end()) { + InterpreterIssue issue("Reentering same configuration during microstep - possible endless loop", + NULL, + InterpreterIssue::USCXML_ISSUE_WARNING); + + USCXML_MONITOR_CALLBACK1(monitors, + reportIssue, + issue); + } + _microstepConfigurations.insert(_configuration); + + return USCXML_MICROSTEPPED; } bool LargeMicroStep::isInFinal(const State* state) { - switch (USCXML_STATE_MASK(state->type)) { - case USCXML_STATE_FINAL: - /* a final state is final */ - return true; - - case USCXML_STATE_ATOMIC: - return false; - - case USCXML_STATE_PARALLEL: - for (auto child : state->children) { - if (!isInFinal(child)) - return false; - } - return true; - - case USCXML_STATE_INITIAL: - return false; - - case USCXML_STATE_COMPOUND: - for (auto child : state->children) { - if (_configuration.find(child) != _configuration.end()) { - return isInFinal(child); - break; - } - } - return true; - - default: - // history - return true; - break; - } - return false; + switch (USCXML_STATE_MASK(state->type)) { + case USCXML_STATE_FINAL: + /* a final state is final */ + return true; + + case USCXML_STATE_ATOMIC: + return false; + + case USCXML_STATE_PARALLEL: + for (auto child : state->children) { + if (!isInFinal(child)) + return false; + } + return true; + + case USCXML_STATE_INITIAL: + return false; + + case USCXML_STATE_COMPOUND: + for (auto child : state->children) { + if (_configuration.find(child) != _configuration.end()) { + return isInFinal(child); + break; + } + } + return true; + + default: + // history + return true; + break; + } + return false; } - + void LargeMicroStep::resortStates(DOMElement* element, const X& xmlPrefix) { - - /** - initials - deep histories - shallow histories - everything else - */ - - // TODO: We can do this in one iteration - - DOMElement* child = element->getFirstElementChild(); - while(child) { - resortStates(child, xmlPrefix); - child = child->getNextElementSibling(); - } - - // shallow history states to top - child = element->getFirstElementChild(); - while(child) { - if (TAGNAME_CAST(child) == xmlPrefix.str() + "history" && - (!HAS_ATTR(element, kXMLCharType) || iequals(ATTR(element, kXMLCharType), "shallow"))) { - - DOMElement* tmp = child->getNextElementSibling(); - if (child != element->getFirstChild()) { - element->insertBefore(child, element->getFirstChild()); - } - child = tmp; - } else { - child = child->getNextElementSibling(); - } - } - - // deep history states to top - child = element->getFirstElementChild(); - while(child) { - if (child->getNodeType() == DOMNode::ELEMENT_NODE && - TAGNAME_CAST(child) == xmlPrefix.str() + "history" && - HAS_ATTR(element, kXMLCharType) && - iequals(ATTR(element, kXMLCharType), "deep")) { - - DOMElement* tmp = child->getNextElementSibling(); - if (child != element->getFirstChild()) { - element->insertBefore(child, element->getFirstChild()); - } - child = tmp; - } else { - child = child->getNextElementSibling(); - } - } - - // initial states on top of histories even - child = element->getFirstElementChild(); - while(child) { - if (child->getNodeType() == DOMNode::ELEMENT_NODE && LOCALNAME_CAST(child) == "initial") { - DOMElement* tmp = child->getNextElementSibling(); - if (child != element->getFirstChild()) { - element->insertBefore(child, element->getFirstChild()); - } - child = tmp; - } else { - child = child->getNextElementSibling(); - } - } + + /** + initials + deep histories + shallow histories + everything else + */ + + // TODO: We can do this in one iteration + + DOMElement* child = element->getFirstElementChild(); + while(child) { + resortStates(child, xmlPrefix); + child = child->getNextElementSibling(); + } + + // shallow history states to top + child = element->getFirstElementChild(); + while(child) { + if (TAGNAME_CAST(child) == xmlPrefix.str() + "history" && + (!HAS_ATTR(element, kXMLCharType) || iequals(ATTR(element, kXMLCharType), "shallow"))) { + + DOMElement* tmp = child->getNextElementSibling(); + if (child != element->getFirstChild()) { + element->insertBefore(child, element->getFirstChild()); + } + child = tmp; + } else { + child = child->getNextElementSibling(); + } + } + + // deep history states to top + child = element->getFirstElementChild(); + while(child) { + if (child->getNodeType() == DOMNode::ELEMENT_NODE && + TAGNAME_CAST(child) == xmlPrefix.str() + "history" && + HAS_ATTR(element, kXMLCharType) && + iequals(ATTR(element, kXMLCharType), "deep")) { + + DOMElement* tmp = child->getNextElementSibling(); + if (child != element->getFirstChild()) { + element->insertBefore(child, element->getFirstChild()); + } + child = tmp; + } else { + child = child->getNextElementSibling(); + } + } + + // initial states on top of histories even + child = element->getFirstElementChild(); + while(child) { + if (child->getNodeType() == DOMNode::ELEMENT_NODE && LOCALNAME_CAST(child) == "initial") { + DOMElement* tmp = child->getNextElementSibling(); + if (child != element->getFirstChild()) { + element->insertBefore(child, element->getFirstChild()); + } + child = tmp; + } else { + child = child->getNextElementSibling(); + } + } } std::pair LargeMicroStep::getExitSet(const Transition* transition) { - if (_exitSetCache.find(transition->postFixOrder) == _exitSetCache.end()) { - std::pair statesToExit; - uint32_t domain = getTransitionDomain(transition); - if (domain == std::numeric_limits::max()) - return statesToExit; - - State* domainState = _states[domain]; - - // start of exit set - statesToExit.first = domainState->documentOrder + 1; // do not include domain itself - - // end of exit set - XERCESC_NS::DOMElement* sibling = domainState->element->getNextElementSibling(); - while(sibling && !isState(sibling)) - sibling = sibling->getNextElementSibling(); - if (sibling) { - State* siblingState = (State*)sibling->getUserData(X("uscxmlState")); - statesToExit.second = siblingState->documentOrder - 1; - } else { - statesToExit.second = _states.size() - 1; - } - _exitSetCache[transition->postFixOrder] = statesToExit; - return statesToExit; - } - return _exitSetCache[transition->postFixOrder]; + if (_exitSetCache.find(transition->postFixOrder) == _exitSetCache.end()) { + std::pair statesToExit; + uint32_t domain = getTransitionDomain(transition); + if (domain == std::numeric_limits::max()) + return statesToExit; + + State* domainState = _states[domain]; + + // start of exit set + statesToExit.first = domainState->documentOrder + 1; // do not include domain itself + + // end of exit set + XERCESC_NS::DOMElement* sibling = domainState->element->getNextElementSibling(); + while(sibling && !isState(sibling)) + sibling = sibling->getNextElementSibling(); + if (sibling) { + State* siblingState = (State*)sibling->getUserData(X("uscxmlState")); + statesToExit.second = siblingState->documentOrder - 1; + } else { + statesToExit.second = _states.size() - 1; + } + _exitSetCache[transition->postFixOrder] = statesToExit; + return statesToExit; + } + return _exitSetCache[transition->postFixOrder]; } uint32_t LargeMicroStep::getTransitionDomain(const Transition* transition) { - if (transition->target.size() == 0) - return std::numeric_limits::max(); - - bool internal = (HAS_ATTR(transition->element, kXMLCharType) ? ATTR(transition->element, kXMLCharType) == "internal" : false); - if (internal && USCXML_STATE_MASK(transition->source->type) == USCXML_STATE_COMPOUND) { - for (auto target : transition->target) { - if (target->ancestors.find(transition->source) == target->ancestors.end()) { - goto BREAK_LOOP; - } - } - return transition->source->documentOrder; - } + if (transition->target.size() == 0) + return std::numeric_limits::max(); + + bool internal = (HAS_ATTR(transition->element, kXMLCharType) ? ATTR(transition->element, kXMLCharType) == "internal" : false); + if (internal && USCXML_STATE_MASK(transition->source->type) == USCXML_STATE_COMPOUND) { + for (auto target : transition->target) { + if (target->ancestors.find(transition->source) == target->ancestors.end()) { + goto BREAK_LOOP; + } + } + return transition->source->documentOrder; + } BREAK_LOOP: - - // find LCCA - uint32_t ancestor = std::numeric_limits::max(); - - // reverse walk up! - for(auto ancIter = transition->source->ancestors.rbegin(); ancIter != transition->source->ancestors.rend(); ancIter++) { - State* anc = *ancIter; - - // LCCA has to be a compound - if (!(USCXML_STATE_MASK(anc->type) == USCXML_STATE_COMPOUND)) - continue; - - // that contains all states - for (auto target : transition->target) { - if (target->ancestors.find(anc) == target->ancestors.end()) { - goto NEXT_ANCESTOR; - } - } - ancestor = anc->documentOrder; - break; - NEXT_ANCESTOR:; - } - - // none found - take uppermost root as ancestor - if (ancestor == std::numeric_limits::max()) - return 0; - return ancestor; + + // find LCCA + uint32_t ancestor = std::numeric_limits::max(); + + // reverse walk up! + for(auto ancIter = transition->source->ancestors.rbegin(); ancIter != transition->source->ancestors.rend(); ancIter++) { + State* anc = *ancIter; + + // LCCA has to be a compound + if (!(USCXML_STATE_MASK(anc->type) == USCXML_STATE_COMPOUND)) + continue; + + // that contains all states + for (auto target : transition->target) { + if (target->ancestors.find(anc) == target->ancestors.end()) { + goto NEXT_ANCESTOR; + } + } + ancestor = anc->documentOrder; + break; +NEXT_ANCESTOR: + ; + } + + // none found - take uppermost root as ancestor + if (ancestor == std::numeric_limits::max()) + return 0; + return ancestor; } - + } diff --git a/src/uscxml/interpreter/LargeMicroStep.h b/src/uscxml/interpreter/LargeMicroStep.h index 0d7d467..35e21de 100644 --- a/src/uscxml/interpreter/LargeMicroStep.h +++ b/src/uscxml/interpreter/LargeMicroStep.h @@ -58,7 +58,9 @@ public: virtual ~LargeMicroStep(); virtual std::shared_ptr create(MicroStepCallbacks* callbacks); - std::string getName() { return "large"; } + std::string getName() { + return "large"; + } virtual InterpreterState step(size_t blockMs); virtual void reset(); @@ -66,46 +68,51 @@ public: virtual std::list getConfiguration(); void markAsCancelled(); - virtual void deserialize(const Data& encodedState) {} - virtual Data serialize() { return Data(); } + virtual void deserialize(const Data& encodedState) {} + virtual Data serialize() { + return Data(); + } protected: - LargeMicroStep() {} // only for the factory - + LargeMicroStep() {} // only for the factory + class State; class Transition; - struct StateOrder - { - bool operator()(const State* lhs, const State* rhs) const { return lhs->documentOrder < rhs->documentOrder; } - }; - struct StateOrderPostFix - { - bool operator()(const State* lhs, const State* rhs) const { return lhs->postFixOrder < rhs->postFixOrder; } - }; - - struct TransitionOrder - { - bool operator()(const Transition* lhs, const Transition* rhs) const { return lhs->postFixOrder < rhs->postFixOrder; } - }; - - std::vector _states; ///< States in document order - std::vector _transitions; ///< Transitions in reverse post-order - - boost::container::flat_set _configuration; - boost::container::flat_set _configurationPostFix; - boost::container::flat_set _invocations; - boost::container::flat_set _history; - boost::container::flat_set _initializedData; + struct StateOrder { + bool operator()(const State* lhs, const State* rhs) const { + return lhs->documentOrder < rhs->documentOrder; + } + }; + struct StateOrderPostFix { + bool operator()(const State* lhs, const State* rhs) const { + return lhs->postFixOrder < rhs->postFixOrder; + } + }; + + struct TransitionOrder { + bool operator()(const Transition* lhs, const Transition* rhs) const { + return lhs->postFixOrder < rhs->postFixOrder; + } + }; + + std::vector _states; ///< States in document order + std::vector _transitions; ///< Transitions in reverse post-order + + boost::container::flat_set _configuration; + boost::container::flat_set _configurationPostFix; + boost::container::flat_set _invocations; + boost::container::flat_set _history; + boost::container::flat_set _initializedData; class Transition { public: - Transition(uint32_t postFixOrder) : postFixOrder(postFixOrder) {} - const uint32_t postFixOrder; // making these const increases performance somewhat + Transition(uint32_t postFixOrder) : postFixOrder(postFixOrder) {} + const uint32_t postFixOrder; // making these const increases performance somewhat XERCESC_NS::DOMElement* element = NULL; - boost::container::flat_set compatible; - boost::container::flat_set conflicting; + boost::container::flat_set compatible; + boost::container::flat_set conflicting; std::pair exitSet; State* source = NULL; @@ -122,83 +129,84 @@ protected: class State { public: - State(uint32_t documentOrder) : documentOrder(documentOrder) {} - const uint32_t documentOrder; - uint32_t postFixOrder; + State(uint32_t documentOrder) : documentOrder(documentOrder) {} + const uint32_t documentOrder; + uint32_t postFixOrder; XERCESC_NS::DOMElement* element; boost::container::flat_set completion; - boost::container::flat_set ancestors; // TODO: leverage! + boost::container::flat_set ancestors; // TODO: leverage! std::vector children; State* parent = NULL; - std::vector transitions; + std::vector transitions; std::vector data; std::vector invoke; std::vector onEntry; std::vector onExit; XERCESC_NS::DOMElement* doneData = NULL; + std::string name; unsigned char type; }; - void init(XERCESC_NS::DOMElement* scxml); + void init(XERCESC_NS::DOMElement* scxml); + + std::list getCompletion(const XERCESC_NS::DOMElement* state); + std::list getHistoryCompletion(const XERCESC_NS::DOMElement* history); - std::list getCompletion(const XERCESC_NS::DOMElement* state); - std::list getHistoryCompletion(const XERCESC_NS::DOMElement* history); + unsigned char _flags = 0; + boost::container::flat_set > _microstepConfigurations; - unsigned char _flags = 0; - boost::container::flat_set > _microstepConfigurations; + bool _isInitialized = false; + bool _isCancelled = false; + Event _event; // we do not care about the event's representation - bool _isInitialized = false; - bool _isCancelled = false; - Event _event; // we do not care about the event's representation + std::list _globalScripts; - std::list _globalScripts; + Binding _binding; + XERCESC_NS::DOMElement* _scxml; + X _xmlPrefix; + X _xmlNS; - Binding _binding; - XERCESC_NS::DOMElement* _scxml; - X _xmlPrefix; - X _xmlNS; - - /// Normalize order of elements per state - void resortStates(XERCESC_NS::DOMElement* node, const X& xmlPrefix); + /// Normalize order of elements per state + void resortStates(XERCESC_NS::DOMElement* node, const X& xmlPrefix); private: - boost::container::flat_set _exitSet; - boost::container::flat_set _entrySet; - boost::container::flat_set _targetSet; - boost::container::flat_set _tmpStates; - - boost::dynamic_bitset _compatible; - boost::dynamic_bitset _conflicting; - - boost::container::flat_set _transSet; - - // adapted from http://www.cplusplus.com/reference/algorithm/set_intersection/ - template - bool intersects(iter_t1 first1, iter_t1 last1, iter_t2 first2, iter_t2 last2) { - while (first1 != last1 && first2 != last2) { - if (compare()(*first1, *first2)) { - ++first1; - } else if (compare()(*first2, *first1)) { - ++first2; - } else { - return true; - } - } - return false; - } - - bool isInFinal(const State* state); - void printStateNames(const boost::container::flat_set& a); - - uint32_t getTransitionDomain(const Transition* transition); - std::pair getExitSet(const Transition* transition); - std::map > _exitSetCache; - - friend class Factory; + boost::container::flat_set _exitSet; + boost::container::flat_set _entrySet; + boost::container::flat_set _targetSet; + boost::container::flat_set _tmpStates; + + boost::dynamic_bitset _compatible; + boost::dynamic_bitset _conflicting; + + boost::container::flat_set _transSet; + + // adapted from http://www.cplusplus.com/reference/algorithm/set_intersection/ + template + bool intersects(iter_t1 first1, iter_t1 last1, iter_t2 first2, iter_t2 last2) { + while (first1 != last1 && first2 != last2) { + if (compare()(*first1, *first2)) { + ++first1; + } else if (compare()(*first2, *first1)) { + ++first2; + } else { + return true; + } + } + return false; + } + + bool isInFinal(const State* state); + void printStateNames(const boost::container::flat_set& a); + + uint32_t getTransitionDomain(const Transition* transition); + std::pair getExitSet(const Transition* transition); + std::map > _exitSetCache; + + friend class Factory; }; } diff --git a/src/uscxml/interpreter/MicroStepImpl.h b/src/uscxml/interpreter/MicroStepImpl.h index 53139bc..df5c07c 100644 --- a/src/uscxml/interpreter/MicroStepImpl.h +++ b/src/uscxml/interpreter/MicroStepImpl.h @@ -58,7 +58,7 @@ public: /** Monitoring */ virtual std::set getMonitors() = 0; - virtual Interpreter getInterpreter() = 0; + virtual const std::string& getSessionId() = 0; virtual Logger getLogger() = 0; /** Cache Data */ @@ -91,11 +91,11 @@ public: virtual void deserialize(const Data& encodedState) = 0; virtual Data serialize() = 0; - /// To register at the factory - virtual std::string getName() = 0; + /// To register at the factory + virtual std::string getName() = 0; protected: - MicroStepImpl() {}; + MicroStepImpl() {}; MicroStepCallbacks* _callbacks; }; diff --git a/src/uscxml/plugins/Factory.cpp b/src/uscxml/plugins/Factory.cpp index 410ac36..9e885e5 100644 --- a/src/uscxml/plugins/Factory.cpp +++ b/src/uscxml/plugins/Factory.cpp @@ -133,10 +133,10 @@ Factory::~Factory() { void Factory::registerPlugins() { #ifndef FEATS_ON_CMD - registerMicrostepper(new LargeMicroStep()); - registerMicrostepper(new FastMicroStep()); + registerMicrostepper(new LargeMicroStep()); + registerMicrostepper(new FastMicroStep()); #endif - + /*** PLUGINS ***/ #ifdef BUILD_AS_PLUGINS @@ -507,36 +507,36 @@ std::shared_ptr Factory::createExecutableContent(const st #ifndef FEATS_ON_CMD bool Factory::hasMicroStepper(const std::string& name) { - if (_microSteppers.find(name) != _microSteppers.end()) { - return true; - } else if(_parentFactory) { - return _parentFactory->hasMicroStepper(name); - } - return false; + if (_microSteppers.find(name) != _microSteppers.end()) { + return true; + } else if(_parentFactory) { + return _parentFactory->hasMicroStepper(name); + } + return false; } - + void Factory::registerMicrostepper(MicroStepImpl* microStepper) { - _microSteppers[microStepper->getName()] = microStepper; + _microSteppers[microStepper->getName()] = microStepper; } std::shared_ptr Factory::createMicroStepper(const std::string& name, MicroStepCallbacks* callbacks) { - if (_microSteppers.find(name) != _microSteppers.end()) { - std::shared_ptr microStepper = _microSteppers[name]->create(callbacks); - return microStepper; - } - - if (_parentFactory) { - return _parentFactory->createMicroStepper(name, callbacks); - } else { - ERROR_EXECUTION_THROW("No Microstepper '" + name + "' known"); - } - - return std::shared_ptr(); + if (_microSteppers.find(name) != _microSteppers.end()) { + std::shared_ptr microStepper = _microSteppers[name]->create(callbacks); + return microStepper; + } + + if (_parentFactory) { + return _parentFactory->createMicroStepper(name, callbacks); + } else { + ERROR_EXECUTION_THROW("No Microstepper '" + name + "' known"); + } + + return std::shared_ptr(); } #endif - + void DataModelImpl::addExtension(DataModelExtension* ext) { ERROR_EXECUTION_THROW("DataModel does not support extensions"); } diff --git a/src/uscxml/plugins/Factory.h b/src/uscxml/plugins/Factory.h index 986ff5f..cd7a5c7 100644 --- a/src/uscxml/plugins/Factory.h +++ b/src/uscxml/plugins/Factory.h @@ -53,31 +53,31 @@ public: Factory(const std::string& pluginPath, Factory* parentFactory); void registerIOProcessor(IOProcessorImpl* ioProcessor); - bool hasIOProcessor(const std::string& type); - std::shared_ptr createIOProcessor(const std::string& type, IOProcessorCallbacks* callbacks); + bool hasIOProcessor(const std::string& type); + std::shared_ptr createIOProcessor(const std::string& type, IOProcessorCallbacks* callbacks); - void registerDataModel(DataModelImpl* dataModel); - bool hasDataModel(const std::string& type); - std::shared_ptr createDataModel(const std::string& type, DataModelCallbacks* callbacks); + void registerDataModel(DataModelImpl* dataModel); + bool hasDataModel(const std::string& type); + std::shared_ptr createDataModel(const std::string& type, DataModelCallbacks* callbacks); - void registerInvoker(InvokerImpl* invoker); - bool hasInvoker(const std::string& type); - std::shared_ptr createInvoker(const std::string& type, InvokerCallbacks* interpreter); + void registerInvoker(InvokerImpl* invoker); + bool hasInvoker(const std::string& type); + std::shared_ptr createInvoker(const std::string& type, InvokerCallbacks* interpreter); - void registerExecutableContent(ExecutableContentImpl* executableContent); - bool hasExecutableContent(const std::string& localName, const std::string& nameSpace); - std::shared_ptr createExecutableContent(const std::string& localName, const std::string& nameSpace, InterpreterImpl* interpreter); + void registerExecutableContent(ExecutableContentImpl* executableContent); + bool hasExecutableContent(const std::string& localName, const std::string& nameSpace); + std::shared_ptr createExecutableContent(const std::string& localName, const std::string& nameSpace, InterpreterImpl* interpreter); #ifndef FEATS_ON_CMD - void registerMicrostepper(MicroStepImpl* microStepper); - bool hasMicroStepper(const std::string& name); - std::shared_ptr createMicroStepper(const std::string& name, MicroStepCallbacks* callbacks); + void registerMicrostepper(MicroStepImpl* microStepper); + bool hasMicroStepper(const std::string& name); + std::shared_ptr createMicroStepper(const std::string& name, MicroStepCallbacks* callbacks); #endif - std::map getIOProcessors(); + std::map getIOProcessors(); void listComponents(); @@ -96,7 +96,7 @@ protected: std::map, ExecutableContentImpl*> _executableContent; #ifndef FEATS_ON_CMD - std::map _microSteppers; + std::map _microSteppers; #endif #ifdef BUILD_AS_PLUGINS diff --git a/src/uscxml/transform/ChartToC.cpp b/src/uscxml/transform/ChartToC.cpp index 0f64ad6..a52cf5e 100644 --- a/src/uscxml/transform/ChartToC.cpp +++ b/src/uscxml/transform/ChartToC.cpp @@ -54,7 +54,7 @@ ChartToC::ChartToC(const Interpreter& other) : TransformerImpl(other), _topMostM _hasNativeDataModel = HAS_ATTR(_scxml, kXMLCharDataModel) && ATTR(_scxml, kXMLCharDataModel) == "native"; findNestedMachines(); - prepare(); + prepare(); if (_extensions.find("prefix") != _extensions.end()) { _prefixes = new std::list(); @@ -382,13 +382,13 @@ void ChartToC::prepare() { setStateCompletion(); // how many bits do we need to represent the state array? - size_t largestStateSpace = 0; - size_t largestTransSpace = 0; - for (auto machine : _allMachines) { - largestStateSpace = (machine->_states.size() > largestStateSpace ? machine->_states.size() : largestStateSpace); - largestTransSpace = (machine->_transitions.size() > largestTransSpace ? machine->_transitions.size() : largestTransSpace); - } - + size_t largestStateSpace = 0; + size_t largestTransSpace = 0; + for (auto machine : _allMachines) { + largestStateSpace = (machine->_states.size() > largestStateSpace ? machine->_states.size() : largestStateSpace); + largestTransSpace = (machine->_transitions.size() > largestTransSpace ? machine->_transitions.size() : largestTransSpace); + } + std::string seperator; _stateCharArraySize = ceil((float)largestStateSpace / (float)8); _stateCharArrayInit = "{"; @@ -1670,13 +1670,13 @@ void ChartToC::writeElementInfo(std::ostream& stream) { size_t i = 0; for (auto iter = params.begin(); iter != params.end(); iter++, i++) { DOMElement* param = *iter; - // TODO: Index is wrong for multiple params! + // TODO: Index is wrong for multiple params! if (param->getParentNode() != parent) { if (i > 0) { stream << " { NULL, NULL, NULL }," << std::endl; - i++; + i++; } - static_cast(param->getParentNode())->setAttribute(X("paramIndex"), X(toStr(i))); + static_cast(param->getParentNode())->setAttribute(X("paramIndex"), X(toStr(i))); parent = param->getParentNode(); } stream << " { "; diff --git a/src/uscxml/util/DOM.h b/src/uscxml/util/DOM.h index 74133e8..2adc675 100644 --- a/src/uscxml/util/DOM.h +++ b/src/uscxml/util/DOM.h @@ -212,6 +212,10 @@ public : _deallocOther = true; } + bool operator==(const XMLCh* other) const { + return XERCESC_NS::XMLString::compareString(other, _unicodeForm) == 0; + } + bool operator==(const X& other) const { return (_unicodeForm == other._unicodeForm) != 0; } diff --git a/test/src/test-performance.cpp b/test/src/test-performance.cpp index 4142b9d..c312b70 100644 --- a/test/src/test-performance.cpp +++ b/test/src/test-performance.cpp @@ -1,5 +1,7 @@ #include "uscxml/config.h" #include "uscxml/Interpreter.h" +#include "uscxml/interpreter/InterpreterMonitor.h" +#include "uscxml/util/DOM.h" #include #include @@ -7,40 +9,62 @@ using namespace uscxml; using namespace std::chrono; +long iterations = 0; +long initMs = 0; +bool exitPerf = false; +system_clock::time_point now; +system_clock::time_point start; +system_clock::time_point report; +system_clock::time_point endTime; + +class PerfMon : public InterpreterMonitor { +public: + virtual void beforeEnteringState(const std::string& sessionId, + const std::string& stateName, + const XERCESC_NS::DOMElement* state) { + if (stateName == "mark") { + iterations++; + now = system_clock::now(); + if (now > report) { + report = now + seconds(1); + std::cout << initMs << ", " << iterations << std::endl; + iterations = 0; + } + } + if (now > endTime) { + exitPerf = true; + } + } +}; + int main(int argc, char** argv) { if (argc < 2) { std::cout << "Expected filename as first parameter" << std::endl; exit(EXIT_FAILURE); } + { + start = system_clock::now(); - Interpreter interpreter = Interpreter::fromURL(argv[1]); - - InterpreterState state; - system_clock::time_point start = system_clock::now(); - - while((state = interpreter.step()) != InterpreterState::USCXML_INITIALIZED) {} - system_clock::time_point now = system_clock::now(); - - std::cout << "init: " << duration_cast(now - start).count() << "ms" << std::endl; - - start = system_clock::now(); - system_clock::time_point endTime = start + seconds(10); - system_clock::time_point report = start + seconds(1); + Interpreter sc = Interpreter::fromURL(argv[1]); + sc.step(); // initialize? - unsigned long iterations = 0; + PerfMon mon; + sc.addMonitor(&mon); - while(true) { now = system_clock::now(); - if (now > endTime) - break; + initMs = duration_cast(now - start).count(); - interpreter.step(); + start = now; + report = start + seconds(1); + endTime = start + seconds(10); - iterations++; - if (now > report) { - report = now + seconds(1); - std::cout << "steps / sec: " << iterations << std::endl; - iterations = 0; + while(true) { + sc.step(); + if (exitPerf) { + goto DONE_AND_EXIT; + } } } +DONE_AND_EXIT: + return 0; } diff --git a/test/src/test-state-pass.cpp b/test/src/test-state-pass.cpp index c07523e..324cce3 100644 --- a/test/src/test-state-pass.cpp +++ b/test/src/test-state-pass.cpp @@ -80,8 +80,8 @@ int main(int argc, char** argv) { state = interpreter.step(); } if (interpreter.isInState("pass")) - return EXIT_SUCCESS; - return EXIT_FAILURE; + return EXIT_SUCCESS; + return EXIT_FAILURE; } catch (Event e) { std::cerr << "Thrown Event out of Interpreter: " << e; -- cgit v0.12 From 89c801bf390f928d173b2dcf1118f3437c9a08f0 Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Wed, 5 Jul 2017 10:29:26 +0200 Subject: Reenabled actual tests for JEXL --- contrib/java/bindings/org/uscxml/tests/JexlDataModelTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/java/bindings/org/uscxml/tests/JexlDataModelTest.java b/contrib/java/bindings/org/uscxml/tests/JexlDataModelTest.java index 3db5b29..a610fda 100644 --- a/contrib/java/bindings/org/uscxml/tests/JexlDataModelTest.java +++ b/contrib/java/bindings/org/uscxml/tests/JexlDataModelTest.java @@ -21,13 +21,13 @@ public class JexlDataModelTest { } System.load(uSCXMLLibPath); - String testUri = "/Users/sradomski/Documents/TK/Code/uscxml/test/w3c/jexl/test144.scxml"; + String testUri = "/Users/sradomski/Documents/TK/Code/uscxml/test/w3c/jexl/test-enc-ISO-8859-1.scxml"; // String testUri = "/Users/sradomski/Desktop/stopwatch.xml"; -// if (args.length > 0) { -// testUri = args[0]; -// } + if (args.length > 0) { + testUri = args[0]; + } { JexlDataModel jdm = new JexlDataModel(); -- cgit v0.12