diff options
Diffstat (limited to 'src/uscxml/interpreter')
-rw-r--r-- | src/uscxml/interpreter/BasicEventQueue.cpp | 18 | ||||
-rw-r--r-- | src/uscxml/interpreter/FastMicroStep.cpp | 188 | ||||
-rw-r--r-- | src/uscxml/interpreter/FastMicroStep.h | 33 | ||||
-rw-r--r-- | src/uscxml/interpreter/InterpreterImpl.cpp | 39 | ||||
-rw-r--r-- | src/uscxml/interpreter/InterpreterImpl.h | 7 | ||||
-rw-r--r-- | src/uscxml/interpreter/MicroStepImpl.h | 3 |
6 files changed, 213 insertions, 75 deletions
diff --git a/src/uscxml/interpreter/BasicEventQueue.cpp b/src/uscxml/interpreter/BasicEventQueue.cpp index 2f8bd48..519754e 100644 --- a/src/uscxml/interpreter/BasicEventQueue.cpp +++ b/src/uscxml/interpreter/BasicEventQueue.cpp @@ -35,17 +35,17 @@ Event BasicEventQueue::dequeue(size_t blockMs) { std::lock_guard<std::recursive_mutex> lock(_mutex); if (blockMs > 0) { - using namespace std::chrono; + using namespace std::chrono; - // TODO: do read http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2661.htm - system_clock::time_point now = system_clock::now(); - system_clock::time_point endTime = now + milliseconds(blockMs); + // TODO: do read http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2661.htm + system_clock::time_point now = system_clock::now(); + system_clock::time_point endTime = now + milliseconds(blockMs); + + // now + milliseconds(blockMs) may not have fitted into a duration type - limit to maximum duration + if (blockMs > system_clock::duration::max().count() - duration_cast<milliseconds>(now.time_since_epoch()).count()) { + endTime = system_clock::time_point::max(); + } - // now + milliseconds(blockMs) may not have fitted into a duration type - limit to maximum duration - if (blockMs > system_clock::duration::max().count() - duration_cast<milliseconds>(now.time_since_epoch()).count()) { - endTime = system_clock::time_point::max(); - } - // block for given milliseconds or until queue is non-empty while (endTime > std::chrono::system_clock::now() && _queue.empty()) { _cond.wait_until(_mutex, endTime); diff --git a/src/uscxml/interpreter/FastMicroStep.cpp b/src/uscxml/interpreter/FastMicroStep.cpp index ed11fca..e41e9f7 100644 --- a/src/uscxml/interpreter/FastMicroStep.cpp +++ b/src/uscxml/interpreter/FastMicroStep.cpp @@ -22,6 +22,7 @@ #include "FastMicroStep.h" #include "uscxml/util/DOM.h" #include "uscxml/util/String.h" +#include "uscxml/util/Base64.hpp" #include "uscxml/util/Predicates.h" #include "uscxml/util/Convenience.h" #include "uscxml/interpreter/InterpreterMonitor.h" @@ -102,6 +103,8 @@ void FastMicroStep::resortStates(DOMElement* element, const X& xmlPrefix) { everything else */ + // TODO: We can do this in one iteration + DOMElement* child = element->getFirstElementChild(); while(child) { resortStates(child, xmlPrefix); @@ -194,9 +197,11 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { _xmlPrefix = std::string(_xmlPrefix) + ":"; } + bool withCache = !envVarIsTrue("USCXML_NOCACHE_FILES"); + resortStates(_scxml, _xmlPrefix); -// assert(false); -// throw NULL; + + Data& cache = _callbacks->getCache().compound["FastMicroStep"]; /** -- All things states -- */ @@ -243,7 +248,20 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { _states[0]->data = DOMUtils::filterChildElements(_xmlPrefix.str() + "data", dataModels, false); } + auto currState = cache.compound["states"].array.begin(); + auto endState = cache.compound["states"].array.end(); + for (i = 0; i < _states.size(); i++) { + Data* cachedState = NULL; + if (withCache) { + if (currState != endState) { + cachedState = &(*currState); + currState++; + } else { + cache.compound["states"].array.push_back(Data()); + cachedState = &(*cache.compound["states"].array.rbegin()); + } + } // collect states with an id attribute if (HAS_ATTR(_states[i]->element, "id")) { _stateIds[ATTR(_states[i]->element, "id")] = i; @@ -273,7 +291,6 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { } } - // set the states type if (false) { } else if (iequals(TAGNAME(_states[i]->element), _xmlPrefix.str() + "initial")) { @@ -297,17 +314,22 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { } // establish the states' completion - std::list<DOMElement*> completion = getCompletion(_states[i]->element); - for (j = 0; j < _states.size(); j++) { - if (!completion.empty() && _states[j]->element == completion.front()) { - _states[i]->completion[j] = true; - completion.pop_front(); - } else { - _states[i]->completion[j] = false; + if (withCache && cachedState->compound.find("completion") != cachedState->compound.end()) { + _states[i]->completion = fromBase64(cachedState->compound["completion"]); + } else { + std::list<DOMElement*> completion = getCompletion(_states[i]->element); + for (j = 0; j < _states.size(); j++) { + if (!completion.empty() && _states[j]->element == completion.front()) { + _states[i]->completion[j] = true; + completion.pop_front(); + } else { + _states[i]->completion[j] = false; + } } + assert(completion.size() == 0); + if (withCache) + cachedState->compound["completion"] = Data(toBase64(_states[i]->completion)); } - assert(completion.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; @@ -354,46 +376,78 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { } assert(tmp.size() == 0); + auto currTrans = cache.compound["transitions"].array.begin(); + auto endTrans = cache.compound["transitions"].array.end(); + for (i = 0; i < _transitions.size(); i++) { + Data* cachedTrans = NULL; + if (withCache) { + if (currTrans != endTrans) { + cachedTrans = &(*currTrans); + currTrans++; + } else { + cache.compound["transitions"].array.push_back(Data()); + cachedTrans = &(*cache.compound["transitions"].array.rbegin()); + } + } // establish the transitions' exit set assert(_transitions[i]->element != NULL); -// std::cout << "i: " << i << std::endl << std::flush; - std::list<DOMElement*> exitList = getExitSetCached(_transitions[i]->element, _scxml); - _cache.exitSet[_transitions[i]->element] = exitList; - - for (j = 0; j < _states.size(); j++) { - if (!exitList.empty() && _states[j]->element == exitList.front()) { - _transitions[i]->exitSet[j] = true; - exitList.pop_front(); - } else { - _transitions[i]->exitSet[j] = false; + + if (withCache && cachedTrans->compound.find("exitset") != cachedTrans->compound.end()) { + _transitions[i]->exitSet = fromBase64(cachedTrans->compound["exitset"]); + } else { + std::list<DOMElement*> exitList = getExitSetCached(_transitions[i]->element, _scxml); + + for (j = 0; j < _states.size(); j++) { + if (!exitList.empty() && _states[j]->element == exitList.front()) { + _transitions[i]->exitSet[j] = true; + exitList.pop_front(); + } else { + _transitions[i]->exitSet[j] = false; + } } + assert(exitList.size() == 0); + + if (withCache) + cachedTrans->compound["exitset"] = Data(toBase64(_transitions[i]->exitSet)); } - assert(exitList.size() == 0); // establish the transitions' conflict set - for (j = i; j < _transitions.size(); j++) { - if (conflictsCached(_transitions[i]->element, _transitions[j]->element, _scxml)) { - _transitions[i]->conflicts[j] = true; - } else { - _transitions[i]->conflicts[j] = false; + if (withCache && cachedTrans->compound.find("conflicts") != cachedTrans->compound.end()) { + _transitions[i]->conflicts = fromBase64(cachedTrans->compound["conflicts"]); + } else { + for (j = i; j < _transitions.size(); j++) { + if (conflictsCached(_transitions[i]->element, _transitions[j]->element, _scxml)) { + _transitions[i]->conflicts[j] = true; + } else { + _transitions[i]->conflicts[j] = false; + } + // std::cout << "."; } -// std::cout << "."; - } - // conflicts matrix is symmetric - for (j = 0; j < i; j++) { - _transitions[i]->conflicts[j] = _transitions[j]->conflicts[i]; - } + // conflicts matrix is symmetric + for (j = 0; j < i; j++) { + _transitions[i]->conflicts[j] = _transitions[j]->conflicts[i]; + } + if (withCache) + cachedTrans->compound["conflicts"] = Data(toBase64(_transitions[i]->conflicts)); + } // establish the transitions' target set - std::list<std::string> targets = tokenize(ATTR(_transitions[i]->element, "target")); - for (auto tIter = targets.begin(); tIter != targets.end(); tIter++) { - if (_stateIds.find(*tIter) != _stateIds.end()) { - _transitions[i]->target[_stateIds[*tIter]] = true; + if (withCache && cachedTrans->compound.find("target") != cachedTrans->compound.end()) { + _transitions[i]->target = fromBase64(cachedTrans->compound["target"]); + } else { + std::list<std::string> targets = tokenize(ATTR(_transitions[i]->element, "target")); + for (auto tIter = targets.begin(); tIter != targets.end(); tIter++) { + if (_stateIds.find(*tIter) != _stateIds.end()) { + _transitions[i]->target[_stateIds[*tIter]] = true; + } } + if (withCache) + cachedTrans->compound["target"] = Data(toBase64(_transitions[i]->target)); + } // the transition's source @@ -422,7 +476,6 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { _transitions[i]->type |= USCXML_TRANS_INITIAL; } - // the transitions event and condition _transitions[i]->event = (HAS_ATTR(_transitions[i]->element, "event") ? ATTR(_transitions[i]->element, "event") : ""); @@ -433,10 +486,39 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { if (_transitions[i]->element->getChildElementCount() > 0) { _transitions[i]->onTrans = _transitions[i]->element; } - } _cache.exitSet.clear(); _isInitialized = true; + +} + +std::string FastMicroStep::toBase64(const boost::dynamic_bitset<BITSET_BLOCKTYPE>& bitset) { + std::vector<boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type> bytes(bitset.num_blocks() + 1); + boost::to_block_range(bitset, bytes.begin()); + + std::string encoded; + encoded.resize(sizeof(boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type) * bytes.size()); + bytes[bytes.size() - 1] = static_cast<boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type>(bitset.size()); + + static_assert(sizeof(boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type) >= sizeof(size_t), "Block type too small to encode dynamic_bitset length"); + + memcpy(&encoded[0], &bytes[0], sizeof(boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type) * bytes.size()); + return base64Encode(encoded.c_str(), encoded.size(), true); +} + +boost::dynamic_bitset<BITSET_BLOCKTYPE> FastMicroStep::fromBase64(const std::string& encoded) { + + std::string decoded = base64Decode(encoded); + assert(decoded.size() % sizeof(boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type) == 0); + std::vector<boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type> bytes(decoded.size() / sizeof(boost::dynamic_bitset<BITSET_BLOCKTYPE>::block_type)); + + memcpy(&bytes[0], &decoded[0], decoded.size()); + + boost::dynamic_bitset<BITSET_BLOCKTYPE> bitset; + bitset.init_from_block_range(bytes.begin(), --bytes.end()); + bitset.resize(bytes[bytes.size() - 1]); + + return bitset; } void FastMicroStep::markAsCancelled() { @@ -451,13 +533,13 @@ InterpreterState FastMicroStep::step(size_t blockMs) { size_t i, j, k; - boost::dynamic_bitset<> exitSet(_states.size(), false); - boost::dynamic_bitset<> entrySet(_states.size(), false); - boost::dynamic_bitset<> targetSet(_states.size(), false); - boost::dynamic_bitset<> tmpStates(_states.size(), false); + boost::dynamic_bitset<BITSET_BLOCKTYPE> exitSet(_states.size(), false); + boost::dynamic_bitset<BITSET_BLOCKTYPE> entrySet(_states.size(), false); + boost::dynamic_bitset<BITSET_BLOCKTYPE> targetSet(_states.size(), false); + boost::dynamic_bitset<BITSET_BLOCKTYPE> tmpStates(_states.size(), false); - boost::dynamic_bitset<> conflicts(_transitions.size(), false); - boost::dynamic_bitset<> transSet(_transitions.size(), false); + boost::dynamic_bitset<BITSET_BLOCKTYPE> conflicts(_transitions.size(), false); + boost::dynamic_bitset<BITSET_BLOCKTYPE> transSet(_transitions.size(), false); #ifdef USCXML_VERBOSE std::cerr << "Config: "; @@ -675,14 +757,14 @@ ESTABLISH_ENTRYSET: /* iterate for ancestors */ i = entrySet.find_first(); - while(i != boost::dynamic_bitset<>::npos) { + while(i != boost::dynamic_bitset<BITSET_BLOCKTYPE>::npos) { entrySet |= USCXML_GET_STATE(i).ancestors; i = entrySet.find_next(i); } /* iterate for descendants */ i = entrySet.find_first(); - while(i != boost::dynamic_bitset<>::npos) { + while(i != boost::dynamic_bitset<BITSET_BLOCKTYPE>::npos) { switch (USCXML_STATE_MASK(USCXML_GET_STATE(i).type)) { @@ -813,7 +895,7 @@ ESTABLISH_ENTRYSET: /* TAKE_TRANSITIONS: */ i = transSet.find_first(); - while(i != boost::dynamic_bitset<>::npos) { + while(i != boost::dynamic_bitset<BITSET_BLOCKTYPE>::npos) { if ((USCXML_GET_TRANS(i).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) == 0) { USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeTakingTransition, USCXML_GET_TRANS(i).element); @@ -841,7 +923,7 @@ ESTABLISH_ENTRYSET: /* ENTER_STATES: */ i = entrySet.find_first(); - while(i != boost::dynamic_bitset<>::npos) { + while(i != boost::dynamic_bitset<BITSET_BLOCKTYPE>::npos) { if (BIT_HAS(i, _configuration)) { // already active @@ -924,7 +1006,7 @@ ESTABLISH_ENTRYSET: BIT_HAS(j, USCXML_GET_STATE(i).ancestors)) { tmpStates.reset(); k = _configuration.find_first(); - while (k != boost::dynamic_bitset<>::npos) { + while (k != boost::dynamic_bitset<BITSET_BLOCKTYPE>::npos) { if (BIT_HAS(j, USCXML_GET_STATE(k).ancestors)) { if (USCXML_STATE_MASK(USCXML_GET_STATE(k).type) == USCXML_STATE_FINAL) { tmpStates ^= USCXML_GET_STATE(k).ancestors; @@ -981,7 +1063,7 @@ bool FastMicroStep::isInState(const std::string& stateId) { std::list<XERCESC_NS::DOMElement*> FastMicroStep::getConfiguration() { std::list<XERCESC_NS::DOMElement*> config; size_t i = _configuration.find_first(); - while(i != boost::dynamic_bitset<>::npos) { + while(i != boost::dynamic_bitset<BITSET_BLOCKTYPE>::npos) { config.push_back(_states[i]->element); i = _configuration.find_next(i); } @@ -1038,7 +1120,7 @@ std::list<DOMElement*> FastMicroStep::getHistoryCompletion(const DOMElement* his /** * Print name of states contained in a (debugging). */ -void FastMicroStep::printStateNames(const boost::dynamic_bitset<>& a) { +void FastMicroStep::printStateNames(const boost::dynamic_bitset<BITSET_BLOCKTYPE>& a) { size_t i; const char* seperator = ""; for (i = 0; i < a.size(); i++) { diff --git a/src/uscxml/interpreter/FastMicroStep.h b/src/uscxml/interpreter/FastMicroStep.h index 810b793..db3e724 100644 --- a/src/uscxml/interpreter/FastMicroStep.h +++ b/src/uscxml/interpreter/FastMicroStep.h @@ -33,6 +33,12 @@ #include <boost/dynamic_bitset.hpp> +#ifdef _WIN32 +#define BITSET_BLOCKTYPE size_t +#else +#define BITSET_BLOCKTYPE +#endif + namespace uscxml { /** @@ -57,11 +63,11 @@ protected: Transition() : element(NULL), source(0), onTrans(NULL), type(0) {} XERCESC_NS::DOMElement* element; - boost::dynamic_bitset<> conflicts; - boost::dynamic_bitset<> exitSet; + boost::dynamic_bitset<BITSET_BLOCKTYPE> conflicts; + boost::dynamic_bitset<BITSET_BLOCKTYPE> exitSet; uint32_t source; - boost::dynamic_bitset<> target; + boost::dynamic_bitset<BITSET_BLOCKTYPE> target; XERCESC_NS::DOMElement* onTrans; @@ -77,9 +83,9 @@ protected: State() : element(NULL), parent(0), documentOrder(0), doneData(NULL), type(0) {} XERCESC_NS::DOMElement* element; - boost::dynamic_bitset<> completion; - boost::dynamic_bitset<> children; - boost::dynamic_bitset<> ancestors; + boost::dynamic_bitset<BITSET_BLOCKTYPE> completion; + boost::dynamic_bitset<BITSET_BLOCKTYPE> children; + boost::dynamic_bitset<BITSET_BLOCKTYPE> ancestors; uint32_t parent; uint32_t documentOrder; @@ -108,12 +114,12 @@ protected: std::vector<Transition*> _transitions; std::list<XERCESC_NS::DOMElement*> _globalScripts; - boost::dynamic_bitset<> _configuration; - boost::dynamic_bitset<> _invocations; - boost::dynamic_bitset<> _history; - boost::dynamic_bitset<> _initializedData; + boost::dynamic_bitset<BITSET_BLOCKTYPE> _configuration; + boost::dynamic_bitset<BITSET_BLOCKTYPE> _invocations; + boost::dynamic_bitset<BITSET_BLOCKTYPE> _history; + boost::dynamic_bitset<BITSET_BLOCKTYPE> _initializedData; - std::set<boost::dynamic_bitset<> > _microstepConfigurations; + std::set<boost::dynamic_bitset<BITSET_BLOCKTYPE> > _microstepConfigurations; Binding _binding; XERCESC_NS::DOMElement* _scxml; @@ -130,13 +136,16 @@ private: bool conflictsCached(const XERCESC_NS::DOMElement* t1, const XERCESC_NS::DOMElement* t2, const XERCESC_NS::DOMElement* root); ///< overrides implementation Predicates::conflicts for speed + std::string toBase64(const boost::dynamic_bitset<BITSET_BLOCKTYPE>& bitset); + boost::dynamic_bitset<BITSET_BLOCKTYPE> fromBase64(const std::string& encoded); + std::list<XERCESC_NS::DOMElement*> getExitSetCached(const XERCESC_NS::DOMElement* transition, const XERCESC_NS::DOMElement* root); CachedPredicates _cache; #ifdef USCXML_VERBOSE - void printStateNames(const boost::dynamic_bitset<>& bitset); + void printStateNames(const boost::dynamic_bitset<BITSET_BLOCKTYPE>& bitset); #endif }; diff --git a/src/uscxml/interpreter/InterpreterImpl.cpp b/src/uscxml/interpreter/InterpreterImpl.cpp index 43059bf..8762b95 100644 --- a/src/uscxml/interpreter/InterpreterImpl.cpp +++ b/src/uscxml/interpreter/InterpreterImpl.cpp @@ -27,11 +27,13 @@ #include "uscxml/messages/Event.h" #include "uscxml/util/String.h" #include "uscxml/util/Predicates.h" +#include "uscxml/util/MD5.hpp" #include "uscxml/plugins/InvokerImpl.h" #include "uscxml/interpreter/Logging.h" #include <iostream> +#include <fstream> #include <assert.h> #include <algorithm> @@ -108,6 +110,15 @@ InterpreterImpl::~InterpreterImpl() { // assert(_invokers.size() == 0); // ::xercesc_3_1::XMLPlatformUtils::Terminate(); + if (!envVarIsTrue("USCXML_NOCACHE_FILES")) { + // save our cache + std::string sharedTemp = URL::getTempDir(true); + std::ofstream dataFS(sharedTemp + PATH_SEPERATOR + md5(_baseURL) + ".uscxml.cache"); + if (dataFS) { + dataFS << _cache; + dataFS.close(); + } + } } void InterpreterImpl::cancel() { @@ -159,7 +170,6 @@ SCXML_STOP_SEARCH: _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); } - } void InterpreterImpl::init() { @@ -169,6 +179,33 @@ void InterpreterImpl::init() { setupDOM(); + if (!envVarIsTrue("USCXML_NOCACHE_FILES")) { + // try to open chached data from resource directory + std::string sharedTemp = URL::getTempDir(true); + std::ifstream dataFS(sharedTemp + PATH_SEPERATOR + md5(_baseURL) + ".uscxml.cache"); + if (dataFS.is_open()) { + std::string cacheStr((std::istreambuf_iterator<char>(dataFS)), + std::istreambuf_iterator<char>()); + _cache = Data::fromJSON(cacheStr); + } + + // get md5 of current document + std::stringstream ss; + ss << *_document; + _md5 = md5(ss.str()); + + if (_cache.compound.find("InterpreterImpl") != _cache.compound.end() && + _cache.compound["InterpreterImpl"].compound.find("md5") != _cache.compound["InterpreterImpl"].compound.end() && + _cache.compound["InterpreterImpl"].compound["md5"].atom != _md5) { + + // that's not our cache! + _cache.clear(); + } + + _cache.compound["InterpreterImpl"].compound["baseURL"] = Data(std::string(_baseURL)); + _cache.compound["InterpreterImpl"].compound["md5"] = Data(_md5); + } + // register io processors std::map<std::string, IOProcessorImpl*> ioProcs = _factory->getIOProcessors(); for (auto ioProcIter = ioProcs.begin(); ioProcIter != ioProcs.end(); ioProcIter++) { diff --git a/src/uscxml/interpreter/InterpreterImpl.h b/src/uscxml/interpreter/InterpreterImpl.h index 1243f7c..0efc70a 100644 --- a/src/uscxml/interpreter/InterpreterImpl.h +++ b/src/uscxml/interpreter/InterpreterImpl.h @@ -143,6 +143,10 @@ public: return Interpreter(shared_from_this()); } + virtual Data& getCache() { + return _cache; + } + /** DataModelCallbacks */ @@ -284,6 +288,7 @@ protected: Factory* _factory; URL _baseURL; + std::string _md5; MicroStep _microStepper; DataModel _dataModel; @@ -305,6 +310,8 @@ protected: std::set<std::string> _autoForwarders; std::set<InterpreterMonitor*> _monitors; + Data _cache; + private: void setupDOM(); }; diff --git a/src/uscxml/interpreter/MicroStepImpl.h b/src/uscxml/interpreter/MicroStepImpl.h index 1544e78..e3f8299 100644 --- a/src/uscxml/interpreter/MicroStepImpl.h +++ b/src/uscxml/interpreter/MicroStepImpl.h @@ -60,6 +60,9 @@ public: virtual std::set<InterpreterMonitor*> getMonitors() = 0; virtual Interpreter getInterpreter() = 0; virtual Logger getLogger() = 0; + + /** Saved State */ + virtual Data& getCache() = 0; }; /** |