summaryrefslogtreecommitdiffstats
path: root/test/src/test-c-machine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/src/test-c-machine.cpp')
-rw-r--r--test/src/test-c-machine.cpp375
1 files changed, 302 insertions, 73 deletions
diff --git a/test/src/test-c-machine.cpp b/test/src/test-c-machine.cpp
index a9e5ecb..4bbc653 100644
--- a/test/src/test-c-machine.cpp
+++ b/test/src/test-c-machine.cpp
@@ -6,13 +6,22 @@
#include <deque> // deque
#include <boost/algorithm/string.hpp> // trim
-#define SCXML_VERBOSE 1
+//#define SCXML_VERBOSE
+
+#include "uscxml/config.h"
+
+#ifdef APPLE
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <pthread.h>
+#endif
#ifndef AUTOINCLUDE_TEST
#include "test-c-machine.machine.c"
#endif
#include "uscxml/Convenience.h"
+#include "uscxml/concurrency/Timer.h"
//#include "uscxml/DOMUtils.h"
#include "uscxml/Factory.h"
#include "uscxml/InterpreterInfo.h"
@@ -21,6 +30,10 @@
#include "uscxml/concurrency/DelayedEventQueue.h"
#include "uscxml/concurrency/tinythread.h"
+#ifdef BUILD_PROFILING
+# include "uscxml/plugins/DataModel.h"
+# endif
+
#define USER_DATA(ctx) ((GenCInterpreterInfo*)(((scxml_ctx*)ctx)->user_data))
using namespace uscxml;
@@ -82,33 +95,49 @@ public:
int matches(const char* desc, const char* event) {
const char* dPtr = desc;
const char* ePtr = event;
- while(true) {
+ while(*dPtr != 0) {
- // next event descriptor
- if (*dPtr == ' ') {
+ 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)
+ return false;
dPtr++;
ePtr = event;
- continue;
+ } else {
+ // move both pointers one character
+ dPtr++;
+ ePtr++;
+
}
// descriptor is done, return match
- if (*dPtr == 0 || *dPtr == '*')
+ if (((*dPtr == 0 || *dPtr == ' ') && (*ePtr == 0 || *ePtr == ' ')) || // exact match, end of string
+ (*dPtr == ' ' && *ePtr == '.') || (*dPtr == 0 && *ePtr == '.')) // prefix match
return true;
-
- // descriptor differs from event name
- if (*dPtr != *ePtr)
- return false;
-
- // move both pointers one character
- dPtr++;
- ePtr++;
}
+ return false;
}
int exec_content_raise(const scxml_ctx* ctx, const char* event) {
Event* e = new Event();
e->name = strdup(event);
+
+ if (boost::starts_with(e->name, "error.")) {
+ e->eventType = Event::PLATFORM;
+ } else {
+ e->eventType = Event::INTERNAL;
+ }
+
+#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;
}
@@ -145,9 +174,42 @@ int is_enabled(const scxml_ctx* ctx, const scxml_transition* t, const void* e) {
return false;
}
-int raise_done_event(const scxml_ctx* ctx, const scxml_state* state) {
- std::string doneName = "done.state.";
- exec_content_raise(ctx, (doneName + state->name).c_str());
+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;
+
+ 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);
+ }
+ e->params.insert(std::make_pair(param->name, paramValue));
+ param++;
+ }
+ } 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;
}
@@ -156,10 +218,16 @@ void delayedSend(void* ctx, std::string eventName) {
SendRequest* e = USER_DATA(ctx)->sendIds[eventName];
if (e->target == "#_internal") {
+ e->eventType = Event::INTERNAL;
+#ifdef SCXML_VERBOSE
printf("Pushing Internal 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);
}
USER_DATA(ctx)->monitor.notify_all();
@@ -182,6 +250,27 @@ int exec_content_cancel(const scxml_ctx* ctx, const char* sendid, const char* se
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);
+ }
+ }
+ return content.str();
+}
+
+
int exec_content_send(const scxml_ctx* ctx, const scxml_elem_send* send) {
SendRequest* e = new SendRequest();
@@ -190,6 +279,8 @@ int exec_content_send(const scxml_ctx* ctx, const scxml_elem_send* send) {
e->target = send->target;
} else if (send->targetexpr != NULL) {
e->target = USER_DATA(ctx)->datamodel->evalAsString(send->targetexpr);
+ } else {
+ e->target = "#_external";
}
if (e->target.size() > 0 && (e->target[0] != '#' || e->target[1] != '_')) {
@@ -198,12 +289,20 @@ int exec_content_send(const scxml_ctx* ctx, const scxml_elem_send* send) {
return SCXML_ERR_INVALID_TARGET;
}
- 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";
+ e->origintype = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor";
+ e->origin = e->target;
+
+ 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 e) {
+ exec_content_raise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
}
// only one somewhat supported
@@ -221,31 +320,67 @@ int exec_content_send(const scxml_ctx* ctx, const scxml_elem_send* send) {
e->name = strdup(send->event);
}
- 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);
+ 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++;
}
- 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;
+ }
+ }
+ }
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+
if (send->content != NULL) {
- e->data = Data(send->content, Data::VERBATIM);
+ // 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);
+ }
}
const char* sendid = NULL;
if (send->id != NULL) {
sendid = send->id;
+ e->sendid = sendid;
} else {
sendid = strdup(UUID::getUUID().c_str());
- if (send->idlocation != NULL)
+ if (send->idlocation != NULL) {
USER_DATA(ctx)->datamodel->assign(send->idlocation, Data(sendid, Data::VERBATIM));
+ } else {
+ e->hideSendId = true;
+ }
}
+
size_t delayMs = 0;
std::string delay;
if (send->delayexpr != NULL) {
@@ -299,9 +434,15 @@ int exec_content_init(const scxml_ctx* ctx, const scxml_elem_data* data) {
}
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;
+ }
+
try {
Data d(expr, Data::INTERPRETED);
- USER_DATA(ctx)->datamodel->assign(location, d);
+ USER_DATA(ctx)->datamodel->assign(key, d);
} catch (Event e) {
exec_content_raise(ctx, e.name.c_str());
return SCXML_ERR_EXEC_CONTENT;
@@ -350,10 +491,25 @@ int exec_content_foreach_done(const scxml_ctx* ctx, const scxml_elem_foreach* fo
}
int exec_content_log(const scxml_ctx* ctx, const char* label, const char* expr) {
- if (label != 0) {
- printf("%s: %s\n", label, expr);
- } else {
- printf("%s\n", USER_DATA(ctx)->datamodel->evalAsString(expr).c_str());
+ try {
+ if (label != 0) {
+// printf("%s: %s\n", label, USER_DATA(ctx)->datamodel->evalAsString(expr).c_str());
+ } else {
+// printf("%s\n", USER_DATA(ctx)->datamodel->evalAsString(expr).c_str());
+ }
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+
+ 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;
}
@@ -366,7 +522,9 @@ void* dequeue_external(const scxml_ctx* ctx) {
Event* e = USER_DATA(ctx)->eq.front();
USER_DATA(ctx)->eq.pop_front();
USER_DATA(ctx)->datamodel->setEvent(*e);
+#ifdef SCXML_VERBOSE
printf("Popping External Event: %s\n", e->name.c_str());
+#endif
return e;
}
@@ -376,54 +534,125 @@ void* dequeue_internal(const scxml_ctx* ctx) {
Event* e = USER_DATA(ctx)->iq.front();
USER_DATA(ctx)->iq.pop_front();
USER_DATA(ctx)->datamodel->setEvent(*e);
+#ifdef SCXML_VERBOSE
printf("Popping Internal Event: %s\n", e->name.c_str());
+#endif
return e;
}
int main(int argc, char** argv) {
+
+#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);
+ }
+#endif
+
int err;
+ size_t benchmarkRuns = 1;
+ const char* envBenchmarkRuns = getenv("USCXML_BENCHMARK_ITERATIONS");
+ if (envBenchmarkRuns != NULL) {
+ benchmarkRuns = strTo<size_t>(envBenchmarkRuns);
+ }
+
+ size_t remainingRuns = benchmarkRuns;
+
// setup info object required for datamodel
GenCInterpreterInfo interpreterInfo;
- interpreterInfo.name = "adsf";
+ interpreterInfo.name = SCXML_MACHINE_NAME;
interpreterInfo.sessionId = "rfwef";
- interpreterInfo.datamodel = Factory::getInstance()->createDataModel("ecmascript", &interpreterInfo);
interpreterInfo.delayQueue.start();
scxml_ctx ctx;
- memset(&ctx, 0, sizeof(scxml_ctx));
interpreterInfo.ctx = &ctx;
-
- // 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;
-
- while((err = scxml_step(&ctx)) == SCXML_ERR_OK);
- assert(ctx.flags & SCXML_CTX_TOP_LEVEL_FINAL);
-
- 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) {
- passIdx = i;
- break;
+
+ double avg = 0;
+ size_t microSteps = 0;
+#ifdef BUILD_PROFILING
+ double avgDm = 0;
+#endif
+
+ 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) { microSteps++; }
+ assert(ctx.flags & SCXML_CTX_TOP_LEVEL_FINAL);
+
+ t.stop();
+ avg += t.elapsed;
+#ifdef BUILD_PROFILING
+ avgDm += interpreterInfo.datamodel.get()->timer.elapsed;
+ interpreterInfo.datamodel.get()->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) {
+ passIdx = i;
+ break;
+ }
}
+
+ assert(IS_SET(passIdx, ctx.config));
+ interpreterInfo.delayQueue.cancelAllEvents();
+ interpreterInfo.eq.clear();
+ interpreterInfo.iq.clear();
}
- assert(IS_SET(passIdx, ctx.config));
+
+ // 14.25311111 us per microstep
+ // 1.923466667 us
+ std::cout << (avg * 1000.0) / (double)benchmarkRuns << " ms on average" << std::endl;
+ std::cout << microSteps << " microsteps per iteration (" << (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;
+#endif
+ interpreterInfo.delayQueue.stop();
+ tthread::this_thread::sleep_for(tthread::chrono::milliseconds(100));
return EXIT_SUCCESS;
} \ No newline at end of file