summaryrefslogtreecommitdiffstats
path: root/test/src/test-c-machine.cpp
diff options
context:
space:
mode:
authorStefan Radomski <sradomski@mintwerk.de>2016-02-04 00:10:57 (GMT)
committerStefan Radomski <sradomski@mintwerk.de>2016-02-04 00:10:57 (GMT)
commit7afc6a257e193986c9305364701085e65c4ccea5 (patch)
tree4bc967a50d872e0267d5cf970ab9b88d87dee16b /test/src/test-c-machine.cpp
parent0b313e00915b31c8c03980b7225f82ac2e9513e6 (diff)
downloaduscxml-7afc6a257e193986c9305364701085e65c4ccea5.zip
uscxml-7afc6a257e193986c9305364701085e65c4ccea5.tar.gz
uscxml-7afc6a257e193986c9305364701085e65c4ccea5.tar.bz2
Preliminary support for SCXML invocations in generated C machines
Diffstat (limited to 'test/src/test-c-machine.cpp')
-rw-r--r--test/src/test-c-machine.cpp1067
1 files changed, 578 insertions, 489 deletions
diff --git a/test/src/test-c-machine.cpp b/test/src/test-c-machine.cpp
index 11bb1c7..1dc95d4 100644
--- a/test/src/test-c-machine.cpp
+++ b/test/src/test-c-machine.cpp
@@ -24,7 +24,7 @@
#include "uscxml/concurrency/Timer.h"
//#include "uscxml/DOMUtils.h"
#include "uscxml/Factory.h"
-#include "uscxml/InterpreterInfo.h"
+//#include "uscxml/Interpreter.h"
#include "uscxml/UUID.h"
#include "uscxml/concurrency/DelayedEventQueue.h"
@@ -34,18 +34,98 @@
# include "uscxml/plugins/DataModel.h"
# endif
-#define USER_DATA(ctx) ((GenCInterpreterInfo*)(((scxml_ctx*)ctx)->user_data))
+#define USER_DATA(ctx) ((StateMachine*)(((scxml_ctx*)ctx)->user_data))
using namespace uscxml;
-typedef struct scxml_foreach_info scxml_foreach_info;
-struct scxml_foreach_info {
- size_t iterations;
- size_t currIteration;
-};
-
-class GenCInterpreterInfo : public InterpreterInfo {
+class StateMachine : public InterpreterInfo {
public:
+ StateMachine(const scxml_machine* machine) : parentMachine(NULL), topMostMachine(NULL) {
+ init(machine);
+ allMachines[sessionId] = this;
+ topMostMachine = this;
+ currentMachine = allMachines.begin();
+ }
+
+ StateMachine(StateMachine* parent, const scxml_machine* machine) {
+ init(machine);
+ parentMachine = parent;
+ topMostMachine = parent->topMostMachine;
+ }
+
+ void init(const scxml_machine* machine) {
+ sessionId = UUID::getUUID();
+
+ // clear and initialize machine context
+ memset(&ctx, 0, sizeof(scxml_ctx));
+ ctx.machine = machine;
+ ctx.user_data = (void*)this;
+
+ // register callbacks with scxml context
+ ctx.is_enabled = &isEnabled;
+ ctx.is_true = &isTrue;
+ ctx.raise_done_event = &raiseDoneEvent;
+ ctx.invoke = &invoke;
+ ctx.exec_content_send = &execContentSend;
+ ctx.exec_content_raise = &execContentRaise;
+ ctx.exec_content_cancel = &execContentCancel;
+ ctx.exec_content_log = &execContentLog;
+ ctx.exec_content_assign = &execContentAssign;
+ ctx.exec_content_foreach_init = &execContentForeachInit;
+ ctx.exec_content_foreach_next = &execContentForeachNext;
+ ctx.exec_content_foreach_done = &execContentForeachDone;
+ ctx.dequeue_external = &dequeueExternal;
+ ctx.dequeue_internal = &dequeueInternal;
+ ctx.exec_content_init = &execContentInit;
+ ctx.exec_content_script = &execContentScript;
+
+ name = machine->name;
+
+ delayQueue.start();
+ dataModel = Factory::getInstance()->createDataModel(machine->datamodel, this);
+ }
+
+ virtual ~StateMachine() {
+ delayQueue.stop();
+ }
+
+ bool hasPendingWork() {
+ return (iq.size() > 0 ||
+ eq.size() > 0 ||
+ ctx.flags & SCXML_CTX_SPONTANEOUS ||
+ ctx.flags == SCXML_CTX_PRISTINE ||
+ memcmp(ctx.config, ctx.invocations, sizeof(ctx.config)) != 0);
+ }
+
+ bool isDone() {
+ return ctx.flags & SCXML_CTX_TOP_LEVEL_FINAL;
+ }
+
+ void reset() {
+ sessionId = UUID::getUUID();
+ iq.clear();
+ eq.clear();
+ delayQueue.cancelAllEvents();
+
+ dataModel = Factory::getInstance()->createDataModel(ctx.machine->datamodel, this);
+
+ }
+
+ int step() {
+ // advance current machine if there are multiple
+ currentMachine++;
+ if (currentMachine == allMachines.end())
+ currentMachine = allMachines.begin();
+
+ StateMachine* toRun = currentMachine->second;
+ if (!toRun->hasPendingWork()) {
+ return SCXML_ERR_IDLE;
+ }
+
+ return scxml_step(&toRun->ctx);
+ }
+
+ // InterpreterInfo
NameSpaceInfo getNameSpaceInfo() const {
return nsInfo;
}
@@ -58,524 +138,568 @@ public:
const std::map<std::string, IOProcessor>& getIOProcessors() {
return ioProcs;
}
- bool isInState(const std::string& stateId) {
- for (int i = 0 ; i < SCXML_NUMBER_STATES; i++) {
- if (scxml_states[i].name != NULL && BIT_HAS(i, ctx->config) && stateId == scxml_states[i].name)
- return true;
- }
- return false;
+ const std::map<std::string, Invoker>& getInvokers() {
+ return invokers;
}
Arabica::DOM::Document<std::string> getDocument() const {
return document;
}
- const std::map<std::string, Invoker>& getInvokers() {
- return invokers;
- }
-
- NameSpaceInfo nsInfo;
- std::string name;
- std::string sessionId;
- std::map<std::string, IOProcessor> ioProcs;
- std::map<std::string, Invoker> invokers;
- Arabica::DOM::Document<std::string> document;
- scxml_ctx* ctx;
- DataModel datamodel;
-
- std::map<const scxml_elem_foreach*, scxml_foreach_info*> foreachInfo;
- std::deque<Event*> iq;
- std::deque<Event*> eq;
-
- DelayedEventQueue delayQueue;
- std::map<std::string, SendRequest*> sendIds;
- tthread::condition_variable monitor;
- tthread::mutex mutex;
-};
+ bool isInState(const std::string& stateId) {
+ for (int i = 0 ; i < ctx.machine->nr_states; i++) {
+ if (ctx.machine->states[i].name != NULL && BIT_HAS(i, ctx.config) && stateId == ctx.machine->states[i].name)
+ return true;
+ }
+ return false;
+ }
-int matches(const char* desc, const char* event) {
- const char* dPtr = desc;
- const char* ePtr = event;
- while(*dPtr != 0) {
+ // callbacks for scxml context
- if (*dPtr == '*' && *ePtr != 0) // something following
- return true;
-
- // descriptor differs from event name
- if (*dPtr != *ePtr) {
- // move to next descriptor
- while(*dPtr != ' ' && *dPtr != 0) {
- dPtr++;
- }
- if (*dPtr == 0)
+ static int isEnabled(const scxml_ctx* ctx, const scxml_transition* t, const void* e) {
+ Event* event = (Event*)e;
+ if (event == NULL) {
+ if (t->event == NULL) {
+ // spontaneous transition, null event
+ if (t->condition != NULL)
+ return isTrue(ctx, t->condition);
+ return true;
+ } else {
+ // spontaneous transition, but real event
return false;
- dPtr++;
- ePtr = event;
- } else {
- // move both pointers one character
- dPtr++;
- ePtr++;
-
+ }
}
- // descriptor is done, return match
- if (((*dPtr == 0 || *dPtr == ' ') && (*ePtr == 0 || *ePtr == ' ')) || // exact match, end of string
- (*dPtr == ' ' && *ePtr == '.') || (*dPtr == 0 && *ePtr == '.')) // prefix match
+ // real transition, real event
+ if (nameMatch(t->event, event->name.c_str())) {
+ if (t->condition != NULL)
+ return isTrue(ctx, t->condition);
return true;
+ }
+ return false;
}
- return false;
-}
-
-int exec_content_raise(const scxml_ctx* ctx, const char* event) {
- Event* e = new Event();
- e->name = event;
- if (boost::starts_with(e->name, "error.")) {
- e->eventType = Event::PLATFORM;
- } else {
- e->eventType = Event::INTERNAL;
+ static int isTrue(const scxml_ctx* ctx, const char* expr) {
+ try {
+ return USER_DATA(ctx)->dataModel.evalAsBool(expr);
+ } catch (Event e) {
+ execContentRaise(ctx, e.name.c_str());
+ }
+ return false;
}
-#ifdef SCXML_VERBOSE
- printf("Raising Internal Event: %s\n", e->name.c_str());
-#endif
- USER_DATA(ctx)->iq.push_back(e);
- return SCXML_ERR_OK;
-}
-
-int is_true(const scxml_ctx* ctx, const char* expr) {
- try {
- return USER_DATA(ctx)->datamodel.evalAsBool(expr);
- } catch (Event e) {
- exec_content_raise(ctx, e.name.c_str());
- }
- return false;
-}
+ static int invoke(const scxml_ctx* ctx, const scxml_state* s, const scxml_elem_invoke* invocation, uint8_t uninvoke) {
+ if (invocation->machine != NULL) {
+ StateMachine* INSTANCE = USER_DATA(ctx);
+ // invoke a nested SCXML machine
+ StateMachine* invokedMachine = new StateMachine(INSTANCE, invocation->machine);
+ invokedMachine->invocation = invocation;
+ invokedMachine->invokeId = invocation->id;
+ assert(invocation->id != NULL);
-int is_enabled(const scxml_ctx* ctx, const scxml_transition* t, const void* e) {
- Event* event = (Event*)e;
- if (event == NULL) {
- if (t->event == NULL) {
- // spontaneous transition, null event
- if (t->condition != NULL)
- return is_true(ctx, t->condition);
- return true;
- } else {
- // spontaneous transition, but real event
- return false;
+ INSTANCE->topMostMachine->allMachines[invokedMachine->invokeId] = invokedMachine;
}
+ return SCXML_ERR_UNSUPPORTED;
}
- // real transition, real event
- if (matches(t->event, event->name.c_str())) {
- if (t->condition != NULL)
- return is_true(ctx, t->condition);
- return true;
- }
- return false;
-}
-
-int raise_done_event(const scxml_ctx* ctx, const scxml_state* state, const scxml_elem_donedata* donedata) {
- Event* e = new Event();
- e->name = std::string("done.state.") + state->name;
+ static int raiseDoneEvent(const scxml_ctx* ctx, const scxml_state* state, const scxml_elem_donedata* donedata) {
+ Event* e = new Event();
+ e->name = std::string("done.state.") + state->name;
- if (donedata) {
- if (donedata->content != NULL) {
- e->data = Data(donedata->content, Data::VERBATIM);
- } else if (donedata->contentexpr != NULL) {
- try {
- e->data = USER_DATA(ctx)->datamodel.getStringAsData(donedata->contentexpr);
- } catch (Event e) {
- exec_content_raise(ctx, e.name.c_str());
- }
- } else {
- try {
- const scxml_elem_param* param = donedata->params;
- while (param && ELEM_PARAM_IS_SET(param)) {
- Data paramValue;
- if (param->expr != NULL) {
- paramValue = USER_DATA(ctx)->datamodel.getStringAsData(param->expr);
- } else if(param->location) {
- paramValue = USER_DATA(ctx)->datamodel.getStringAsData(param->location);
+ if (donedata) {
+ if (donedata->content != NULL) {
+ e->data = Data(donedata->content, Data::VERBATIM);
+ } else if (donedata->contentexpr != NULL) {
+ try {
+ e->data = USER_DATA(ctx)->dataModel.getStringAsData(donedata->contentexpr);
+ } catch (Event e) {
+ execContentRaise(ctx, e.name.c_str());
+ }
+ } else {
+ try {
+ const scxml_elem_param* param = donedata->params;
+ while (param && ELEM_PARAM_IS_SET(param)) {
+ Data paramValue;
+ if (param->expr != NULL) {
+ paramValue = USER_DATA(ctx)->dataModel.getStringAsData(param->expr);
+ } else if(param->location) {
+ paramValue = USER_DATA(ctx)->dataModel.getStringAsData(param->location);
+ }
+ e->params.insert(std::make_pair(param->name, paramValue));
+ param++;
}
- e->params.insert(std::make_pair(param->name, paramValue));
- param++;
+ } catch (Event e) {
+ execContentRaise(ctx, e.name.c_str());
}
- } catch (Event e) {
- exec_content_raise(ctx, e.name.c_str());
}
}
- }
-
-#ifdef SCXML_VERBOSE
- printf("Raising Done Event: %s\n", e->name.c_str());
-#endif
- USER_DATA(ctx)->iq.push_back(e);
- return SCXML_ERR_OK;
-}
-
-void delayedSend(void* ctx, std::string eventName) {
- tthread::lock_guard<tthread::mutex> lock(USER_DATA(ctx)->mutex);
-
- SendRequest* sr = USER_DATA(ctx)->sendIds[eventName];
- Event* e = new Event(*sr);
- if (sr->target == "#_internal") {
- e->eventType = Event::INTERNAL;
#ifdef SCXML_VERBOSE
- printf("Pushing Internal Event: %s\n", e->name.c_str());
+ printf("Raising Done Event: %s\n", e->name.c_str());
#endif
USER_DATA(ctx)->iq.push_back(e);
- } else {
- e->eventType = Event::EXTERNAL;
-#ifdef SCXML_VERBOSE
- printf("Pushing External Event: %s\n", e->name.c_str());
-#endif
- USER_DATA(ctx)->eq.push_back(e);
+ return SCXML_ERR_OK;
}
- USER_DATA(ctx)->monitor.notify_all();
- delete sr;
-}
-int exec_content_cancel(const scxml_ctx* ctx, const char* sendid, const char* sendidexpr) {
- std::string eventId;
- if (sendid != NULL) {
- eventId = sendid;
- } else if (sendidexpr != NULL) {
- eventId = USER_DATA(ctx)->datamodel.evalAsString(sendidexpr);
- }
+ static int execContentSend(const scxml_ctx* ctx, const scxml_elem_send* send) {
+ SendRequest* e = new SendRequest();
- if (eventId.length() > 0) {
- USER_DATA(ctx)->delayQueue.cancelEvent(eventId);
- } else {
- exec_content_raise(ctx, "error.execution");
- return SCXML_ERR_EXEC_CONTENT;
- }
- return SCXML_ERR_OK;
-}
-
-std::string spaceNormalize(const std::string& text) {
- std::stringstream content;
- std::string seperator;
-
- size_t start = 0;
- for (int i = 0; i < text.size(); i++) {
- if (isspace(text[i])) {
- if (i > 0 && start < i) {
- content << seperator << text.substr(start, i - start);
- seperator = " ";
- }
- while(isspace(text[++i])); // skip whitespaces
- start = i;
- } else if (i + 1 == text.size()) {
- content << seperator << text.substr(start, i + 1 - start);
+ std::string target;
+ if (send->target != NULL) {
+ e->target = send->target;
+ } else if (send->targetexpr != NULL) {
+ e->target = USER_DATA(ctx)->dataModel.evalAsString(send->targetexpr);
+ } else {
+ e->target = "#_external";
}
- }
- return content.str();
-}
+ if (e->target.size() > 0 && (e->target[0] != '#' || e->target[1] != '_')) {
+ delete e;
+ execContentRaise(ctx, "error.execution");
+ return SCXML_ERR_INVALID_TARGET;
+ }
-int exec_content_send(const scxml_ctx* ctx, const scxml_elem_send* send) {
- SendRequest* e = new SendRequest();
+ e->origintype = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor";
+ e->origin = e->target;
- std::string target;
- if (send->target != NULL) {
- e->target = send->target;
- } else if (send->targetexpr != NULL) {
- e->target = USER_DATA(ctx)->datamodel.evalAsString(send->targetexpr);
- } else {
- e->target = "#_external";
- }
+ try {
+ if (send->type != NULL) {
+ e->type = send->type;
+ } else if (send->typeexpr != NULL) {
+ e->type = USER_DATA(ctx)->dataModel.evalAsString(send->typeexpr);
+ } else {
+ e->type = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor";
+ }
+ } catch (Event exc) {
+ execContentRaise(ctx, exc.name.c_str());
+ delete e;
+ return SCXML_ERR_EXEC_CONTENT;
+ }
- if (e->target.size() > 0 && (e->target[0] != '#' || e->target[1] != '_')) {
- delete e;
- exec_content_raise(ctx, "error.execution");
- return SCXML_ERR_INVALID_TARGET;
- }
+ // only one somewhat supported
+ if (e->type != "http://www.w3.org/TR/scxml/#SCXMLEventProcessor") {
+ delete e;
+ execContentRaise(ctx, "error.execution");
+ return SCXML_ERR_INVALID_TARGET;
+ }
- e->origintype = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor";
- e->origin = e->target;
+ e->origintype = e->type;
- try {
- if (send->type != NULL) {
- e->type = send->type;
- } else if (send->typeexpr != NULL) {
- e->type = USER_DATA(ctx)->datamodel.evalAsString(send->typeexpr);
+ if (send->eventexpr != NULL) {
+ e->name = USER_DATA(ctx)->dataModel.evalAsString(send->eventexpr);
} else {
- e->type = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor";
+ e->name = send->event;
}
- } catch (Event exc) {
- exec_content_raise(ctx, exc.name.c_str());
- delete e;
- return SCXML_ERR_EXEC_CONTENT;
- }
- // only one somewhat supported
- if (e->type != "http://www.w3.org/TR/scxml/#SCXMLEventProcessor") {
- delete e;
- exec_content_raise(ctx, "error.execution");
- return SCXML_ERR_INVALID_TARGET;
- }
+ try {
+ const scxml_elem_param* param = send->params;
+ while (param && ELEM_PARAM_IS_SET(param)) {
+ Data paramValue;
+ if (param->expr != NULL) {
+ paramValue = USER_DATA(ctx)->dataModel.getStringAsData(param->expr);
+ } else if(param->location) {
+ paramValue = USER_DATA(ctx)->dataModel.getStringAsData(param->location);
+ }
+ e->params.insert(std::make_pair(param->name, paramValue));
+ param++;
+ }
+ } catch (Event e) {
+ execContentRaise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
+ }
- e->origintype = e->type;
+ try {
+ if (send->namelist != NULL) {
+ const char* bPtr = &send->namelist[0];
+ const char* ePtr = bPtr;
+ while(*ePtr != '\0') {
+ ePtr++;
+ if (*ePtr == ' ' || *ePtr == '\0') {
+ std::string key(bPtr, ePtr - bPtr);
+ e->params.insert(std::make_pair(key, USER_DATA(ctx)->dataModel.getStringAsData(key)));
+ if (*ePtr == '\0')
+ break;
+ bPtr = ++ePtr;
+ }
+ }
+ }
+ } catch (Event e) {
+ execContentRaise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
+ }
- if (send->eventexpr != NULL) {
- e->name = USER_DATA(ctx)->datamodel.evalAsString(send->eventexpr);
- } else {
- e->name = send->event;
- }
+ if (send->content != NULL) {
+ // will it parse as json?
+ Data d = Data::fromJSON(send->content);
+ if (!d.empty()) {
+ e->data = d;
+ } else {
+ e->data = Data(spaceNormalize(send->content), Data::VERBATIM);
+ }
+ }
- try {
- const scxml_elem_param* param = send->params;
- while (param && ELEM_PARAM_IS_SET(param)) {
- Data paramValue;
- if (param->expr != NULL) {
- paramValue = USER_DATA(ctx)->datamodel.getStringAsData(param->expr);
- } else if(param->location) {
- paramValue = USER_DATA(ctx)->datamodel.getStringAsData(param->location);
+ std::string sendid;
+ if (send->id != NULL) {
+ sendid = send->id;
+ e->sendid = sendid;
+ } else {
+ sendid = UUID::getUUID();
+ if (send->idlocation != NULL) {
+ USER_DATA(ctx)->dataModel.assign(send->idlocation, Data(sendid, Data::VERBATIM));
+ } else {
+ e->hideSendId = true;
}
- e->params.insert(std::make_pair(param->name, paramValue));
- param++;
- }
- } catch (Event e) {
- exec_content_raise(ctx, e.name.c_str());
- return SCXML_ERR_EXEC_CONTENT;
- }
-
- try {
- if (send->namelist != NULL) {
- const char* bPtr = &send->namelist[0];
- const char* ePtr = bPtr;
- while(*ePtr != '\0') {
- ePtr++;
- if (*ePtr == ' ' || *ePtr == '\0') {
- std::string key(bPtr, ePtr - bPtr);
- e->params.insert(std::make_pair(key, USER_DATA(ctx)->datamodel.getStringAsData(key)));
- if (*ePtr == '\0')
- break;
- bPtr = ++ePtr;
- }
+ }
+
+ size_t delayMs = 0;
+ std::string delay;
+ if (send->delayexpr != NULL) {
+ delay = USER_DATA(ctx)->dataModel.evalAsString(send->delayexpr);
+ } else if (send->delay != NULL) {
+ delay = send->delay;
+ }
+ if (delay.size() > 0) {
+ boost::trim(delay);
+
+ NumAttr delayAttr(delay);
+ if (iequals(delayAttr.unit, "ms")) {
+ delayMs = strTo<uint32_t>(delayAttr.value);
+ } else if (iequals(delayAttr.unit, "s")) {
+ delayMs = strTo<double>(delayAttr.value) * 1000;
+ } else if (delayAttr.unit.length() == 0) { // unit less delay is interpreted as milliseconds
+ delayMs = strTo<uint32_t>(delayAttr.value);
+ } else {
+ std::cerr << "Cannot make sense of delay value " << delay << ": does not end in 's' or 'ms'";
}
}
- } catch (Event e) {
- exec_content_raise(ctx, e.name.c_str());
- return SCXML_ERR_EXEC_CONTENT;
- }
- if (send->content != NULL) {
- // will it parse as json?
- Data d = Data::fromJSON(send->content);
- if (!d.empty()) {
- e->data = d;
- } else {
- e->data = Data(spaceNormalize(send->content), Data::VERBATIM);
+ if (USER_DATA(ctx)->invokeId.size() > 0) {
+ e->invokeid = USER_DATA(ctx)->invokeId;
}
- }
- std::string sendid;
- if (send->id != NULL) {
- sendid = send->id;
- e->sendid = sendid;
- } else {
- sendid = UUID::getUUID();
- if (send->idlocation != NULL) {
- USER_DATA(ctx)->datamodel.assign(send->idlocation, Data(sendid, Data::VERBATIM));
+ USER_DATA(ctx)->sendIds[sendid] = e;
+ if (delayMs > 0) {
+ USER_DATA(ctx)->delayQueue.addEvent(sendid, delayedSend, delayMs, (void*)ctx);
} else {
- e->hideSendId = true;
+ delayedSend((void*)ctx, sendid);
}
+
+ return SCXML_ERR_OK;
}
+ static int execContentRaise(const scxml_ctx* ctx, const char* event) {
+ Event* e = new Event();
+ e->name = event;
- size_t delayMs = 0;
- std::string delay;
- if (send->delayexpr != NULL) {
- delay = USER_DATA(ctx)->datamodel.evalAsString(send->delayexpr);
- } else if (send->delay != NULL) {
- delay = send->delay;
+ if (boost::starts_with(e->name, "error.")) {
+ e->eventType = Event::PLATFORM;
+ } else {
+ e->eventType = Event::INTERNAL;
+ }
+ USER_DATA(ctx)->iq.push_back(e);
+ return SCXML_ERR_OK;
}
- if (delay.size() > 0) {
- boost::trim(delay);
- NumAttr delayAttr(delay);
- if (iequals(delayAttr.unit, "ms")) {
- delayMs = strTo<uint32_t>(delayAttr.value);
- } else if (iequals(delayAttr.unit, "s")) {
- delayMs = strTo<double>(delayAttr.value) * 1000;
- } else if (delayAttr.unit.length() == 0) { // unit less delay is interpreted as milliseconds
- delayMs = strTo<uint32_t>(delayAttr.value);
+ static int execContentCancel(const scxml_ctx* ctx, const char* sendid, const char* sendidexpr) {
+ std::string eventId;
+ if (sendid != NULL) {
+ eventId = sendid;
+ } else if (sendidexpr != NULL) {
+ eventId = USER_DATA(ctx)->dataModel.evalAsString(sendidexpr);
+ }
+
+ if (eventId.length() > 0) {
+ USER_DATA(ctx)->delayQueue.cancelEvent(eventId);
} else {
- std::cerr << "Cannot make sense of delay value " << delay << ": does not end in 's' or 'ms'";
+ execContentRaise(ctx, "error.execution");
+ return SCXML_ERR_EXEC_CONTENT;
}
+ return SCXML_ERR_OK;
}
- USER_DATA(ctx)->sendIds[sendid] = e;
- if (delayMs > 0) {
- USER_DATA(ctx)->delayQueue.addEvent(sendid, delayedSend, delayMs, (void*)ctx);
- } else {
- delayedSend((void*)ctx, sendid);
+ static int execContentLog(const scxml_ctx* ctx, const char* label, const char* expr) {
+ try {
+ if (label != NULL) {
+ printf("%s%s", label, (expr != NULL ? ": " : ""));
+ }
+ if (expr != NULL) {
+ std::string msg = USER_DATA(ctx)->dataModel.evalAsString(expr);
+ printf("%s", msg.c_str());
+ }
+ if (label != NULL || expr != NULL) {
+ printf("\n");
+ }
+ } catch (Event e) {
+ execContentRaise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+ return SCXML_ERR_OK;
}
- return SCXML_ERR_OK;
-}
-
-int exec_content_init(const scxml_ctx* ctx, const scxml_elem_data* data) {
- while(ELEM_DATA_IS_SET(data)) {
- Data d;
- if (data->expr != NULL) {
- d = Data(data->expr, Data::INTERPRETED);
- } else if (data->content != NULL) {
- d = Data(data->content, Data::INTERPRETED);
- } else {
- d = Data("undefined", Data::INTERPRETED);
+ static int execContentAssign(const scxml_ctx* ctx, const char* location, const char* expr) {
+ std::string key = location;
+ if (key == "_sessionid" || key == "_name" || key == "_ioprocessors" || key == "_invokers" || key == "_event") {
+ execContentRaise(ctx, "error.execution");
+ return SCXML_ERR_EXEC_CONTENT;
}
+
try {
- USER_DATA(ctx)->datamodel.init(data->id, d);
+ Data d(expr, Data::INTERPRETED);
+ USER_DATA(ctx)->dataModel.assign(key, d);
} catch (Event e) {
- exec_content_raise(ctx, e.name.c_str());
+ execContentRaise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
}
- data++;
+ return SCXML_ERR_OK;
}
- return SCXML_ERR_OK;
-}
-int exec_content_assign(const scxml_ctx* ctx, const char* location, const char* expr) {
- std::string key = location;
- if (key == "_sessionid" || key == "_name" || key == "_ioprocessors" || key == "_invokers" || key == "_event") {
- exec_content_raise(ctx, "error.execution");
- return SCXML_ERR_EXEC_CONTENT;
- }
+ static int execContentForeachInit(const scxml_ctx* ctx, const scxml_elem_foreach* foreach) {
+ try {
+ scxml_foreach_info* feInfo = (scxml_foreach_info*)malloc(sizeof(scxml_foreach_info));
+ USER_DATA(ctx)->foreachInfo[foreach] = feInfo;
- try {
- Data d(expr, Data::INTERPRETED);
- USER_DATA(ctx)->datamodel.assign(key, d);
- } catch (Event e) {
- exec_content_raise(ctx, e.name.c_str());
- return SCXML_ERR_EXEC_CONTENT;
+ feInfo->iterations = USER_DATA(ctx)->dataModel.getLength(foreach->array);
+ feInfo->currIteration = 0;
+ } catch (Event e) {
+ execContentRaise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+ return SCXML_ERR_OK;
}
- return SCXML_ERR_OK;
-}
-int exec_content_foreach_init(const scxml_ctx* ctx, const scxml_elem_foreach* foreach) {
- try {
- scxml_foreach_info* feInfo = (scxml_foreach_info*)malloc(sizeof(scxml_foreach_info));
- USER_DATA(ctx)->foreachInfo[foreach] = feInfo;
-
- feInfo->iterations = USER_DATA(ctx)->datamodel.getLength(foreach->array);
- feInfo->currIteration = 0;
- } catch (Event e) {
- exec_content_raise(ctx, e.name.c_str());
- return SCXML_ERR_EXEC_CONTENT;
+ static int execContentForeachNext(const scxml_ctx* ctx, const scxml_elem_foreach* foreach) {
+ try {
+ scxml_foreach_info* feInfo = USER_DATA(ctx)->foreachInfo[foreach];
+ if (feInfo->currIteration < feInfo->iterations) {
+ USER_DATA(ctx)->dataModel.setForeach((foreach->item != NULL ? foreach->item : ""),
+ (foreach->array != NULL ? foreach->array : ""),
+ (foreach->index != NULL ? foreach->index : ""),
+ feInfo->currIteration);
+ feInfo->currIteration++;
+ return SCXML_ERR_OK;
+ }
+ } catch (Event e) {
+ execContentRaise(ctx, e.name.c_str());
+ free(USER_DATA(ctx)->foreachInfo[foreach]);
+ USER_DATA(ctx)->foreachInfo.erase(foreach);
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+ return SCXML_ERR_FOREACH_DONE;
}
- return SCXML_ERR_OK;
-}
-int exec_content_foreach_next(const scxml_ctx* ctx, const scxml_elem_foreach* foreach) {
- try {
- scxml_foreach_info* feInfo = USER_DATA(ctx)->foreachInfo[foreach];
- if (feInfo->currIteration < feInfo->iterations) {
- USER_DATA(ctx)->datamodel.setForeach((foreach->item != NULL ? foreach->item : ""),
- (foreach->array != NULL ? foreach->array : ""),
- (foreach->index != NULL ? foreach->index : ""),
- feInfo->currIteration);
- feInfo->currIteration++;
- return SCXML_ERR_OK;
- }
- } catch (Event e) {
- exec_content_raise(ctx, e.name.c_str());
+ static int execContentForeachDone(const scxml_ctx* ctx, const scxml_elem_foreach* foreach) {
free(USER_DATA(ctx)->foreachInfo[foreach]);
USER_DATA(ctx)->foreachInfo.erase(foreach);
- return SCXML_ERR_EXEC_CONTENT;
+ return SCXML_ERR_OK;
+ }
+
+ static int execContentInit(const scxml_ctx* ctx, const scxml_elem_data* data) {
+ while(ELEM_DATA_IS_SET(data)) {
+ Data d;
+ if (data->expr != NULL) {
+ d = Data(data->expr, Data::INTERPRETED);
+ } else if (data->content != NULL) {
+ d = Data(data->content, Data::INTERPRETED);
+ } else {
+ d = Data("undefined", Data::INTERPRETED);
+ }
+ try {
+ USER_DATA(ctx)->dataModel.init(data->id, d);
+ } catch (Event e) {
+ execContentRaise(ctx, e.name.c_str());
+ }
+ data++;
+ }
+ return SCXML_ERR_OK;
}
- return SCXML_ERR_FOREACH_DONE;
-}
-int exec_content_foreach_done(const scxml_ctx* ctx, const scxml_elem_foreach* foreach) {
- free(USER_DATA(ctx)->foreachInfo[foreach]);
- USER_DATA(ctx)->foreachInfo.erase(foreach);
- return SCXML_ERR_OK;
-}
-
-int exec_content_log(const scxml_ctx* ctx, const char* label, const char* expr) {
- try {
- if (label != NULL) {
- printf("%s%s", label, (expr != NULL ? ": " : ""));
- }
- if (expr != NULL) {
- std::string msg = USER_DATA(ctx)->datamodel.evalAsString(expr);
- printf("%s", msg.c_str());
+ static int execContentScript(const scxml_ctx* ctx, const char* src, const char* content) {
+ if (content != NULL) {
+ USER_DATA(ctx)->dataModel.eval(Arabica::DOM::Element<std::string>(), content);
+ } else if (src != NULL) {
+ return SCXML_ERR_UNSUPPORTED;
}
- if (label != NULL || expr != NULL) {
- printf("\n");
- }
- } catch (Event e) {
- exec_content_raise(ctx, e.name.c_str());
- return SCXML_ERR_EXEC_CONTENT;
+ return SCXML_ERR_OK;
}
- return SCXML_ERR_OK;
-}
-int exec_content_script(const scxml_ctx* ctx, const char* src, const char* content) {
- if (content != NULL) {
- USER_DATA(ctx)->datamodel.eval(Arabica::DOM::Element<std::string>(), content);
- } else if (src != NULL) {
- return SCXML_ERR_UNSUPPORTED;
- }
- return SCXML_ERR_OK;
-}
+ static void* dequeueExternal(const scxml_ctx* ctx) {
+ tthread::lock_guard<tthread::mutex> lock(USER_DATA(ctx)->mutex);
+ if (USER_DATA(ctx)->eq.size() == 0)
+ return NULL;
+
+ Event* e = USER_DATA(ctx)->eq.front();
+ USER_DATA(ctx)->eq.pop_front();
+ USER_DATA(ctx)->dataModel.setEvent(*e);
-void* dequeue_external(const scxml_ctx* ctx) {
- tthread::lock_guard<tthread::mutex> lock(USER_DATA(ctx)->mutex);
- while (USER_DATA(ctx)->eq.size() == 0) {
- USER_DATA(ctx)->monitor.wait(USER_DATA(ctx)->mutex);
+ if (e->invokeid.size() > 0) {
+ // we need to check for finalize content
+ StateMachine* invokedMachine = USER_DATA(ctx)->allMachines[e->invokeid];
+ if (invokedMachine->invocation->finalize != NULL)
+ invokedMachine->invocation->finalize(ctx,
+ invokedMachine->invocation,
+ e);
+ }
+
+#ifdef SCXML_VERBOSE
+ printf("Popping External Event: %s\n", e->name.c_str());
+#endif
+ return e;
}
- Event* e = USER_DATA(ctx)->eq.front();
- USER_DATA(ctx)->eq.pop_front();
- USER_DATA(ctx)->datamodel.setEvent(*e);
+
+ static void* dequeueInternal(const scxml_ctx* ctx) {
+ if (USER_DATA(ctx)->iq.size() == 0)
+ return NULL;
+ Event* e = USER_DATA(ctx)->iq.front();
+ USER_DATA(ctx)->iq.pop_front();
+ USER_DATA(ctx)->dataModel.setEvent(*e);
#ifdef SCXML_VERBOSE
- printf("Popping External Event: %s\n", e->name.c_str());
+ printf("Popping Internal Event: %s\n", e->name.c_str());
#endif
- return e;
-}
+ return e;
+ }
+
+ static void delayedSend(void* ctx, std::string eventName) {
+ tthread::lock_guard<tthread::mutex> lock(USER_DATA(ctx)->mutex);
+
+ SendRequest* sr = USER_DATA(ctx)->sendIds[eventName];
+ Event* e = new Event(*sr);
-void* dequeue_internal(const scxml_ctx* ctx) {
- if (USER_DATA(ctx)->iq.size() == 0)
- return NULL;
- Event* e = USER_DATA(ctx)->iq.front();
- USER_DATA(ctx)->iq.pop_front();
- USER_DATA(ctx)->datamodel.setEvent(*e);
+ if (sr->target == "#_internal") {
+ e->eventType = Event::INTERNAL;
#ifdef SCXML_VERBOSE
- printf("Popping Internal Event: %s\n", e->name.c_str());
+ printf("Pushing Internal Event: %s\n", e->name.c_str());
#endif
- return e;
-}
+ USER_DATA(ctx)->iq.push_back(e);
+ } else if (sr->target == "#_parent") {
+ e->eventType = Event::EXTERNAL;
+ if (USER_DATA(ctx)->parentMachine != NULL) {
+ USER_DATA(ctx)->parentMachine->eq.push_back(e);
+ }
+ // TODO: handle invalid parent
+ } else {
+ e->eventType = Event::EXTERNAL;
+#ifdef SCXML_VERBOSE
+ printf("Pushing External Event: %s\n", e->name.c_str());
+#endif
+ USER_DATA(ctx)->eq.push_back(e);
+ }
+ USER_DATA(ctx)->monitor.notify_all();
+ delete sr;
+ }
-int main(int argc, char** argv) {
+ static std::string spaceNormalize(const std::string& text) {
+ std::stringstream content;
+ std::string seperator;
- std::cout << "sizeof(scxml_state): " << sizeof(scxml_state) << std::endl;
- std::cout << "sizeof(scxml_transition): " << sizeof(scxml_transition) << std::endl;
- std::cout << "sizeof(scxml_ctx): " << sizeof(scxml_ctx) << std::endl;
+ size_t start = 0;
+ for (int i = 0; i < text.size(); i++) {
+ if (isspace(text[i])) {
+ if (i > 0 && start < i) {
+ content << seperator << text.substr(start, i - start);
+ seperator = " ";
+ }
+ while(isspace(text[++i])); // skip whitespaces
+ start = i;
+ } else if (i + 1 == text.size()) {
+ content << seperator << text.substr(start, i + 1 - start);
+ }
+ }
+ return content.str();
+ }
-#ifdef APPLE
- mach_timebase_info_data_t timebase_info;
- mach_timebase_info(&timebase_info);
-
- const uint64_t NANOS_PER_MSEC = 1000000ULL;
- double clock2abs = ((double)timebase_info.denom / (double)timebase_info.numer) * NANOS_PER_MSEC;
-
- thread_time_constraint_policy_data_t policy;
- policy.period = 0;
- policy.computation = (uint32_t)(5 * clock2abs); // 5 ms of work
- policy.constraint = (uint32_t)(10 * clock2abs);
- policy.preemptible = FALSE;
-
- int kr = thread_policy_set(pthread_mach_thread_np(pthread_self()),
- THREAD_TIME_CONSTRAINT_POLICY,
- (thread_policy_t)&policy,
- THREAD_TIME_CONSTRAINT_POLICY_COUNT);
- if (kr != KERN_SUCCESS) {
- mach_error("thread_policy_set:", kr);
- exit(1);
+ // TODO: isolate InterpreterImpl to reduce header deps on libxml/parser.h
+ static bool nameMatch(const std::string& eventDescs, const std::string& eventName) {
+ if(eventDescs.length() == 0 || eventName.length() == 0)
+ return false;
+
+ // naive case of single descriptor and exact match
+ if (iequals(eventDescs, eventName))
+ return true;
+
+ size_t start = 0;
+ std::string eventDesc;
+ for (int i = 0; i < eventDescs.size(); i++) {
+ if (isspace(eventDescs[i])) {
+ if (i > 0 && start < i - 1) {
+ eventDesc = eventDescs.substr(start, i - start);
+ }
+ while(isspace(eventDescs[++i])); // skip whitespaces
+ start = i;
+ } else if (i + 1 == eventDescs.size()) {
+ eventDesc = eventDescs.substr(start, i + 1 - start);
+ }
+
+ if (eventDesc.size() > 0) {
+ // remove optional trailing .* for CCXML compatibility
+ if (eventDesc.find("*", eventDesc.size() - 1) != std::string::npos)
+ eventDesc = eventDesc.substr(0, eventDesc.size() - 1);
+ if (eventDesc.find(".", eventDesc.size() - 1) != std::string::npos)
+ eventDesc = eventDesc.substr(0, eventDesc.size() - 1);
+
+ // was eventDesc the * wildcard
+ if (eventDesc.size() == 0)
+ return true;
+
+ // eventDesc has to be a real prefix of event now and therefore shorter
+ if (eventDesc.size() > eventName.size())
+ goto NEXT_DESC;
+
+ // are they already equal?
+ if (iequals(eventDesc, eventName))
+ return true;
+
+ if (eventName.find(eventDesc) == 0) {
+ if (eventName.find(".", eventDesc.size()) == eventDesc.size())
+ return true;
+ }
+NEXT_DESC:
+ eventDesc = "";
+ }
+ }
+ return false;
}
-#endif
+
+ std::map<std::string, StateMachine*> allMachines;
+
+ StateMachine* parentMachine;
+ StateMachine* topMostMachine;
+ std::map<std::string, StateMachine* >::iterator currentMachine; // next machine to advance
+
+ int state;
+ scxml_ctx ctx;
+
+ std::string sessionId;
+ std::string name;
+
+ // in case we were invoked
+ std::string invokeId;
+ const scxml_elem_invoke* invocation;
+
+ std::deque<Event*> iq;
+ std::deque<Event*> eq;
+
+ DataModel dataModel;
+
+protected:
+ struct scxml_foreach_info {
+ size_t iterations;
+ size_t currIteration;
+ };
+
+ NameSpaceInfo nsInfo;
+ std::map<std::string, IOProcessor> ioProcs;
+ std::map<std::string, Invoker> invokers;
+ Arabica::DOM::Document<std::string> document;
+
+ DelayedEventQueue delayQueue;
+ std::map<std::string, SendRequest*> sendIds;
+ std::map<const scxml_elem_foreach*, scxml_foreach_info*> foreachInfo;
+
+ tthread::condition_variable monitor;
+ tthread::mutex mutex;
+};
+
+
+int main(int argc, char** argv) {
int err;
size_t benchmarkRuns = 1;
@@ -584,90 +708,56 @@ int main(int argc, char** argv) {
benchmarkRuns = strTo<size_t>(envBenchmarkRuns);
}
-
size_t remainingRuns = benchmarkRuns;
- // setup info object required for datamodel
- GenCInterpreterInfo interpreterInfo;
- interpreterInfo.name = SCXML_MACHINE_NAME;
- interpreterInfo.sessionId = "rfwef";
- interpreterInfo.delayQueue.start();
-
- scxml_ctx ctx;
- interpreterInfo.ctx = &ctx;
-
double avg = 0;
size_t microSteps = 0;
#ifdef BUILD_PROFILING
double avgDm = 0;
#endif
+ StateMachine rootMachine(&scxml_machines[0]);
+
Timer tTotal;
tTotal.start();
while(remainingRuns-- > 0) {
- memset(&ctx, 0, sizeof(scxml_ctx));
-
- // fresh dm (expensive :( )
- interpreterInfo.datamodel = Factory::getInstance()->createDataModel("ecmascript", &interpreterInfo);
-
- // set info object as user data
- ctx.user_data = (void*)&interpreterInfo;
-
- // register callbacks with scxml context
- ctx.is_enabled = &is_enabled;
- ctx.is_true = &is_true;
- ctx.raise_done_event = &raise_done_event;
-
- ctx.exec_content_send = &exec_content_send;
- ctx.exec_content_raise = &exec_content_raise;
- ctx.exec_content_cancel = &exec_content_cancel;
- ctx.exec_content_log = &exec_content_log;
- ctx.exec_content_assign = &exec_content_assign;
- ctx.exec_content_foreach_init = &exec_content_foreach_init;
- ctx.exec_content_foreach_next = &exec_content_foreach_next;
- ctx.exec_content_foreach_done = &exec_content_foreach_done;
- ctx.dequeue_external = &dequeue_external;
- ctx.dequeue_internal = &dequeue_internal;
- ctx.exec_content_init = &exec_content_init;
- ctx.exec_content_script = &exec_content_script;
Timer t;
t.start();
-
microSteps = 0;
- while((err = scxml_step(&ctx)) == SCXML_ERR_OK) {
+
+
+ for (;;) {
+ err = rootMachine.step();
+ if (rootMachine.isDone())
+ break;
t.stop();
microSteps++;
- if (ctx.event != NULL) {
- delete ((Event*)(ctx.event));
- }
+
t.start();
}
microSteps++;
- assert(ctx.flags & SCXML_CTX_TOP_LEVEL_FINAL);
-
+ assert(rootMachine.ctx.flags & SCXML_CTX_TOP_LEVEL_FINAL);
t.stop();
+
avg += t.elapsed;
#ifdef BUILD_PROFILING
- avgDm += interpreterInfo.datamodel.timer.elapsed;
- interpreterInfo.datamodel.timer.elapsed = 0;
+ avgDm += rootMachine.dataModel.timer.elapsed;
+ rootMachine.dataModel.timer.elapsed = 0;
#endif
size_t passIdx = 0;
- for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
- if (scxml_states[i].name && strcmp(scxml_states[i].name, "pass") == 0) {
+ for (int i = 0; i < rootMachine.ctx.machine->nr_states; i++) {
+ if (rootMachine.ctx.machine->states[i].name && strcmp(rootMachine.ctx.machine->states[i].name, "pass") == 0) {
passIdx = i;
break;
}
}
- if(!BIT_HAS(passIdx, ctx.config)) {
+ if(!BIT_HAS(passIdx, rootMachine.ctx.config)) {
std::cerr << "Interpreter did not end in pass" << std::endl;
exit(EXIT_FAILURE);
}
- interpreterInfo.delayQueue.cancelAllEvents();
- interpreterInfo.eq.clear();
- interpreterInfo.iq.clear();
}
tTotal.stop();
std::cout << benchmarkRuns << " iterations" << std::endl;
@@ -676,10 +766,9 @@ int main(int argc, char** argv) {
std::cout << microSteps << " microsteps per iteration" << std::endl;
std::cout << (avg * 1000.0) / ((double)benchmarkRuns * (double)microSteps) << " ms per microstep" << std::endl;
#ifdef BUILD_PROFILING
- std::cout << (avgDm * 1000.0) / (double)benchmarkRuns << " ms in datamodel" << std::endl;
- std::cout << ((avg - avgDm) * 1000.0) / ((double)benchmarkRuns * (double)microSteps) << " ms per microstep \\wo datamodel" << std::endl;
+ std::cout << (avgDm * 1000.0) / (double)benchmarkRuns << " ms in dataModel" << std::endl;
+ std::cout << ((avg - avgDm) * 1000.0) / ((double)benchmarkRuns * (double)microSteps) << " ms per microstep \\wo dataModel" << std::endl;
#endif
- interpreterInfo.delayQueue.stop();
tthread::this_thread::sleep_for(tthread::chrono::milliseconds(100));
return EXIT_SUCCESS;
-} \ No newline at end of file
+}