Initial commit to the PapyrusRT repository.
This is the initial commit as submitted to CQ 9646.
The repository contains two top-level folders:
- codegen: the code-generator
- rts: the run-time system
Also-by: Andrew Eidsness, Zeligsoft contractor, andrewe@jfront.com,
Barry Maher, Zeligsoft contractor, bmaher@gpinc.ca, Ernesto Posse,
Zeligsoft, eposse@zeligsoft.com, Tim McGuire, Zeligsoft,
tmcguire@zeligsoft.com, Young-Soo Roh, Zeligsoft, ysroh@zeligsoft.com,
Toby McClean, Zeligsoft, toby@zeligsoft.com, Charles Rivet, Zeligsoft,
charles@zeligsoft.com, Andreas Henriksson, Ericsson,
andreas.henriksson@ericsson.com, Akos Horvath, IncQuery Labs,
akos.horvath@incquerylabs.com, Gabor Batori, Ericsson,
Gabor.Batori@ericsson.com, Abel Hegedus, IncQuery Labs,
abel.hegedus@incquerylabs.com, Denes Harmath, IncQuery Labs,
harmathdenes@gmail.com
Signed-off-by: Ernesto Posse <eposse@gmail.com>
diff --git a/rts/umlrt/umlrtapi.cc b/rts/umlrt/umlrtapi.cc
new file mode 100644
index 0000000..d20dc9e
--- /dev/null
+++ b/rts/umlrt/umlrtapi.cc
@@ -0,0 +1,148 @@
+// umlrtapi.cc - catch-all RTS API called from everywhere
+// Definitions here can be placed elsewhere at a later date...
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtapi.hh"
+#include "umlrtslot.hh"
+#include "umlrtcommsport.hh"
+#include "umlrtcommsportrole.hh"
+#include "umlrtcontroller.hh"
+#include "umlrtmessagepool.hh"
+#include "umlrtsignalelementpool.hh"
+#include "umlrttimerpool.hh"
+#include "basefatal.hh"
+#include "basedebug.hh"
+
+namespace umlrt
+{
+ // Allocate a signal from the system-pool.
+ UMLRTSignalElement * SignalElementGetFromPool()
+ {
+ UMLRTSignalElementPool * pool = UMLRTController::getSignalElementPool();
+
+ if (!pool)
+ {
+ FATAL("no signal pool defined (get)");
+ }
+ UMLRTSignalElement * element = (UMLRTSignalElement *)pool->get();
+ BDEBUG(BD_SIGNALALLOC, "get from pool signal-qid[%d] %s\n", (element == NULL) ? -1 : element->qid, (element == NULL) ? "IGNORE NULL" : "ok");
+
+ element->setAllocated(true);
+
+ return element;
+ }
+
+ // Deallocate a signal back to the system pool.
+ void SignalElementPutToPool( UMLRTSignalElement * element )
+ {
+ UMLRTSignalElementPool * pool = UMLRTController::getSignalElementPool();
+
+ if (!pool)
+ {
+ FATAL("no signal pool defined (put)");
+ }
+ BDEBUG(BD_SIGNALALLOC, "put to pool signal-qid[%d] %s\n", (element == NULL) ? -1 : element->qid, (element == NULL) ? "IGNORE NULL" : "ok");
+
+ element->setAllocated(false);
+
+ return pool->put(element);
+ }
+
+ // Allocate a message from the system-pool.
+ UMLRTMessage * MessageGetFromPool()
+ {
+ UMLRTMessagePool * pool = UMLRTController::getMessagePool();
+ UMLRTMessage * msg;
+
+ if (!pool)
+ {
+ FATAL("no message pool defined (get)");
+ }
+ if ((msg = (UMLRTMessage *)pool->get()) != NULL)
+ {
+ if (msg->allocated)
+ {
+ FATAL("Obtained an allocated message from the pool.");
+ }
+ msg->allocated = true;
+ }
+ return msg;
+ }
+
+ // Put a signal back to the system pool.
+ void MessagePutToPool( UMLRTMessage * message )
+ {
+ UMLRTMessagePool * pool = UMLRTController::getMessagePool();
+
+ UMLRTSignal invalid;
+
+ message->signal = invalid; // Causes application signal element to be 'dereferenced'.
+
+ if (!pool)
+ {
+ FATAL("no message pool defined (put)");
+ }
+ if (!message->allocated)
+ {
+ FATAL("Putting an unallocated message on the pool.");
+ }
+ message->allocated = false;
+
+ pool->put(message);
+ }
+
+ // Allocate a timer from the system-pool.
+ UMLRTTimer * TimerGetFromPool()
+ {
+ UMLRTTimerPool * pool = UMLRTController::getTimerPool();
+ UMLRTTimer * timer;
+
+ if (!pool)
+ {
+ FATAL("no timer pool defined (get)");
+ }
+ if ((timer = (UMLRTTimer *)pool->get()) != NULL)
+ {
+ if (timer->allocated)
+ {
+ FATAL("timer obtained from pool was already allocated.");
+ }
+ timer->allocated = true;
+ }
+ BDEBUG(BD_TIMERALLOC, "get from pool timer-qid[%d] %s\n", (timer == NULL) ? -1 : timer->qid, (timer == NULL) ? "IGNORE NULL" : "ok");
+
+ return timer;
+ }
+
+ // Put a timer back to the system pool.
+ void TimerPutToPool( UMLRTTimer * timer )
+ {
+ UMLRTTimerPool * pool = UMLRTController::getTimerPool();
+
+ if (!pool)
+ {
+ FATAL("no timer pool defined (put)");
+ }
+ if (!timer->allocated)
+ {
+ FATAL("attempting to return an unallocated timer to the pool.");
+ }
+ timer->allocated = false;
+
+ BDEBUG(BD_TIMERALLOC, "put to pool timer-qid[%d] signal-qid[%d] %s\n",
+ timer->qid, timer->signal.getQid());
+
+ UMLRTOutSignal invalid;
+
+ timer->signal = invalid; // Causes application signal element to be 'dereferenced'.
+
+ pool->put(timer);
+ }
+}
diff --git a/rts/umlrt/umlrtbasicthread.cc b/rts/umlrt/umlrtbasicthread.cc
new file mode 100644
index 0000000..99a8d07
--- /dev/null
+++ b/rts/umlrt/umlrtbasicthread.cc
@@ -0,0 +1,93 @@
+// umlrtbasicthread.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <string.h>
+#include "basefatal.hh"
+#include "umlrtbasicthread.hh"
+
+// See umlrtbasicthread.hh for documentation.
+
+// Bug 31 raised to move this to +/os/linux/osbasicthread.cc.
+
+/*static*/ void * UMLRTBasicThread::static_entrypoint( void * arg )
+{
+ threadargs_t *threadargs = (threadargs_t*)arg;
+
+ return threadargs->inst->run( threadargs->args );
+}
+
+
+UMLRTBasicThread::UMLRTBasicThread( const char * name_ ) : tid(0)
+{
+ memset(name, 0, sizeof(name));
+ strncpy(name, name_, sizeof(name) - 1);
+}
+
+// Start the thread, passing in a single argument.
+
+void UMLRTBasicThread::start( void * args )
+{
+ pthread_attr_t attr;
+
+ memset(&attr, 0, sizeof(attr));
+
+ if (pthread_attr_init( &attr) < 0)
+ {
+ FATAL_ERRNO("pthread_attr_init");
+ }
+ threadargs.inst = this;
+ threadargs.args = args;
+
+ if (pthread_create(&tid, &attr, static_entrypoint, &threadargs) < 0)
+ {
+ FATAL_ERRNO("pthread_create");
+ }
+#ifdef __USE_GNU
+ // May not be universally available.
+ pthread_setname_np(tid, name); // Attempt this, but ignore errors.
+#endif
+}
+
+// Wait for the thread to complete and get returned value.
+
+void * UMLRTBasicThread::join()
+{
+ void *ret;
+ if (pthread_join(tid, &ret) < 0)
+ {
+ FATAL_ERRNO("pthread_join");
+ }
+ return ret;
+}
+
+// Returns true if this thread is currently running thread.
+
+bool UMLRTBasicThread::isMyThread()
+{
+ return pthread_self() == tid;
+}
+
+// Return my name
+
+const char * UMLRTBasicThread::getName() const
+{
+ return name;
+}
+
+// Return running thread id.
+/*static*/ UMLRTBasicThread::threadid_t UMLRTBasicThread::selfId()
+{
+ return (threadid_t)pthread_self();
+}
+
diff --git a/rts/umlrt/umlrtcapsule.cc b/rts/umlrt/umlrtcapsule.cc
new file mode 100644
index 0000000..f508e6f
--- /dev/null
+++ b/rts/umlrt/umlrtcapsule.cc
@@ -0,0 +1,35 @@
+// umlrtcapsule.cc
+
+/*******************************************************************************
+* Copyright (c) 2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtcapsuleclass.hh"
+#include "umlrtcapsule.hh"
+#include "umlrtframeservice.hh"
+#include "basedebug.hh"
+#include "basedebugtype.hh"
+
+UMLRTCapsule::~UMLRTCapsule ( )
+{
+ BDEBUG(BD_INSTANTIATE, "%s destructor\n", slot->name);
+ UMLRTCapsuleToControllerMap::removeCapsule(slot->name, this);
+}
+
+UMLRTCapsule::UMLRTCapsule ( const UMLRTRtsInterface * rtsif_, const UMLRTCapsuleClass * capsuleClass_, UMLRTSlot * slot, const UMLRTCommsPort * * borderPorts_, const UMLRTCommsPort * internalPorts_, bool isStatic_ ) : rtsif(rtsif_), capsuleClass(capsuleClass_), slot(slot), borderPorts(borderPorts_), internalPorts(internalPorts_), isStatic(isStatic_)
+{
+ BDEBUG(BD_INSTANTIATE, "slot %s constructor\n", slot->name);
+ UMLRTCapsuleToControllerMap::addCapsule(slot->name, this);
+}
+
+void UMLRTCapsule::unbindPort ( bool isBorder, int portIndex, int farEndIndex )
+{
+}
+
+void UMLRTCapsule::bindPort ( bool isBorder, int portIndex, int farEndIndex )
+{
+}
diff --git a/rts/umlrt/umlrtcapsuleid.cc b/rts/umlrt/umlrtcapsuleid.cc
new file mode 100644
index 0000000..1ec3189
--- /dev/null
+++ b/rts/umlrt/umlrtcapsuleid.cc
@@ -0,0 +1,24 @@
+// umlrtcapsuleid.c
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtcapsule.hh"
+#include "umlrtcapsuleid.hh"
+#include "basefatal.hh"
+#include <stdlib.h>
+
+// Must have verified #isValid before calling this.
+UMLRTCapsule * UMLRTCapsuleId::getCapsule() const
+{
+ if (!capsule)
+ {
+ FATAL("no capsule defined");
+ }
+ return capsule;
+}
diff --git a/rts/umlrt/umlrtcapsuletocontrollermap.cc b/rts/umlrt/umlrtcapsuletocontrollermap.cc
new file mode 100644
index 0000000..a746025
--- /dev/null
+++ b/rts/umlrt/umlrtcapsuletocontrollermap.cc
@@ -0,0 +1,285 @@
+// umlrtcapsuletocontrollermap.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtcapsule.hh"
+#include "umlrtcapsuletocontrollermap.hh"
+#include "umlrtcontroller.hh"
+#include "umlrthashmap.hh"
+#include "basefatal.hh"
+#include "basedebug.hh"
+#include "basedebugtype.hh"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+UMLRTHashMap * UMLRTCapsuleToControllerMap::capsuleToControllerListMap = NULL;
+UMLRTHashMap * UMLRTCapsuleToControllerMap::controllerNameMap = NULL;
+UMLRTHashMap * UMLRTCapsuleToControllerMap::capsuleNameMap = NULL;
+
+/*static*/ UMLRTHashMap * UMLRTCapsuleToControllerMap::getControllerNameMap()
+{
+ if (controllerNameMap == NULL)
+ {
+ controllerNameMap = new UMLRTHashMap("controllerNameMap", UMLRTHashMap::compareString);
+ }
+ return controllerNameMap;
+}
+
+/*static*/ UMLRTHashMap * UMLRTCapsuleToControllerMap::getCapsuleToControllerListMap()
+{
+ if (capsuleToControllerListMap == NULL)
+ {
+ capsuleToControllerListMap = new UMLRTHashMap("capsuleToControllerMap", UMLRTHashMap::compareString);
+ }
+ return capsuleToControllerListMap;
+}
+
+/*static*/ UMLRTHashMap * UMLRTCapsuleToControllerMap::getCapsuleNameMap()
+{
+ if (capsuleNameMap == NULL)
+ {
+ capsuleNameMap = new UMLRTHashMap("capsuleNameMap", UMLRTHashMap::compareString);
+ }
+ return capsuleNameMap;
+}
+
+// Add a controller by name.
+/*static*/ bool UMLRTCapsuleToControllerMap::addController( const char * controllername, UMLRTController * controller )
+{
+ if (getControllerFromName(controllername) != NULL)
+ {
+ FATAL("ERROR: Controller %s has already registered.", controllername);
+ }
+ BDEBUG(BD_CONTROLLER, "add controller(%s) p(%p)\n", controllername, controller );
+
+ getControllerNameMap()->insert( controllername, (void *)controller);
+
+ return true;
+}
+
+// Add a capsule by name.
+/*static*/ void UMLRTCapsuleToControllerMap::addCapsule( const char * capsulename, UMLRTCapsule * capsule )
+{
+ getCapsuleNameMap()->insert( capsulename, (void *)capsule);
+}
+
+/*static*/ const UMLRTCapsule * UMLRTCapsuleToControllerMap::getCapsuleFromName ( const char * capsulename )
+{
+ return (const UMLRTCapsule *)getCapsuleNameMap()->getObject(capsulename);
+}
+
+// Remove a capsule instance from the list.
+/*static*/ void UMLRTCapsuleToControllerMap::removeCapsule ( const char * capsuleName, UMLRTCapsule * capsule )
+{
+ getCapsuleNameMap()->remove(capsuleName);
+}
+
+/*static*/ UMLRTController * UMLRTCapsuleToControllerMap::getControllerFromName( const char * controllerName )
+{
+ return (UMLRTController *)getControllerNameMap()->getObject(controllerName);
+}
+
+/*static*/ UMLRTController * UMLRTCapsuleToControllerMap::getFirstController()
+{
+ return (UMLRTController *)getControllerNameMap()->getFirstObject();
+}
+
+/*static*/ const char * UMLRTCapsuleToControllerMap::getControllerNameForCapsule ( const char * capsuleName, const UMLRTCapsuleClass * capsuleClass )
+{
+ const char * controllerName = NULL;
+ UMLRTHashMap * controllerList = (UMLRTHashMap *)getCapsuleToControllerListMap()->getObject(capsuleName);
+ if (controllerList)
+ {
+ // Attempt to find the class-specific controller.
+ controllerName = (const char *) controllerList->getObject(capsuleClass->name);
+ if (controllerName == NULL)
+ {
+ // Class-specific controller not assigned - look for the default.
+ controllerName = (const char *)controllerList->getObject((void*)NULL);
+ }
+ }
+ return controllerName;
+}
+
+// Return the controller assigned to this capsule. Returns NULL if no controller is assigned or doesn't exist.
+/*static*/ UMLRTController * UMLRTCapsuleToControllerMap::getControllerForCapsule ( const char * capsuleName, const UMLRTCapsuleClass * capsuleClass )
+{
+ const char * controllerName = getControllerNameForCapsule( capsuleName, capsuleClass );
+
+ return (UMLRTController *)getControllerFromName(controllerName);
+}
+
+/*static*/ void UMLRTCapsuleToControllerMap::addCapsuleToControllerSpec ( char * capsuleName, char * controllerName, char * className )
+{
+ // className can be NULL, indicating the default controller for all capsule classes incarnated into the slot.
+
+ UMLRTHashMap * controllerList = (UMLRTHashMap *)getCapsuleToControllerListMap()->getObject(capsuleName);
+
+ if (controllerList == NULL)
+ {
+ // This capsule has no previous controller assignments - no controller list - add one.
+ const char * capsuleKey = strdup(capsuleName);
+ controllerList = new UMLRTHashMap(capsuleKey, UMLRTHashMap::compareString);
+ getCapsuleToControllerListMap()->insert(capsuleKey, (void *)controllerList);
+ }
+ if ((controllerList->getObject(className)) != NULL)
+ {
+ FATAL("-C option: capsule-to-controller-map already had an entry for capsule '%s' class '%s'",
+ capsuleName, (className == NULL) ? "(default)" : className);
+ }
+ // Assign this controller to the capsule/class combination.
+ const char *classKey = className;
+ if (classKey != NULL)
+ {
+ classKey = strdup(className);
+ }
+ controllerList->insert(classKey, (void *)strdup(controllerName));
+}
+
+// Create a map of capsule to controller
+/*static*/ bool UMLRTCapsuleToControllerMap::parseCapsuleControllerLine ( char * line )
+{
+ bool ok = false;
+ char * capsuleName;
+ char * saveptr;
+ capsuleName = strtok_r( line, " =\n\r\t", &saveptr);
+ if (capsuleName != NULL)
+ {
+ char * controllerName = strtok_r(NULL, " =\n\r\t", &saveptr);
+ if (controllerName != NULL)
+ {
+ char * className = strtok_r(NULL, " = \n\r\t", &saveptr);
+ // If className is NULL, then this is the default controller assignment for this capsule - no capsule class specified.
+ addCapsuleToControllerSpec( capsuleName, controllerName, className );
+ ok = true;
+ }
+ }
+ return ok;
+}
+
+// Create a map of capsule to controller
+/*static*/ bool UMLRTCapsuleToControllerMap::readCapsuleControllerMap( const char * controllerfile )
+{
+ bool ok = true;
+ FILE *fd;
+
+ if ((fd = fopen(controllerfile, "r")) == NULL)
+ {
+ printf("ERROR: failed to open controllers file %s\n", controllerfile);
+ ok = false;
+ }
+ else
+ {
+ char * line;
+ char buf[1024];
+
+ while (((line = fgets(buf, sizeof(buf), fd)) != NULL) && ok)
+ {
+ ok = UMLRTCapsuleToControllerMap::parseCapsuleControllerLine(buf);
+ }
+ }
+ return ok;
+}
+
+// Assign static capsule controllers based on the run-time map.
+/*static*/ void UMLRTCapsuleToControllerMap::setStaticCapsuleControllers( UMLRTSlot * slots, size_t size )
+{
+ for (size_t i = 0; i < size; ++i)
+ {
+ UMLRTController * controller = getControllerForCapsule(slots[i].name, slots[i].capsuleClass);
+
+ // Only reassign static capsules (i.e. controller already assigned) whose controllers were specified in the run-time map.
+ if ((slots[i].controller != NULL) && (controller != NULL))
+ {
+ // Reassign the capsule according to the run-time map collected.
+ slots[i].controller = controller;
+ }
+ }
+}
+
+
+// Debug output the the capsule, controller and capsule-to-controller maps.
+/*static*/ void UMLRTCapsuleToControllerMap::debugOutputControllerList()
+{
+ BDEBUG(BD_MODEL, "Controller list: { <controller name>, <instance address> }\n");
+
+ UMLRTHashMap::Iterator iter = getControllerNameMap()->getIterator();
+
+ if (iter == iter.end())
+ {
+ BDEBUG(BD_MODEL, " No controllers.\n");
+ }
+ else
+ {
+ while (iter != iter.end())
+ {
+ BDEBUG(BD_MODEL, " { %s, %p }\n", ((UMLRTController *)((char *)iter.getObject()))->getName(), (UMLRTController *)((char *)iter.getObject()));
+ iter = iter.next();
+ }
+ }
+}
+
+/*static*/ void UMLRTCapsuleToControllerMap::debugOutputCapsuleList()
+{
+ BDEBUG(BD_MODEL, "Capsule list: { <capsule name>, <instance address>, <capsule class>, <assigned controller> }\n");
+
+ UMLRTHashMap::Iterator iter = getCapsuleNameMap()->getIterator();
+
+ if (iter == iter.end())
+ {
+ BDEBUG(BD_MODEL, " No capsules.\n");
+ }
+ else
+ {
+ while (iter != iter.end())
+ {
+ UMLRTCapsule * capsule = (UMLRTCapsule *)iter.getObject();
+ BDEBUG(BD_MODEL, " { %s, %p, %s, %s }\n",
+ capsule->getName(), capsule, capsule->getClass()->name, capsule->getSlot()->controller->getName());
+ iter = iter.next();
+ }
+ }
+}
+
+// Debug output of capsule-to-controller map.
+/*static*/ void UMLRTCapsuleToControllerMap::debugOutputCaspuleToControllerMap()
+{
+ BDEBUG(BD_MODEL, "Capsule to controller map: { <slot>, <controller>, <capsule class> }\n");
+
+ UMLRTHashMap::Iterator ctclIter = getCapsuleToControllerListMap()->getIterator();
+
+ if (ctclIter == ctclIter.end())
+ {
+ BDEBUG(BD_MODEL, " No capsule to controller assignments.\n");
+ }
+ else
+ {
+ while (ctclIter != ctclIter.end())
+ {
+ const char * capsuleName = (const char *)ctclIter.getKey();
+ UMLRTHashMap * controllerList = (UMLRTHashMap *)ctclIter.getObject();
+ if (controllerList)
+ {
+ UMLRTHashMap::Iterator clIter = controllerList->getIterator();
+ while (clIter != clIter.end())
+ {
+ BDEBUG(BD_MODEL, " { %s, %s, %s }\n",
+ capsuleName,
+ (clIter.getObject() == NULL) ? "?no controller?" : clIter.getObject(),
+ (clIter.getKey() == NULL) ? "(default)" : clIter.getKey());
+ clIter = clIter.next();
+ }
+ }
+ ctclIter = ctclIter.next();
+ }
+ }
+}
+
+
diff --git a/rts/umlrt/umlrtcommsport.cc b/rts/umlrt/umlrtcommsport.cc
new file mode 100644
index 0000000..f7c9eaf
--- /dev/null
+++ b/rts/umlrt/umlrtcommsport.cc
@@ -0,0 +1,78 @@
+// umlrtcommsport.c
+
+/*******************************************************************************
+* Copyright (c) 2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtcommsport.hh"
+#include "umlrtcontroller.hh"
+#include "umlrtmessage.hh"
+#include "umlrtqueue.hh"
+
+/*static*/ bool UMLRTCommsPort::purgeMatchCompare( UMLRTMessage * msg, const PurgeRecall * purge )
+{
+ bool match = true;
+ if (purge->index != -1)
+ {
+ match = (purge->index == (int)msg->sapIndex0);
+ if (match && (purge->id != -1))
+ {
+ match = (purge->id == (int)msg->signal.getId());
+ }
+ }
+ return match;
+}
+
+/*static*/ void UMLRTCommsPort::purgeMatchNotify( UMLRTMessage * msg, const PurgeRecall * purge )
+{
+ umlrt::MessagePutToPool(msg);
+}
+
+int UMLRTCommsPort::purge( int index, int id ) const
+{
+ int count = 0;
+ const PurgeRecall purge = { index, false/*front*/, id };
+ if (deferQueue != NULL)
+ {
+ count = deferQueue->remove( (UMLRTQueue::match_compare_t)purgeMatchCompare, (UMLRTQueue::match_notify_t)purgeMatchNotify, (void *)&purge );
+ }
+ return count;
+}
+
+
+/*static*/ bool UMLRTCommsPort::recallMatchCompare( UMLRTMessage * msg, const PurgeRecall * recall )
+{
+ bool match = true;
+ if (recall->index != -1)
+ {
+ match = (recall->index == (int)msg->sapIndex0);
+ }
+ return match;
+}
+
+/*static*/ void UMLRTCommsPort::recallMatchNotify( UMLRTMessage * msg, const PurgeRecall * recall )
+{
+ msg->destPort->slot->controller->recall( msg, recall->front );
+}
+
+int UMLRTCommsPort::recall( int index, bool front, bool one, int id ) const
+{
+ int count = 0;
+ const PurgeRecall recall = { index, front, id };
+
+ if (deferQueue != NULL)
+ {
+ count = deferQueue->remove( (UMLRTQueue::match_compare_t)recallMatchCompare, (UMLRTQueue::match_notify_t)recallMatchNotify, (void *)&recall, one );
+ }
+ return count;
+}
+
+const UMLRTCommsPortRole * UMLRTCommsPort::role() const
+{
+ return border ? ((containerClass->portRolesBorder == NULL) ? NULL : &containerClass->portRolesBorder[roleIndex]) : ((containerClass->portRolesInternal == NULL) ? NULL : &containerClass->portRolesInternal[roleIndex]);
+}
+
diff --git a/rts/umlrt/umlrtcontroller.cc b/rts/umlrt/umlrtcontroller.cc
new file mode 100644
index 0000000..55716d8
--- /dev/null
+++ b/rts/umlrt/umlrtcontroller.cc
@@ -0,0 +1,958 @@
+// umlrtcontroller.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+// UMLRTController is the main controller-class.
+
+#include "basedebug.hh"
+#include "basefatal.hh"
+#include "umlrtapi.hh"
+#include "umlrtcontroller.hh"
+#include "umlrtcapsule.hh"
+#include "umlrtslot.hh"
+#include "umlrtcapsuletocontrollermap.hh"
+#include "umlrtcapsulerole.hh"
+#include "umlrtcapsulepart.hh"
+#include "umlrtcommsport.hh"
+#include "umlrtcommsportfarend.hh"
+#include "umlrtcommsportrole.hh"
+#include "umlrtcontrollercommand.hh"
+#include "umlrtframeservice.hh"
+#include "umlrtmessage.hh"
+#include "umlrtobjectclass.hh"
+#include "umlrtprotocol.hh"
+#include "umlrttimer.hh"
+#include "umlrttimespec.hh"
+#include "umlrtqueue.hh"
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/select.h>
+#include <string.h>
+#include <stdarg.h>
+
+// The application-wide free message pool.
+/*static*/ UMLRTMessagePool * UMLRTController::messagePool = NULL;
+
+// The application-wide free signal pool.
+/*static*/ UMLRTSignalElementPool * UMLRTController::signalElementPool = NULL;
+
+// The application-wide free timer pool.
+/*static*/ UMLRTTimerPool * UMLRTController::timerPool = NULL;
+
+// Error codes to string
+static const char * errorToString[] = UMLRTCONTROLLER_ERROR_CODE_TO_STRING;
+
+
+UMLRTController::UMLRTController(const char * name_, size_t numSlots_, const UMLRTSlot slots_[])
+ : UMLRTBasicThread(name_), name(name_), incomingQueue(name), capsuleQueue(name), numSlots(numSlots_), slots(slots_), lastError(E_OK)
+{
+ // Register the controller with the capsule-to-controller map.
+ UMLRTCapsuleToControllerMap::addController(name, this);
+}
+
+bool UMLRTController::cancelTimer( const UMLRTTimerId id )
+{
+ if (id.isValid())
+ {
+ const UMLRTTimer * timer = id.getTimer();
+
+ char buf[UMLRTTimespec::TIMESPEC_TOSTRING_SZ];
+ BDEBUG(BD_TIMER, "cancel timer(%p) destPort(%s) isInterval(%d) priority(%d) payloadSize(%d) due(%s)\n",
+ timer, timer->destPort->role()->name, timer->isInterval, timer->signal.getPriority(), timer->signal.getPayloadSize(),
+ timer->isInterval ? timer->due.toStringRelative(buf, sizeof(buf)) : timer->due.toString(buf, sizeof(buf)));
+ }
+ return timerQueue.cancel(id);
+}
+
+/*static*/ bool UMLRTController::deallocateMsgMatchCompare( UMLRTMessage * msg, UMLRTSlot * slot )
+{
+ return !msg->isCommand && msg->destSlot == slot;
+}
+
+/*static*/ void UMLRTController::deallocateMsgMatchNotify( UMLRTMessage * msg, UMLRTSlot * slot )
+{
+ BDEBUG(BD_DESTROY, "Purging message destined for slot %s\n", slot->name);
+
+ umlrt::MessagePutToPool(msg);
+}
+
+// Callback to purge timers for a condemned slot.
+/*static*/ bool UMLRTController::deallocateTimerMatchCompare( UMLRTTimer * timer, UMLRTSlot * slot )
+{
+ return timer->destSlot == slot;
+}
+
+// Callback when a timer is being deleted.
+/*static*/ void UMLRTController::deallocateTimerMatchNotify( UMLRTTimer * timer, UMLRTSlot * slot )
+{
+ BDEBUG(BD_DESTROY, "Purging timer destined for slot %s\n", slot->name);
+
+ umlrt::TimerPutToPool(timer);
+}
+
+void UMLRTController::deallocateSlotResources( UMLRTSlot * slot )
+{
+ if (slot == NULL)
+ {
+ FATAL("attempting to deallocate the NULL slot.");
+ }
+ BDEBUG(BD_DESTROY, "Remove messages and timers for slot %s\n", slot->name);
+
+ incomingQueue.remove( (UMLRTQueue::match_compare_t)deallocateMsgMatchCompare, (UMLRTQueue::match_notify_t)deallocateMsgMatchNotify, slot );
+ capsuleQueue.remove( (UMLRTQueue::match_compare_t)deallocateMsgMatchCompare, (UMLRTQueue::match_notify_t)deallocateMsgMatchNotify, slot );
+ timerQueue.remove( (UMLRTQueue::match_compare_t)deallocateTimerMatchCompare, (UMLRTQueue::match_notify_t)deallocateTimerMatchNotify, slot );
+}
+
+// Deliver a signal to the destination port.
+bool UMLRTController::deliver ( const UMLRTCommsPort * destPort, const UMLRTSignal &signal, size_t srcPortIndex )
+{
+ // Assumes global RTS lock acquired.
+
+ UMLRTMessage * msg = umlrt::MessageGetFromPool();
+ bool ok = false;
+
+ if (!msg)
+ {
+ signal.getSrcPort()->slot->controller->setError(E_SEND_NO_MSG_AVL);
+ }
+ else if (destPort == NULL)
+ {
+ FATAL("Message from slot %s (port %s) has destination port NULL.", signal.getSrcPort()->slot->name, signal.getSrcPort()->role()->name);
+ }
+ else
+ {
+ // Look up sapIndex0 so receiver knows which index in their replicated port the message was received on.
+ msg->sapIndex0 = signal.getSrcPort()->farEnds[srcPortIndex].farEndIndex;
+ msg->signal = signal;
+ msg->destPort = destPort;
+ msg->destSlot = destPort->slot;
+ msg->srcPortIndex = srcPortIndex;
+ msg->isCommand = false;
+
+ // Source port may not exist.
+ BDEBUG(BD_SIGNALALLOC, "%s: deliver signal-qid[%d] id(%d) -> %s(%s[%d]) payloadSize(%d)\n",
+ name,
+ msg->signal.getQid(),
+ msg->signal.getId(),
+ msg->destPort->slot->name, msg->destPort->role()->name, msg->sapIndex0,
+ msg->signal.getPayloadSize());
+
+ if (isMyThread())
+ {
+ // If this is me delivering the message, I can deliver directly to my capsule queues.
+ capsuleQueue.enqueue(msg);
+ }
+ else
+ {
+ // Otherwise, I deliver to the remote capsule's incoming queue.
+ incomingQueue.enqueue(msg);
+ }
+ ok = true;
+ }
+ return ok;
+}
+
+void UMLRTController::enqueueAbort()
+{
+ UMLRTControllerCommand command;
+
+ // Explicitly reset unused command contents.
+ command.capsule = NULL;
+ command.isTopSlot = false;
+ command.serializedData = NULL;
+ command.sizeSerializedData = 0;
+ command.slot = NULL;
+
+ // Format command and enqueue it.
+ command.command = UMLRTControllerCommand::ABORT;
+ enqueueCommand(command);
+}
+
+void UMLRTController::enqueueCommand( const UMLRTControllerCommand & command )
+{
+ UMLRTMessage *msg = umlrt::MessageGetFromPool();
+
+ msg->signal.initialize(sizeof(UMLRTControllerCommand), UMLRTSignalElement::PRIORITY_SYSTEM);
+ uint8_t * payload;
+
+ BDEBUG(BD_SIGNALALLOC, "%s: deliver signal-qid[%d] as command %d\n",
+ name,
+ msg->signal.getQid(),
+ command.command);
+
+ if ((payload = msg->signal.getPayload()) == NULL)
+ {
+ FATAL("initialized signal had no payload.");
+ }
+ memcpy( payload, &command, sizeof(command));
+ msg->isCommand = true;
+
+ if (isMyThread())
+ {
+ // If this is me delivering the message, I can deliver directly to my capsule queues.
+ capsuleQueue.enqueue(msg);
+ }
+ else
+ {
+ // Otherwise, I deliver to the remote capsule's incoming queue.
+ incomingQueue.enqueue(msg);
+ }
+}
+
+void UMLRTController::enqueueDebugOutputModel()
+{
+ UMLRTControllerCommand command;
+
+ // Explicitly reset unused command contents.
+ command.capsule = NULL;
+ command.isTopSlot = false;
+ command.serializedData = NULL;
+ command.sizeSerializedData = 0;
+ command.slot = NULL;
+
+ // Format command and enqueue it.
+ command.command = UMLRTControllerCommand::DEBUG_OUTPUT_MODEL;
+
+ enqueueCommand(command);
+}
+
+void UMLRTController::enqueueDeport( UMLRTSlot * slot )
+{
+ UMLRTControllerCommand command;
+
+ // Explicitly reset unused command contents.
+ command.capsule = NULL;
+ command.isTopSlot = false;
+ command.serializedData = NULL;
+ command.sizeSerializedData = 0;
+
+ // Format command and enqueue it.
+ command.command = UMLRTControllerCommand::DEPORT;
+ command.slot = slot;
+
+ enqueueCommand(command);
+}
+
+void UMLRTController::enqueueDestroy( UMLRTSlot * slot, bool isTopSlot )
+{
+ UMLRTControllerCommand command;
+
+ // Explicitly reset unused command contents.
+ command.capsule = NULL;
+ command.serializedData = NULL;
+ command.sizeSerializedData = 0;
+
+ // Format command and enqueue it.
+ command.command = UMLRTControllerCommand::DESTROY;
+ command.slot = slot;
+ command.isTopSlot = isTopSlot;
+
+ enqueueCommand(command);
+}
+
+void UMLRTController::enqueueImport( UMLRTSlot * slot, UMLRTCapsule * capsule )
+{
+ UMLRTControllerCommand command;
+
+ // Explicitly reset unused command contents.
+ command.isTopSlot = false;
+ command.serializedData = NULL;
+ command.sizeSerializedData = 0;
+
+ // Format command and enqueue it.
+ command.command = UMLRTControllerCommand::IMPORT;
+ command.slot = slot;
+ command.capsule = capsule;
+
+ enqueueCommand(command);
+}
+
+void UMLRTController::enqueueIncarnate( UMLRTCapsule * capsule, const void * userData, const UMLRTObject_class * type )
+{
+ UMLRTControllerCommand command;
+
+ // May be called from within the context of another controller thread.
+
+ // Explicitly reset unused command contents.
+ command.isTopSlot = false;
+ command.slot = NULL;
+
+ // Format command and enqueue it.
+ command.command = UMLRTControllerCommand::INCARNATE;
+ command.capsule = capsule;
+
+ serializeIncarnateData( userData, type, &command.sizeSerializedData, &command.serializedData);
+ // Controller frees serialized data buffer back to the heap.
+
+ command.sizeSerializedData = 0;
+ command.serializedData = NULL;
+
+ // Notification to destination controller occurs as a result of this enqueue.
+ enqueueCommand(command);
+}
+
+// Return true if abort was received.
+bool UMLRTController::executeCommand( UMLRTMessage * msg )
+{
+ bool abort = false;
+ UMLRTControllerCommand * command = (UMLRTControllerCommand *)msg->signal.getPayload();
+
+ if (command != NULL)
+ {
+ switch (command->command)
+ {
+ case UMLRTControllerCommand::ABORT:
+ BDEBUG(BD_COMMAND, "%s: ABORT command received\n", getName());
+ abort = true;
+ break;
+
+ case UMLRTControllerCommand::DEBUG_OUTPUT_MODEL:
+ BDEBUG(BD_COMMAND, "%s: DEBUG_OUTPUT_MODEL command received\n", getName());
+ debugOutputModel();
+ break;
+
+ case UMLRTControllerCommand::DEPORT:
+ BDEBUG(BD_COMMAND, "%s: DEPORT from slot %s command received\n", getName(), command->slot->name);
+ UMLRTFrameService::controllerDeport(command->slot, false/*synchronous*/, false/*lockAcquired*/);
+ break;
+
+ case UMLRTControllerCommand::DESTROY:
+ BDEBUG(BD_COMMAND, "%s: DESTROY from slot %s (is %stop slot) command received\n", getName(), command->slot->name, command->isTopSlot ? "" : "NOT ");
+ UMLRTFrameService::controllerDestroy(command->slot, command->isTopSlot, false/*synchronous*/, false/*lockAcquired*/);
+ break;
+
+ case UMLRTControllerCommand::IMPORT:
+ BDEBUG(BD_COMMAND, "%s: IMPORT capsule %s to slot %s command received\n", getName(), command->capsule->getName(), command->slot->name);
+ UMLRTFrameService::controllerImport(command->slot, command->capsule, false/*synchronous*/, false/*lockAcquired*/);
+ break;
+
+ case UMLRTControllerCommand::INCARNATE:
+ BDEBUG(BD_COMMAND, "%s: INCARNATE capsule %s (size data %d) command received\n", getName(), command->capsule->getName(), command->sizeSerializedData);
+ UMLRTFrameService::controllerIncarnate(command->capsule, command->sizeSerializedData, command->serializedData);
+ break;
+
+ case UMLRTControllerCommand::UNDEFINED:
+ default:
+ FATAL("%s:unknown controller (%d) command received", getName(), command->command);
+ }
+ }
+ return abort;
+}
+
+// Initialize the free pools.
+/*static*/ void UMLRTController::initializePools( UMLRTSignalElementPool * signalElementPool_, UMLRTMessagePool * messagePool_,
+ UMLRTTimerPool * timerPool_ )
+{
+ signalElementPool = signalElementPool_;
+ messagePool = messagePool_;
+ timerPool = timerPool_;
+}
+
+// Wait for the controller thread to die.
+void UMLRTController::join()
+{
+ UMLRTBasicThread::join();
+}
+
+// Get the system-wide signal pool.
+/*static*/ UMLRTSignalElementPool * UMLRTController::getSignalElementPool()
+{
+ return signalElementPool;
+}
+
+// Get the system-wide message pool.
+/*static*/ UMLRTMessagePool * UMLRTController::getMessagePool()
+{
+ return messagePool;
+}
+
+// Get the system-wide timer pool.
+/*static*/ UMLRTTimerPool * UMLRTController::getTimerPool()
+{
+ return timerPool;
+}
+
+bool UMLRTController::isMyThread()
+{
+ return UMLRTBasicThread::isMyThread();
+}
+
+// Output an error containing a user-defined message and the 'strerror()' string.
+void UMLRTController::perror( const char * fmt, ...) const
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ printf(": %s\n", strerror());
+ va_end(ap);
+}
+
+// Deliver a signal to the destination port.
+void UMLRTController::recall( UMLRTMessage * msg, bool front )
+{
+ capsuleQueue.enqueue(msg, front);
+}
+
+// Main loop
+void * UMLRTController::run( void * args )
+{
+ printf("Controller \"%s\" running.\n", getName());
+
+ for (size_t i = 0; i < numSlots; ++i)
+ {
+ if (slots[i].controller == this)
+ {
+ if ((slots[i].capsule != NULL) && (!slots[i].condemned))
+ {
+ if ( (slots[i].role() == NULL)
+ || ((slots[i].role()->optional == 0) && (slots[i].role()->plugin == 0)))
+ {
+ BDEBUG(BD_INSTANTIATE, "%s: initialize capsule %s (class %s).\n", getName(), slots[i].name, slots[i].capsuleClass->name );
+
+ UMLRTFrameService::initializeCapsule(slots[i].capsule, 0, NULL);
+ }
+ }
+ }
+ }
+
+ bool abort = false;
+ while (!abort)
+ {
+ // Queue messages associated with all timed-out timers.
+ capsuleQueue.queueTimerMessages(&timerQueue);
+
+ // Transfer all incoming messages to capsule queues.
+ capsuleQueue.moveAll(incomingQueue);
+
+ // Inject all available messages, highest priority msgs first.
+ UMLRTMessage * msg;
+ while (!abort && (msg = capsuleQueue.dequeueHighestPriority()) != NULL)
+ {
+ if (msg->isCommand)
+ {
+ abort = executeCommand(msg);
+ }
+ else if (msg->destSlot->capsule == NULL)
+ {
+ FATAL("%s: signal id(%d) to slot %s (no capsule instance) should not occur\n",
+ getName(), msg->signal.getId(), msg->destSlot->name);
+ }
+ else
+ {
+ if (msg->destSlot->condemned)
+ {
+ // Drop messages to a condemned slot.
+ BDEBUG(BD_INJECT, "%s: dropping signal-qid[%d] id(%d) to slot %s (slot condemned)\n",
+ getName(), msg->signal.getQid(), msg->signal.getId(), msg->destSlot->name);
+ }
+ else
+ {
+ // Source port may no longer exist.
+ BDEBUG(BD_INJECT, "%s: inject signal-qid[%d] into %s {%s[%d]} id %d\n",
+ getName(), msg->signal.getQid(), msg->destSlot->capsule->getName(), msg->destPort->role()->name, msg->sapIndex0, msg->signal.getId());
+
+ base::debugLogData( BD_SIGNALDATA, msg->signal.getPayload(), msg->signal.getPayloadSize());
+
+ // Inject the signal into the capsule.
+ msg->destPort->slot->capsule->inject((UMLRTMessage)*msg);
+ }
+ }
+ // Put the message back in the pool (handles signal allocation also).
+ umlrt::MessagePutToPool(msg);
+ }
+ // Wait on the incoming queue or a timeout.
+ if (!abort)
+ {
+ wait();
+ }
+ }
+ // Leave this output in here for now.
+ printf("Controller %s is aborting.\n", getName());
+
+ // Bug 215 - must destroy owned capsules + slots here.
+
+ return(NULL);
+}
+
+/*static*/ void UMLRTController::serializeIncarnateData( const void * userData, const UMLRTObject_class * type, size_t * sizeSerializedDataP, void * * serializedDataP )
+{
+ (*sizeSerializedDataP) = 0;
+ (*serializedDataP) = NULL;
+
+ if (userData != NULL)
+ {
+ if (type == NULL)
+ {
+ FATAL("Type descriptor missing. Has been previously checked.");
+ }
+ // Define data pointer and size.
+ if (((*serializedDataP) = malloc((*sizeSerializedDataP) = type->getSize(type))) == NULL)
+ {
+ FATAL("could not allocate memory for serialized data.");
+ }
+ // Encode the data.
+ type->encode(type, userData, (*serializedDataP));
+ }
+ // The controller frees the serialized data buffer after it's been copied to the initialize message.
+}
+
+// Set the error code.
+void UMLRTController::setError( Error error )
+{
+ lastError = error;
+ if ((error != E_OK) && (base::debugGetEnabledTypes() & (1 << BD_ERROR)) != 0)
+ {
+ BDEBUG(BD_ERROR, "Controller %s setError(%d):'%s'\n", getName(), lastError, strerror());
+ }
+}
+
+// Start the controller thread.
+void UMLRTController::spawn()
+{
+ // No arguments for this thread.
+ start(NULL);
+}
+
+// See umlrtcontroller.hh.
+const char * UMLRTController::strerror( ) const
+{
+ Error error = getError();
+ if ((error < 0) || (error >= E_MAX))
+ {
+ SWERR("error code(%d) out of range max(%d)", error, E_MAX);
+ return "unknown error code";
+ }
+ else
+ {
+ return errorToString[error];
+ }
+}
+
+void UMLRTController::startTimer( const UMLRTTimer * timer )
+{
+ char buf[UMLRTTimespec::TIMESPEC_TOSTRING_SZ];
+ BDEBUG(BD_TIMER, "start timer(%p) destPort(%s) isInterval(%d) priority(%d) payloadSize(%d) due(%s)\n",
+ timer, timer->destPort->role()->name, timer->isInterval, timer->signal.getPriority(), timer->signal.getPayloadSize(),
+ timer->isInterval ? timer->due.toStringRelative(buf, sizeof(buf)) : timer->due.toString(buf, sizeof(buf)));
+
+ timerQueue.enqueue(timer);
+}
+
+// Wait on an external message or a timeout.
+void UMLRTController::wait()
+{
+ // Linux-specific implementation for the time-being.
+
+ // If there is a timer running, this holds the remaining time as the timeout of the select.
+ struct timeval remainTimeval;
+
+ // We default to 'wait forever', unless a timer is running.
+ struct timeval * selectTimeval = NULL;
+
+ bool wait = true; // Set this false if a timer is due or an incoming message appeared.
+
+ // Get the time remaining on the first timer in the queue (if one exists).
+ if (!timerQueue.isEmpty())
+ {
+ UMLRTTimespec remainTimespec = timerQueue.timeRemaining();
+
+ if (remainTimespec.isZeroOrNegative())
+ {
+ // The timer is due - don't wait.
+ wait = false;
+ BDEBUG(BD_TIMER, "%s:timer is due\n", name);
+ }
+ else
+ {
+ // A timer is waiting but is not yet due. Set up the timeout. Will be non-zero.
+ remainTimeval.tv_sec = remainTimespec.tv_sec;
+ remainTimeval.tv_usec = remainTimespec.tv_nsec / 1000;
+
+ char tmbuf[UMLRTTimespec::TIMESPEC_TOSTRING_SZ];
+ BDEBUG(BD_TIMER, "%s: timer is not due - remain(%s)\n", name, remainTimespec.toStringRelative(tmbuf, sizeof(tmbuf)));
+
+ // Select will not wait forever.
+ selectTimeval = &remainTimeval;
+ }
+ }
+ if (!incomingQueue.isEmpty())
+ {
+ BDEBUG(BD_CONTROLLER, "%s: incoming q non-empty\n", name);
+ wait = false;
+ }
+ if (wait)
+ {
+ // selectTimeval remains NULL if no timers are running. In that case, select will wait
+ // forever until a message is delivered or a new timer is added to the timer-queue.
+
+ // Get the queue notification file descriptors.
+ int msgNotifyFd = incomingQueue.getNotifyFd();
+ int timerFd = timerQueue.getNotifyFd();
+
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ FD_SET(msgNotifyFd, &fds);
+ FD_SET(timerFd, &fds);
+
+ // select wants to know the highest-numbered fd + 1.
+ int nfds = msgNotifyFd + 1;
+ if (timerFd > msgNotifyFd)
+ {
+ nfds = timerFd + 1;
+ }
+
+ // TODO - Bug 238 - DEBUG - remove this later.
+ if (!selectTimeval)
+ {
+ // DEBUG - the intent is to wait forever until notification, since no timer is pending.
+ // However, we wake the process up every 10 seconds during debugging.
+ // No harm in doing that - the controller returns here if it finds nothing to do.
+ remainTimeval.tv_sec = 10;
+ remainTimeval.tv_usec = 0;
+ selectTimeval = &remainTimeval;
+ }
+ // end DEBUG - remove this later
+
+ BDEBUG(BD_CONTROLLER, "%s: call select msgfd(%d) timeout[%d,%d]\n",
+ name,
+ msgNotifyFd,
+ selectTimeval ? selectTimeval->tv_sec : -1,
+ selectTimeval ? selectTimeval->tv_usec : -1);
+
+
+ if ((select(nfds, &fds, NULL, NULL, selectTimeval)) < 0)
+ {
+ FATAL_ERRNO("select");
+ }
+ if (FD_ISSET(msgNotifyFd, &fds))
+ {
+ // Clear message notify-pending.
+ incomingQueue.clearNotifyFd();
+ }
+ if (FD_ISSET(timerFd, &fds))
+ {
+ // Clear message notify-pending.
+ timerQueue.clearNotifyFd();
+ }
+ }
+}
+
+/*static*/ void UMLRTController::debugOutputModelPortDeferQueueWalk( const UMLRTMessage * msg, void *userData )
+{
+ BDEBUG(BD_MODEL, " msg: id(%d) data [%d]\n",
+ msg->signal.getId(),
+ *(int *)msg->signal.getPayload());
+}
+
+void UMLRTController::debugOutputModelPortDeferQueue( const UMLRTCommsPort * port )
+{
+ if (port != NULL)
+ {
+ if (port->deferQueue != NULL)
+ {
+ if (!port->deferQueue->isEmpty())
+ {
+ BDEBUG(BD_MODEL,
+ " defer queue:\n");
+ port->deferQueue->walk( (UMLRTQueue::walk_callback_t)debugOutputModelPortDeferQueueWalk, NULL);
+ }
+ }
+ }
+}
+
+void UMLRTController::debugOutputModelPort( const UMLRTCommsPort * port, size_t index )
+{
+ if (!port)
+ {
+ BDEBUG(BD_MODEL,
+" port [%u]: (null)\n", index);
+ }
+ else
+ {
+ BDEBUG(BD_MODEL,
+" %s[%u] (id %d) %s%s %s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+ port->role()->name,
+ port->role()->numFarEnd,
+ port->role()->id,
+ port->role()->protocol,
+ port->role()->conjugated ? "~" : "",
+ port->proxy ? "(proxy)" : "",
+ port->spp ? "(SPP " : "",
+ port->spp ? ((port->registeredName == NULL) ? "?" : port->registeredName) : "",
+ port->spp ? ")" : "",
+ port->sap ? "(SAP " : "",
+ port->sap ? ((port->registeredName == NULL) ? "?" : port->registeredName) : "",
+ port->sap ? ")" : "",
+ port->automatic ? "(auto)" : "",
+ port->notification ? "(notify)" : "",
+ (port->unbound) ? "('unbound')" : "",
+ port->relay ? "(relay)" : "",
+ port->wired ? "(wired)" : "",
+ port->locked ? "(locked)" : "");
+
+ for (size_t j = 0; j < port->numFarEnd; ++j)
+ {
+ const char * farEndSlotName = "(none)";
+ const char * farEndPortName = "(none)";
+ const char * farEndSlotNoInstance = "(no instance)";
+ size_t farEndIndex = 0;
+ if (port->farEnds[j].port != NULL)
+ {
+ farEndSlotName = port->farEnds[j].port->slot->name;
+ farEndPortName = port->farEnds[j].port->role()->name;
+ farEndIndex = port->farEnds[j].farEndIndex;
+ if (port->farEnds[j].port->slot->capsule != NULL)
+ {
+ farEndSlotNoInstance = "";
+ }
+ }
+ BDEBUG(BD_MODEL, " farEnd[%u] : -> { slot %s, port %s[%u] %s}\n",
+ j, farEndSlotName, farEndPortName, farEndIndex, farEndSlotNoInstance );
+ }
+ debugOutputModelPortDeferQueue(port);
+ }
+}
+
+void UMLRTController::debugOutputModelPortsArray( size_t numPorts, const UMLRTCommsPort * ports )
+{
+ if (numPorts == 0)
+ {
+ BDEBUG(BD_MODEL,
+" ports : (none)\n");
+ }
+ for (size_t i = 0; i < numPorts; ++i)
+ {
+ debugOutputModelPort(&ports[i], i);
+ }
+}
+
+void UMLRTController::debugOutputModelPortsList( size_t numPorts, const UMLRTCommsPort * * ports )
+{
+ if (numPorts == 0)
+ {
+ BDEBUG(BD_MODEL,
+" ports : (none)\n");
+ }
+ for (size_t i = 0; i < numPorts; ++i)
+ {
+ debugOutputModelPort(ports[i], i);
+ }
+}
+
+void UMLRTController::debugOutputClassInheritance( const UMLRTCapsuleClass * capsuleClass )
+{
+ const UMLRTCapsuleClass * parent = capsuleClass->super;
+
+ if (parent == NULL)
+ {
+ BDEBUG(BD_MODEL, "(none)");
+ }
+ while (parent != NULL)
+ {
+ BDEBUG(BD_MODEL, "-> %s ", parent->name);
+ parent = parent->super;
+ }
+ BDEBUG(BD_MODEL,"\n");
+}
+
+void UMLRTController::debugOutputSlots( const UMLRTSlot * slot )
+{
+ BDEBUG(BD_MODEL,
+" %s:\n", slot->name);
+ BDEBUG(BD_MODEL,
+" capsule instance : %-30s (%p)\n", slot->capsule ? slot->capsule->getName() : "(none)", slot->capsule);
+ if (slot->role() == NULL)
+ {
+ BDEBUG(BD_MODEL,
+" role : (none)\n");
+ }
+ else
+ {
+ BDEBUG(BD_MODEL,
+" role : %-30s [%d..%d] %s %s\n",
+ slot->role()->name,
+ slot->role()->multiplicityLower,
+ slot->role()->multiplicityUpper,
+ slot->role()->optional ? "optional " : "",
+ slot->role()->plugin ? "plugin " : "");
+
+ }
+ BDEBUG(BD_MODEL,
+" index : %d\n", slot->capsuleIndex);
+ BDEBUG(BD_MODEL,
+" class : %-30s (# sub-capsule roles : %u) (# border ports : %u) (# internal ports : %u)\n",
+ slot->capsuleClass->name,
+ slot->capsuleClass->numSubcapsuleRoles,
+ slot->capsuleClass->numPortRolesBorder,
+ slot->capsuleClass->numPortRolesInternal);
+ BDEBUG(BD_MODEL,
+" class inheritance : ");
+ debugOutputClassInheritance(slot->capsuleClass);
+ BDEBUG(BD_MODEL,
+" controller : %s\n", slot->controller->getName());
+ if (slot->slotToBorderMap == NULL)
+ {
+ BDEBUG(BD_MODEL,
+" slot to border map : (none)\n");
+ }
+ else
+ {
+ BDEBUG(BD_MODEL,
+" slot to border map :");
+ for (size_t i = 0; i < slot->role()->capsuleClass->numPortRolesBorder; ++i)
+ {
+ BDEBUG(BD_MODEL, " [sl %lu=cp %d]", i, slot->slotToBorderMap[i]);
+ }
+ BDEBUG(BD_MODEL,"\n");
+ }
+ if (slot->numPorts == 0)
+ {
+ BDEBUG(BD_MODEL,
+" slot ports : (none)\n");
+ }
+ else
+ {
+ BDEBUG(BD_MODEL,
+" slot ports :\n");
+ debugOutputModelPortsArray(slot->numPorts, slot->ports);
+ }
+ const UMLRTCommsPort * portlist;
+ const UMLRTCommsPort * * borderPorts;
+ if (slot->capsule == NULL)
+ {
+ BDEBUG(BD_MODEL,
+" capsule border ports : (none - no instance running in slot)\n");
+ }
+ else if ((borderPorts = slot->capsule->getBorderPorts()) == NULL)
+ {
+ BDEBUG(BD_MODEL,
+" capsule border ports : (no border ports)\n");
+ }
+ else if (slot->capsuleClass->numPortRolesBorder == 0)
+ {
+ BDEBUG(BD_MODEL,
+" capsule border ports : (class has no border ports)\n");
+ }
+ else
+ {
+ BDEBUG(BD_MODEL,
+" capsule border ports : \n");
+ debugOutputModelPortsList(slot->capsuleClass->numPortRolesBorder, borderPorts);
+ }
+ if (slot->capsule == NULL)
+ {
+ BDEBUG(BD_MODEL,
+" capsule internal ports : (none)\n");
+ }
+ else if ((portlist = slot->capsule->getInternalPorts()) == NULL)
+ {
+ BDEBUG(BD_MODEL,
+" capsule internal ports : (none)\n");
+ }
+ else
+ {
+ BDEBUG(BD_MODEL,
+" capsule internal ports :\n");
+ debugOutputModelPortsArray(slot->capsuleClass->numPortRolesInternal, portlist);
+ }
+ // recurse into parts.
+ if (slot->capsuleClass->numSubcapsuleRoles == 0)
+ {
+ BDEBUG(BD_MODEL,
+" # sub-capsule parts : (none)\n");
+ }
+ else
+ {
+ BDEBUG(BD_MODEL,
+" # sub-capsule parts : %d\n", slot->numParts);
+
+ for (size_t i = 0; i < slot->numParts; ++i)
+ {
+ BDEBUG(BD_MODEL,
+" role [%u]: %s [%d..%d] %s %s\n",
+ i,
+ slot->parts[i].role()->name,
+ slot->parts[i].role()->multiplicityLower,
+ slot->parts[i].role()->multiplicityUpper,
+ (slot->parts[i].role()->optional) ? "optional " : "",
+ (slot->parts[i].role()->plugin) ? "plugin " : "");
+ }
+ }
+ // Recurse into sub-structure outputing slot info.
+ for (size_t i = 0; i < slot->numParts; ++i)
+ {
+ for (size_t j = 0; j < slot->parts[i].numSlot; ++j)
+ {
+ debugOutputSlots(slot->parts[i].slots[j]);
+ }
+ }
+}
+
+void UMLRTController::debugOutputSlotContainment( const UMLRTSlot * slot, size_t nesting )
+{
+ for (size_t i = 0; i < nesting; ++i)
+ {
+ BDEBUG(BD_MODEL, " ");
+ }
+ BDEBUG(BD_MODEL, "{ %s, %s, %p, %s }\n",
+ slot->name,
+ (slot->capsule == NULL) ? "(none)" : slot->capsule->getName(),
+ slot->capsule,
+ slot->capsuleClass->name);
+ for (size_t i = 0; i < slot->numParts; ++i)
+ {
+ for (size_t j = 0; j < slot->parts[i].numSlot; ++j)
+ {
+ debugOutputSlotContainment(slot->parts[i].slots[j], nesting + 1);
+ }
+ }
+}
+
+void UMLRTController::debugOutputModel()
+{
+ // Acquire global RTS lock for this.
+ UMLRTFrameService::rtsLock();
+
+ char timebuf[UMLRTTimespec::TIMESPEC_TOSTRING_SZ];
+ UMLRTTimespec tm;
+
+ UMLRTTimespec::getClock(&tm);
+ BDEBUG(BD_MODEL, "Model structure at time %s:\n", tm.toString(timebuf, sizeof(timebuf)));
+
+ UMLRTCapsuleToControllerMap::debugOutputControllerList();
+
+ UMLRTCapsuleToControllerMap::debugOutputCapsuleList();
+
+ UMLRTProtocol::debugOutputServiceRegistration();
+
+ const UMLRTCapsule * top = UMLRTCapsuleToControllerMap::getCapsuleFromName("Top");
+
+ if (top == NULL)
+ {
+ BDEBUG(BD_MODEL, "ERROR: no 'Top' capsule found - no slot containment output.\n");
+ }
+ else
+ {
+ const UMLRTSlot * slot = top->getSlot();
+ BDEBUG(BD_MODEL, "Slot containment: { <slot>, <capsule name>, <capsule instance address>, <capsule class> } \n");
+ debugOutputSlotContainment( slot, 1 );
+ }
+ if (top == NULL)
+ {
+ BDEBUG(BD_MODEL, "ERROR: no 'Top' capsule found - no slot list output.\n");
+ }
+ else
+ {
+ const UMLRTSlot * slot = top->getSlot();
+ BDEBUG(BD_MODEL, "Slot list:\n");
+ debugOutputSlots( slot );
+ }
+ UMLRTFrameService::rtsUnlock();
+}
diff --git a/rts/umlrt/umlrtframeprotocol.cc b/rts/umlrt/umlrtframeprotocol.cc
new file mode 100644
index 0000000..771b8cd
--- /dev/null
+++ b/rts/umlrt/umlrtframeprotocol.cc
@@ -0,0 +1,372 @@
+// umlrtframeprotocol.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtcapsule.hh"
+#include "umlrtcapsuleid.hh"
+#include "umlrtcapsulepart.hh"
+#include "umlrtcommsport.hh"
+#include "umlrtcontroller.hh"
+#include "umlrtframeprotocol.hh"
+#include "umlrtframeservice.hh"
+#include "basefatal.hh"
+
+// Return true if a subClass is a sub-class of baseClass.
+bool UMLRTFrameProtocol::OutSignals::classIsKindOf( const UMLRTCommsPort * srcPort, const UMLRTCapsuleClass & subClass, const UMLRTCapsuleClass & baseClass )
+{
+ bool isKindOf = false;
+ const UMLRTCapsuleClass * parentClass = &subClass;
+
+ while ((parentClass != NULL) && !isKindOf)
+ {
+ if (parentClass == &baseClass)
+ {
+ isKindOf = true;
+ }
+ parentClass = parentClass->super;
+ }
+ return isKindOf;
+}
+
+// Return the name of a capsule class.
+const char * UMLRTFrameProtocol::OutSignals::className( const UMLRTCommsPort * srcPort, const UMLRTCapsuleClass & capsuleClass )
+{
+ return capsuleClass.name;
+}
+
+// Return the capsule class of a capsule instance.
+const UMLRTCapsuleClass & UMLRTFrameProtocol::OutSignals::classOf( const UMLRTCommsPort * srcPort, const UMLRTCapsuleId id )
+{
+ return *id.getCapsule()->getClass();
+}
+
+
+bool UMLRTFrameProtocol::OutSignals::deport( const UMLRTCommsPort * srcPort, const UMLRTCapsuleId id, const UMLRTCapsulePart * part )
+{
+ bool ok = false;
+
+ // Initialize error code to E_NONE, in case anybody looks at it after success.
+ srcPort->slot->controller->setError(UMLRTController::E_OK);
+
+ if (!id.isValid())
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_DEPORT_INVLD);
+ }
+ else if (!part->role()->plugin)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_DEPORT_NONPLUG);
+ }
+ else
+ {
+ UMLRTCapsule * capsule = id.getCapsule();
+
+ // Only specified the the capsule id within the part - we must find the slot being deported.
+ for (size_t i = 0; (i < part->role()->multiplicityUpper) && !ok; ++i)
+ {
+ if (part->slots[i]->capsule == capsule)
+ {
+ ok = true;
+ UMLRTFrameService::requestControllerDeport(part->slots[i], false/*lockAcquired*/);
+ }
+ }
+ if (!ok)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_DEPORT_NOINST);
+ }
+ }
+ return ok;
+}
+
+bool UMLRTFrameProtocol::OutSignals::destroy( const UMLRTCommsPort * srcPort, const UMLRTCapsuleId id )
+{
+ bool ok = false;
+ UMLRTCapsule * capsule = id.getCapsule();
+
+ // Initialize error code to E_NONE, in case anybody looks at it after success.
+ srcPort->slot->controller->setError(UMLRTController::E_OK);
+
+ if (capsule == NULL)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_DESTR_INVALID);
+ }
+ else if (capsule->getSlot()->role() == NULL)
+ {
+ FATAL("Destroying capsule without a role. Destroying Top?");
+ }
+ else if (capsule->getSlot()->role()->optional)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_DESTR_NONOPT);
+ }
+ else
+ {
+ // Either we destroy the capsule in this context or we send a DESTROY Controller Command to execute the destroy.
+ UMLRTFrameService::requestControllerDestroy(capsule->getSlot(), true/*isTopSlot*/, false/*lockAcquired*/);
+ ok = true;
+ }
+ return ok;
+}
+
+bool UMLRTFrameProtocol::OutSignals::destroy( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part )
+{
+ bool ok = false;
+
+ // Initialize error code to E_NONE, in case anybody looks at it after success.
+ srcPort->slot->controller->setError(UMLRTController::E_OK);
+
+ if (part->role() == NULL)
+ {
+ FATAL("Destroying part without a role. Destroying Top?");
+ }
+ if (!part->role()->optional)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_DESTR_NONOPT);
+ }
+ else
+ {
+ ok = true; // Assume ok from this point.
+
+ for (size_t i = part->role()->multiplicityLower; i < part->numSlot; ++i)
+ {
+ if (part->slots[i]->capsule != NULL)
+ {
+ // Either we destroy the capsule in this context or we send a DESTROY Controller Command to execute the destroy.
+ // This is a destroy of multiple slots and each is the 'top slot' of its associated sub-structure.
+ UMLRTFrameService::requestControllerDestroy(part->slots[i], true/*isTopSlot*/, false/*lockAcquired*/);
+ ok = true;
+ }
+ }
+ }
+ return ok;
+}
+
+bool UMLRTFrameProtocol::OutSignals::import( const UMLRTCommsPort * srcPort, const UMLRTCapsuleId id, const UMLRTCapsulePart * destPart, int index )
+{
+ bool ok = false;
+
+ if (!id.isValid())
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_IMPORT_NOINST);
+ }
+ else
+ {
+ ok = UMLRTFrameService::importCapsule(srcPort, id.getCapsule(), destPart, index);
+ }
+ return ok;
+}
+
+bool UMLRTFrameProtocol::OutSignals::import( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * srcPart, const UMLRTCapsulePart * dest, int index )
+{
+ bool ok = false;
+
+ if ((srcPart->numSlot > 1) && index == -1)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_IMPORT_PARTREPL);
+ }
+ else if (srcPart->slots[0]->capsule == NULL)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_IMPORT_NOINST);
+ }
+ else
+ {
+ ok = UMLRTFrameService::importCapsule(srcPort, srcPart->slots[0]->capsule, dest, index);
+ }
+ return ok;
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::incarnate( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, const char * logThread, int index ) const
+{
+ return UMLRTFrameService::incarnateCapsule(
+ srcPort,
+ part, // part
+ NULL, // capsuleClass
+ NULL, // userData
+ NULL, // type
+ logThread,
+ NULL, // controller
+ index
+ );
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::incarnate( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, UMLRTController * controller, int index ) const
+{
+ return UMLRTFrameService::incarnateCapsule(
+ srcPort,
+ part,
+ NULL, // capsuleClass
+ NULL, // userData
+ NULL, // type
+ NULL, // logThread
+ controller,
+ index
+ );
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::incarnate( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, const UMLRTCapsuleClass & capsuleClass, const char * logThread, int index ) const
+{
+ return UMLRTFrameService::incarnateCapsule(
+ srcPort,
+ part,
+ &capsuleClass,
+ NULL, // userData
+ NULL, // type
+ logThread,
+ NULL, // controller
+ index
+ );
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::incarnate( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, const UMLRTCapsuleClass & capsuleClass, UMLRTController * controller, int index ) const
+{
+ return UMLRTFrameService::incarnateCapsule(
+ srcPort,
+ part,
+ &capsuleClass,
+ NULL, // userData
+ NULL, // type
+ NULL, // logThread
+ controller,
+ index
+ );
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::incarnate( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, const void * userData, const UMLRTObject_class * type, const char * logThread, int index ) const
+{
+ return UMLRTFrameService::incarnateCapsule(
+ srcPort,
+ part,
+ NULL, // capsuleClass
+ userData,
+ type,
+ logThread,
+ NULL, // controller
+ index
+ );
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::incarnate( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, const void * userData, const UMLRTObject_class * type, UMLRTController * controller, int index ) const
+{
+ return UMLRTFrameService::incarnateCapsule(
+ srcPort,
+ part,
+ NULL, // capsuleClass
+ userData,
+ type,
+ NULL, // logThread
+ controller,
+ index
+ );
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::incarnate( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, const UMLRTCapsuleClass & capsuleClass, const void * userData, const UMLRTObject_class * type, const char * logThread, int index ) const
+{
+ return UMLRTFrameService::incarnateCapsule(
+ srcPort,
+ part,
+ &capsuleClass,
+ userData,
+ type,
+ logThread,
+ NULL, // controller
+ index
+ );
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::incarnate( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, const UMLRTCapsuleClass & capsuleClass, const void * userData, const UMLRTObject_class * type, UMLRTController * controller, int index ) const
+{
+ return UMLRTFrameService::incarnateCapsule(
+ srcPort,
+ part,
+ &capsuleClass,
+ userData,
+ type,
+ NULL, // logThread
+ controller,
+ index
+ );
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::incarnate( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, const UMLRTTypedValue * value, const char * logThread, int index ) const
+{
+ return UMLRTFrameService::incarnateCapsule(
+ srcPort,
+ part,
+ NULL, // capsuleClass
+ value->data, // userData
+ value->type, // type
+ logThread,
+ NULL, // controller
+ index
+ );
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::incarnate( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, const UMLRTTypedValue * value, UMLRTController * controller, int index ) const
+{
+ return UMLRTFrameService::incarnateCapsule(
+ srcPort,
+ part,
+ NULL, // capsuleClass
+ value->data, // userData
+ value->type, // type
+ NULL, // logThread
+ controller,
+ index
+ );
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::incarnate( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, const UMLRTCapsuleClass & capsuleClass, const UMLRTTypedValue * value, const char * logThread, int index ) const
+{
+ return UMLRTFrameService::incarnateCapsule(
+ srcPort,
+ part,
+ &capsuleClass,
+ value->data, // userData
+ value->type, // type
+ logThread,
+ NULL, // controller
+ index
+ );
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::incarnate( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, const UMLRTCapsuleClass & capsuleClass, const UMLRTTypedValue * value, UMLRTController * controller, int index ) const
+{
+ return UMLRTFrameService::incarnateCapsule(
+ srcPort,
+ part,
+ &capsuleClass,
+ value->data, // userData
+ value->type, // type
+ NULL, // logThread
+ controller,
+ index
+ );
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::incarnateAt( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, int index )
+{
+ return UMLRTFrameService::incarnateCapsule(
+ srcPort,
+ part, // part
+ NULL, // capsuleClass
+ NULL, // userData
+ NULL, // type
+ NULL, // logThread
+ NULL, // controller
+ index
+ );
+}
+
+const UMLRTCapsuleId UMLRTFrameProtocol::OutSignals::me( const UMLRTCommsPort * srcPort )
+{
+ return UMLRTCapsuleId(srcPort->slot->capsule);
+}
+
+const UMLRTCapsuleClass & myClass( const UMLRTCommsPort * srcPort )
+{
+ return *srcPort->slot->capsule->getClass();
+}
diff --git a/rts/umlrt/umlrtframeservice.cc b/rts/umlrt/umlrtframeservice.cc
new file mode 100644
index 0000000..d43ef90
--- /dev/null
+++ b/rts/umlrt/umlrtframeservice.cc
@@ -0,0 +1,1800 @@
+// umlrtframeservice.cc
+
+/*******************************************************************************
+* Copyright (c) 2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtapi.hh"
+#include "umlrtcapsule.hh"
+#include "umlrtcapsulepart.hh"
+#include "umlrtcapsulerole.hh"
+#include "umlrtcommsport.hh"
+#include "umlrtcommsportfarend.hh"
+#include "umlrtframeservice.hh"
+#include "umlrtprotocol.hh"
+#include "umlrtslot.hh"
+#include "basedebugtype.hh"
+#include "basedebug.hh"
+#include "basefatal.hh"
+
+// Use a global lock on the RTS for now for modifying ports and access far-end ports during message delivery.
+/*static*/ UMLRTMutex UMLRTFrameService::rtsGlobalLock;
+
+/*static*/ UMLRTRtsInterfaceUmlrt UMLRTFrameService::rtsifUmlrt;
+
+/*static*/ void UMLRTFrameService::bindSubcapsulePort ( bool isBorder, UMLRTCapsule * subcapsule, int portIndex, int farEndIndex )
+{
+ if (subcapsule != NULL)
+ {
+ BDEBUG(BD_CONNECT, "bind subcapsule %s portIndex %d fei %d\n", subcapsule->getName(), portIndex, farEndIndex);
+
+ subcapsule->bindPort(isBorder, portIndex, farEndIndex);
+ }
+}
+
+/*static*/ const UMLRTCommsPort * * UMLRTFrameService::bindPorts ( UMLRTSlot * slot, UMLRTCapsule * capsule, const UMLRTCapsuleClass * requestedClass,
+ const UMLRTCapsuleClass * slotClass, const UMLRTCommsPort * * borderPorts, bool bind, bool import )
+{
+ // Assumes global RTS lock acquired.
+
+ // For incarnate, the newBorderPorts will become the top capsule's border ports (caspule instance created in this context but initialized by controller.)
+ // For import, the returned border port list is discarded. It's run twice - once for checking in the context of the importer (no binding occurs)
+ // and once in the context of the slot's controller (where input border port list is actually updated.)
+
+ const UMLRTCommsPort * * newBorderPorts = new const UMLRTCommsPort * [requestedClass->numPortRolesBorder];
+
+ // The slot will get the importPortMap to assist with deport.
+ int * slotToBorderMap = new int[slotClass->numPortRolesBorder]; // Will hold index into newBorderPorts for each slot port.
+
+ // Remember the number of bound instances on each slot port for determining whether we need a proxy port or need to make connections.
+ size_t * slotBoundCount = new size_t[slotClass->numPortRolesBorder]; // Will hold the number of bound instances on each slot port.
+
+ // Each of the bound slot ports have to be bound to a capsule border port.
+ for (size_t i = 0; i < slotClass->numPortRolesBorder; ++i)
+ {
+ // Set all slotToBorderMap entries to 'capsule border port not mapped to this slot'.
+ slotToBorderMap[i] = -1;
+
+ // The number of bound instances on each slot port are remembered as each is visited.
+ slotBoundCount[i] = 0;
+ }
+ // Initialize 'newBorderPorts' - the new border ports for the capsule if binding succeeds.
+ for (size_t i = 0; i < requestedClass->numPortRolesBorder; ++i)
+ {
+ if ((borderPorts != NULL) && borderPorts[i] == NULL)
+ {
+ FATAL("slot %s capsule %s border port was NULL - should at least be the unbound port.",
+ slot->name, capsule->getName());
+ }
+ if ((borderPorts != NULL) && !borderPorts[i]->unbound)
+ {
+ // Existing capsule border port is bound either to a slot port or is a proxy port bound to one or more slots.
+ newBorderPorts[i] = borderPorts[i];
+ }
+ else
+ {
+ // Capsule border port is unbound (or we are incarnating and didn't have a set of pre-existing caspule border ports.)
+ newBorderPorts[i] = NULL;
+ }
+ }
+ // Port compatibility check.
+ // The ports are assumed to be ordered.
+
+ size_t border_i = 0; // Port index on requesting class. We're searching the capsule's border ports for unbound ports to match bound slot ports.
+
+ bool compatible = true; // Assume compatibility until we fail.
+
+ // Go through each port on the slot. Quit if we fail to find a match for any bound slot port. Do the actual binding after we've proven the capsule fits.
+ bool done = false;
+
+ for (size_t slot_i = 0; (slot_i < slotClass->numPortRolesBorder) && compatible && !done; ++slot_i)
+ {
+ bool match = false;
+
+ // Remember how many port instances were bound on the slot port.
+ slotBoundCount[slot_i] = bindPortsCountBound( &slot->ports[slot_i]);
+
+ // Loop through the remaining requesting capsule border ports looking for a match to slot port slot_i.
+ while (!done && !match)
+ {
+ // If port-name match.
+ if (!strcmp(slotClass->portRolesBorder[slot_i].name, requestedClass->portRolesBorder[border_i].name))
+ {
+ // Get the number of bound instances on the capsules's border port.
+ size_t borderBoundCount = bindPortsCountBound(newBorderPorts[border_i]);
+
+ if (strcmp(slotClass->portRolesBorder[slot_i].protocol, requestedClass->portRolesBorder[border_i].protocol))
+ {
+ // protocol mismatch.
+ done = !0;
+ BDEBUG(BD_BINDFAIL, "BIND FAIL:%d %d:slot %s (class %s) port %s (protocol %s) has MISMATCHED PROTOCOL with capsule (class %s) (protocol %s).\n",
+ slot_i, border_i, slot->name, slot->capsuleClass, slotClass->portRolesBorder[slot_i].name, slotClass->portRolesBorder[slot_i].protocol, requestedClass->name, requestedClass->portRolesBorder[border_i].protocol);
+ }
+ else if (slotClass->portRolesBorder[slot_i].conjugated != requestedClass->portRolesBorder[border_i].conjugated)
+ {
+ // conjugation mismatch.
+ done = !0;
+ BDEBUG(BD_BINDFAIL, "BIND FAIL:%d %d:slot %s port %s (protocol %s) has MISMATCHED CONJUGATION with capsule (class %s)\n",
+ slot_i, border_i, slot->name, slotClass->portRolesBorder[slot_i].name, slotClass->portRolesBorder[slot_i].protocol, requestedClass->name);
+ }
+ else if ((requestedClass->portRolesBorder[border_i].numFarEnd - borderBoundCount) < slotBoundCount[slot_i])
+ {
+ // capsule border port unbound instances insufficient for binding
+ done = !0;
+ BDEBUG(BD_BINDFAIL, "BIND FAIL:%d %d:slot %s port %s (protocol %s) BOUND PORTS [%lu] EXCEEDS CAPSULE'S UNBOUND (class %s) port repl unbound instances [%lu].\n",
+ slot_i, border_i, slot->name, slotClass->portRolesBorder[slot_i].name, slotClass->portRolesBorder[slot_i].protocol, slotBoundCount[slot_i], requestedClass->name,
+ requestedClass->portRolesBorder[border_i].numFarEnd - borderBoundCount);
+ }
+ else if (slotBoundCount[slot_i] > 0)
+ {
+ // The slot port has to be bound to this port because it has bindings.
+
+ BDEBUG(BD_BINDDEBUG, "BIND DEBUG:%d %d:slot %s port %s (protocol %s) repl %lu MATCHED to capsule border port (bindings reqd).\n",
+ slot_i, border_i, slot->name, slotClass->portRolesBorder[slot_i].name, slotClass->portRolesBorder[slot_i].protocol, slotClass->portRolesBorder[slot_i].numFarEnd);
+
+ // We can accommodate this port bind. Remember the details, but don't bind just yet.
+ match = true;
+ slotToBorderMap[slot_i] = border_i; // Remember which capsule border port index was mapped to slot slot_i.
+ if (newBorderPorts[border_i] == NULL)
+ {
+ // The capsule border port was not already mapped elsewhere, so just map this slot's port to this capsule border port for now.
+ newBorderPorts[border_i] = &slot->ports[slot_i];
+ }
+ }
+ else
+ {
+ // This is the case where a slot port matches the name (and protocol and conjugation) of the instantiated/incarnated
+ // capsule border port but has no bound instances. We don't map the slot port to the capsule border port, leaveing the capsule border
+ // port available for bindings elsewhere during future imports into other slots.
+ match = true;
+ BDEBUG(BD_BINDDEBUG, "BIND DEBUG:%d %d:slot %s port %s (protocol %s) repl %lu MATCH IGNORED (no bindings reqd).\n",
+ slot_i, border_i, slot->name, slotClass->portRolesBorder[slot_i].name, slotClass->portRolesBorder[slot_i].protocol, slotClass->portRolesBorder[slot_i].numFarEnd);
+ }
+ }
+ // Advance to next capsule border port - and quit if we've exhausted them.
+ if (++border_i >= requestedClass->numPortRolesBorder)
+ {
+ done = !0;
+ }
+ }
+ if (!match)
+ {
+ // Slot port slot_i didn't find a home in the capsule border port list, so fail port binding.
+ compatible = false;
+ BDEBUG(BD_BINDFAIL, "BIND FAIL:slot %s port %s (protocol %s) repl %lu FAILED TO BIND.\n",
+ slot->name, slotClass->portRolesBorder[slot_i].name, slotClass->portRolesBorder[slot_i].protocol, slotClass->portRolesBorder[slot_i].numFarEnd);
+ }
+ }
+ if (compatible && bind)
+ {
+ // Succeeded with the bind-check. Redefine the slot's map from slot-port-to-capsule-border-port.
+ if (slot->slotToBorderMap != NULL)
+ {
+ FATAL("Slot %s already had a slotToBorderMap during port binding.", slot->name);
+ }
+ if (bind)
+ {
+ // For both incarnate and import, we're binding and define the slotToBorderMap here for deporting or destroy.
+ slot->slotToBorderMap = slotToBorderMap;
+ }
+ // For import, we do port binding. For incarnate, we may create proxy ports.
+
+ // First, replace any matched slot ports in the new capsule border port list with proxies where they are required.
+ // We create proxy ports when the capsule border port repl > slot port repl, or the slot port has unbound instances.
+ for (size_t slot_i = 0; (slot_i < slotClass->numPortRolesBorder); ++slot_i)
+ {
+ // If this slot port was bound to a capsule border port in the check above AND the capsule didn't have a previous binding on the port.
+ if ((slotToBorderMap[slot_i] != -1) && (newBorderPorts[slotToBorderMap[slot_i]] == &slot->ports[slot_i]))
+ {
+ // If we get to here, the capsule border port that matched the slot port was not previously bound.
+ // We've assigned the slot port as a 'possible' capsule border port.
+
+ // Here we check whether the slot port can stay as the border port. If not, we create a proxy port for the capsule border port.
+
+ // If the slot port replication count is less than the capsule border port can support
+ // or the slot had unbound instances...
+ if ( (slot->ports[slot_i].numFarEnd < requestedClass->portRolesBorder[slotToBorderMap[slot_i]].numFarEnd)
+ || (slot->ports[slot_i].numFarEnd > slotBoundCount[slot_i]))
+ {
+ // ...create a proxy port with a new far-end list to accommodate the capsule border port's larger replication count.
+ // Duplicate the bound connections that were already on the slot port. The slot port's far-end still points the slot port, which is good.
+
+ // If we're importing, we notify the capsule instance of the bound instances while we copy them to the proxy port.
+ // For incarnation, the capsule class instantiate function will handle the port binding.
+ newBorderPorts[slotToBorderMap[slot_i]] = bindPortsCreateProxyPort(slot, capsule, borderPorts, &slot->ports[slot_i], requestedClass, slotToBorderMap[slot_i], import);
+ BDEBUG(BD_BINDDEBUG, "BIND DEBUG:%d %d:slot %s port %s (protocol %s) repl %lu CREATE PROXY PORT repl %lu\n",
+ slot_i, slotToBorderMap[slot_i], slot->name, slotClass->portRolesBorder[slot_i].name, slotClass->portRolesBorder[slot_i].protocol, slotClass->portRolesBorder[slot_i].numFarEnd,
+ requestedClass->portRolesBorder[slotToBorderMap[slot_i]].numFarEnd);
+ }
+ else
+ {
+ // The slot port is fully bound and the capsule border port was unbound.
+ // The slot port becomes the capsule border port because all slot ports are bound.
+ newBorderPorts[slotToBorderMap[slot_i]] = &slot->ports[slot_i];
+
+ BDEBUG(BD_BINDDEBUG, "BIND DEBUG:%d %d:slot %s port %s (protocol %s) repl %lu MATCH BECOMES capsule border port\n",
+ slot_i, slotToBorderMap[slot_i], slot->name, slotClass->portRolesBorder[slot_i].name, slotClass->portRolesBorder[slot_i].protocol, slotClass->portRolesBorder[slot_i].numFarEnd,
+ requestedClass->portRolesBorder[slotToBorderMap[slot_i]].numFarEnd);
+
+ // If this is import, we notify the capsule instance here.
+ if (import)
+ {
+ for (size_t i = 0; i < requestedClass->portRolesBorder[slotToBorderMap[slot_i]].numFarEnd; ++i)
+ {
+ capsule->bindPort(true/*isBorder*/, slotToBorderMap[slot_i], i);
+ }
+ // Inform the capsule of the entire port binding.
+ BDEBUG(BD_BIND, "capsule %s (class %s) bind entire %s port %d during import\n", slot->name, slot->capsuleClass->name,
+ (borderPorts[slotToBorderMap[slot_i]] == &slot->ports[slot_i]) ? "slot" : "proxy", slotToBorderMap[slot_i]);
+ }
+ }
+ }
+ else if (slotToBorderMap[slot_i] != -1)
+ {
+ // Will never be true for incarnate here, because incarnate didn't have capsule border ports to begin with.
+ // This border port (that is NOT this slot's border port) can accommodate this slot port because it has enough free instances.
+ // Previously unbound port instances of the capsule border port are used to accommodate this slot port's far-ends.
+ // Bind the bound slot instances to the far-ends of unbound capsule border port instances.
+ bindPortsAddFarEnds(borderPorts[slotToBorderMap[slot_i]], slotToBorderMap[slot_i], &slot->ports[slot_i], capsule, import);
+ BDEBUG(BD_BINDDEBUG, "BIND DEBUG:%d %d:slot %s port %s (protocol %s) repl %lu bindings added to previously existing border port\n",
+ slot_i, slotToBorderMap[slot_i], slot->name, slotClass->portRolesBorder[slot_i].name, slotClass->portRolesBorder[slot_i].protocol, slotClass->portRolesBorder[slot_i].numFarEnd);
+ }
+ BDEBUG(BD_BINDDEBUG, "BIND OK:%d: slot %s port %s (protocol %s) (repl %d) %s capsule (class %s) border port%s.\n",
+ slot_i,
+ slot->name,
+ slotClass->portRolesBorder[slot_i].name,
+ slotClass->portRolesBorder[slot_i].protocol,
+ slotClass->portRolesBorder[slot_i].numFarEnd,
+ (slotToBorderMap[slot_i] != -1) ?
+ ((newBorderPorts[slotToBorderMap[slot_i]] == &slot->ports[slot_i]) ? "BECOMES the" : "PROXY USED for the") : "NO BINDS REQUIRED and the",
+ requestedClass->name,
+ (slotToBorderMap[slot_i] != -1) ? "" : " was NOT BOUND");
+ }
+ // We've done all slot port binding to capsule border port. Import binding has called the capsule instance to notify of the bindings.
+ // At this point, any capsule border port that remains unbound by the algorithm above is still 'completely unbound' and we can
+ // create the 'unbound port' for this one.
+ for (size_t i = 0; i < requestedClass->numPortRolesBorder; ++i)
+ {
+ if (newBorderPorts[i] == NULL)
+ {
+ newBorderPorts[i] = createPorts(slot, requestedClass, 1, requestedClass->portRolesBorder, true/*border*/, true/*proxy*/, i/*roleIndexOffset*/, true/*isUnbound*/);
+ }
+ }
+ }
+ if (!compatible)
+ {
+ // Binding failed - delete resources. (If binding succeeded, the slot has the 'slotToBorderMap' and the newBorderPorts
+ // is returned to the caller (to be passed to the initial capsule instantiation during incarnation and discarded (but indicates
+ // success) for import in the context of the importer. Binding is re-done for import in the context of the capsule's controller.
+ delete[] slotToBorderMap;
+ slotToBorderMap = NULL;
+ delete[] newBorderPorts;
+ newBorderPorts = NULL;
+ }
+ return newBorderPorts;
+}
+
+/*static*/ void UMLRTFrameService::bindPortsAddFarEnds ( const UMLRTCommsPort * capsuleBorderPort, int border_i, const UMLRTCommsPort * slotport, UMLRTCapsule * capsule, bool import )
+{
+ // Assumes global RTS lock already acquired.
+
+ // We're adding the slot port bound far-ends to free far-end instances on a capsule proxy port.
+ // For every slot port instance, find a free home on the capsule border port.
+ for (size_t i = 0; i < slotport->numFarEnd; ++i)
+ {
+ size_t border_fei = 0; // Start at the first 'far-end instance' (fei) of the capsule border port.
+
+ if (slotport->farEnds[i].port != NULL)
+ {
+ // slot port far end is connected, search forward through the capsule border port looking for an unbound instance.
+ bool bound = false;
+ while ((border_fei < capsuleBorderPort->numFarEnd) && !bound)
+ {
+ if (capsuleBorderPort->farEnds[border_fei].port == NULL)
+ {
+ // Connect the slot port's far-end to the (formerly) unbound far end on the capsule's border port.
+ // NOTE: The slot port far-end is not modified.
+ capsuleBorderPort->farEnds[border_fei].port = slotport->farEnds[i].port;
+ capsuleBorderPort->farEnds[border_fei].farEndIndex = slotport->farEnds[i].farEndIndex;
+ bound = true;
+
+ BDEBUG(BD_BIND, "capsule %s (class %s) bind proxy port instance %d %d during import\n",
+ capsuleBorderPort->slot->name, capsuleBorderPort->slot->capsuleClass->name, border_i, border_fei);
+
+ if (import)
+ {
+ // For import, we notify the capsule instance of the bind.
+ capsule->bindPort(true/*isBorder*/, border_i, border_fei);
+ }
+ }
+ border_fei++;
+ }
+ if (border_fei >= capsuleBorderPort->numFarEnd && !bound)
+ {
+ // Should never happen. We'd already verified that the capsuleBorderPort can accommodate the slot bindings.
+ FATAL("failed to connect all slot port's far-ends to the capsule border port during import.");
+ }
+ }
+ }
+}
+
+/*static*/ size_t UMLRTFrameService::bindPortsCountBound ( const UMLRTCommsPort * port )
+{
+ // Assumes global RTS lock already acquired.
+
+ size_t count = 0; // The 'unbound port' has no far-ends.
+
+ if (port != NULL)
+ {
+ for (size_t i = 0; i < port->numFarEnd; ++i)
+ {
+ if (port->farEnds[i].port != NULL)
+ {
+ ++count;
+ }
+ }
+ }
+ return count;
+}
+
+/*static*/ const UMLRTCommsPort * UMLRTFrameService::bindPortsCreateProxyPort ( UMLRTSlot * slot, UMLRTCapsule * capsule, const UMLRTCommsPort * * borderPorts, const UMLRTCommsPort * slotPort, const UMLRTCapsuleClass * requestedClass, int border_i, bool import )
+{
+ // Assumes global RTS lock already acquired.
+
+ // Create the border port unconnected.
+ const UMLRTCommsPort * newBorderPort = createPorts( slot, requestedClass, 1, &requestedClass->portRolesBorder[border_i], true/*border*/, true/*proxy*/, border_i);
+
+ // Copy connections from the slot port. The border port is larger - the 'extra' port instances remain unconnected.
+ for (size_t i = 0; i < slotPort->numFarEnd; ++i)
+ {
+ newBorderPort->farEnds[i].farEndIndex = slotPort->farEnds[i].farEndIndex;
+ if (((newBorderPort->farEnds[i].port = slotPort->farEnds[i].port) != NULL) && import)
+ {
+ // We've just bound a far-end of a slot port to a new capsule border proxy port - notify the capsule instance when we're importing.
+ capsule->bindPort(true/*isBorder*/, border_i, i);
+ }
+ }
+ return newBorderPort;
+}
+
+/*static*/ bool UMLRTFrameService::bindServicePort ( const UMLRTCommsPort * sapPort, const UMLRTCommsPort * sppPort )
+{
+ bool ok = false;
+
+ BDEBUG(BD_SAP, "SAP [%s %s] bind to SPP [%s %s]\n", (sapPort == NULL) ? "NULL" : sapPort->slot->name, (sapPort == NULL) ? "NULL" : sapPort->role()->name,
+ (sppPort == NULL) ? "NULL" : sppPort->slot->name, (sppPort == NULL) ? "NULL" : sppPort->role()->name);
+
+ if (sapPort && sppPort)
+ {
+ if (sapPort->numFarEnd != 1)
+ {
+ FATAL("SAP slot %s port %s has > 1 far-ends (has %d)", sapPort->slot->name, sapPort->role()->name, sapPort->numFarEnd);
+ }
+ else if (sapPort->farEnds[0].port != NULL)
+ {
+ FATAL("SAP slot %s port %s already connected to slot %s port %s", sapPort->slot->name, sapPort->role()->name,
+ sapPort->farEnds[0].port->slot->name, sapPort->farEnds[0].port->role()->name);
+ }
+ else if (strcmp(sapPort->role()->protocol, sppPort->role()->protocol))
+ {
+ // TODO - should inform user - Bug 246
+ BDEBUG(BD_SWERR, "binding SAP[%s %s] to SPP[%s %s] mismatched protocol sap(%s) spp(%s)\n", sapPort->slot->name, sapPort->role()->name,
+ sppPort->slot->name, sppPort->role()->name, sapPort->role()->protocol, sppPort->role()->protocol);
+ }
+ else if (sapPort->role()->conjugated == sppPort->role()->conjugated)
+ {
+ // TODO - should inform user - Bug 246
+ BDEBUG(BD_SWERR, "binding SAP[%s %s] to SPP[%s %s] conjugation not complementary\n", sapPort->slot->name, sapPort->role()->name,
+ sppPort->slot->name, sppPort->role()->name);
+ }
+ else
+ {
+ for (size_t i = 0; (i < sppPort->numFarEnd) && !ok; ++i)
+ {
+ if (sppPort->farEnds[i].port == NULL)
+ {
+ connectPorts(sapPort, 0, sppPort, i);
+ sendBoundUnbound(sapPort, 0, sppPort, i, true/*isBind*/);
+ sendBoundUnbound(sppPort, i, sapPort, 0, true/*isBind*/);
+ ok = true;
+ }
+ }
+ }
+ }
+ return ok;
+}
+
+/*static*/ void UMLRTFrameService::condemnParts ( UMLRTSlot * slot )
+{
+ slot->condemned = true;
+ for (size_t i = 0; i < slot->numParts; ++i)
+ {
+ for (size_t j = 0; j < slot->parts[i].numSlot; ++j)
+ {
+ // set 'condemned' on sub-structure.
+ condemnParts(slot->parts[i].slots[j]);
+ }
+ }
+}
+
+/*static*/ void UMLRTFrameService::connectPorts ( const UMLRTCommsPort * p1, size_t index1, const UMLRTCommsPort * p2, size_t index2 )
+{
+ // Assumes global RTS lock already acquired.
+
+ BDEBUG(BD_CONNECT, "connectPorts: ");
+ if (p1 == NULL)
+ {
+ BDEBUG(BD_CONNECT, "P1 NULL");
+ }
+ else if (p1->unbound)
+ {
+ BDEBUG(BD_CONNECT, "P1 UNBOUND");
+ }
+ else
+ {
+ BDEBUG(BD_CONNECT, "P1 slot(%s) port(%s[%lu]) id(%d) (%s) repl sz(%d)\n",
+ p1->slot->name,
+ p1->role()->name,
+ index1,
+ p1->role()->id,
+ p1->role()->protocol,
+ p1->role()->numFarEnd);
+
+ // if p1 has a far end, connect p1's far-end to p2.
+ if (p1->farEnds[index1].port)
+ {
+ size_t original_index1 = index1;
+ index1 = p1->farEnds[index1].farEndIndex;
+ p1 = p1->farEnds[original_index1].port;
+ BDEBUG(BD_CONNECT, " *** connecting P1->farEnd: slot(%s) port(%s[%lu]) id(%d) (%s) repl sz(%d)\n",
+ p1->slot->name,
+ p1->role()->name,
+ index1,
+ p1->role()->id,
+ p1->role()->protocol,
+ p1->role()->numFarEnd);
+ }
+ if (p2 == NULL)
+ {
+ BDEBUG(BD_CONNECT, " P2 NULL\n");
+ }
+ else if (p2->unbound)
+ {
+ BDEBUG(BD_CONNECT, " P2 UNBOUND\n");
+ }
+ else
+ {
+ BDEBUG(BD_CONNECT, " P2 slot(%s) port(%s[%lu]) id(%d) (%s) repl sz(%d)\n",
+ p2->slot->name,
+ p2->role()->name,
+ index2,
+ p2->role()->id,
+ p2->role()->protocol,
+ p2->role()->numFarEnd);
+ }
+ p1->farEnds[index1].port = p2->unbound ? NULL : p2;
+ p1->farEnds[index1].farEndIndex = p2->unbound ? 0 : index2;
+ if ((p2 != NULL) && (!p2->unbound))
+ {
+ if (index2 > p2->numFarEnd)
+ {
+ FATAL("slot %s port %s p1 %s[%d] p2 %s[%d] p2 index exceeds p2->numFarEnd(%d)",
+ p1->slot->name, p1->role()->name, index1, p2->role()->name, index2, p2->numFarEnd);
+ }
+ p2->farEnds[index2].port = p1;
+ p2->farEnds[index2].farEndIndex = index1;
+ }
+ }
+}
+
+/*static*/ void UMLRTFrameService::connectRelayPort ( const UMLRTCommsPort * relayPort, size_t relayIndex, const UMLRTCommsPort * destPort, size_t destIndex )
+{
+ // Assumes global RTS lock already acquired.
+
+ BDEBUG(BD_CONNECT, "connectRelayPort: ");
+ if (relayPort == NULL)
+ {
+ BDEBUG(BD_CONNECT, "P1 NULL");
+ }
+ else if (relayPort->unbound)
+ {
+ BDEBUG(BD_CONNECT, "P1 UNBOUND");
+ }
+ else
+ {
+ relayPort->relay = true;
+
+ BDEBUG(BD_CONNECT, "P1 slot(%s) port(%s[%lu]) id(%d) (%s) repl sz(%d)\n",
+ relayPort->slot->name,
+ relayPort->role()->name,
+ relayIndex,
+ relayPort->role()->id,
+ relayPort->role()->protocol,
+ relayPort->role()->numFarEnd);
+
+ size_t original_index1 = relayIndex;
+ relayIndex = relayPort->farEnds[relayIndex].farEndIndex;
+ relayPort = relayPort->farEnds[original_index1].port;
+ if (relayPort == NULL)
+ {
+ BDEBUG(BD_CONNECT, " *** connecting P1->farEnd: NULL - will leave p2 'as is'\n");
+ }
+ else
+ {
+ BDEBUG(BD_CONNECT, " *** connecting P1->farEnd: slot(%s) port(%s[%lu]) id(%d) (%s) repl sz(%d)\n",
+ relayPort->slot->name,
+ relayPort->role()->name,
+ relayIndex,
+ relayPort->role()->id,
+ relayPort->role()->protocol,
+ relayPort->role()->numFarEnd);
+ }
+ if ((relayPort != NULL) && (destPort != NULL))
+ {
+ BDEBUG(BD_CONNECT, " P2 slot(%s) port(%s[%lu]) id(%d) (%s) repl sz(%d)\n",
+ destPort->slot->name,
+ destPort->role()->name,
+ destIndex,
+ destPort->role()->id,
+ destPort->role()->protocol,
+ destPort->role()->numFarEnd);
+ destPort->farEnds[destIndex].port = relayPort;
+ destPort->farEnds[destIndex].farEndIndex = relayIndex;
+ relayPort->farEnds[relayIndex].port = destPort;
+ relayPort->farEnds[relayIndex].farEndIndex = destIndex;
+ }
+ }
+}
+
+/*static*/ void UMLRTFrameService::controllerDeport ( UMLRTSlot * slot, bool synchronous, bool lockAcquired )
+{
+ // Get lock if we don't have it.
+ if (!lockAcquired)
+ {
+ rtsLock();
+ }
+ if (slot == NULL)
+ {
+ FATAL("Attempt deport from NULL slot.");
+ }
+ else if ((slot->capsule == NULL) && !synchronous)
+ {
+ // Must have received the deport request 'late'. Just ignore it.
+ BDEBUG(BD_IMPORT, "Received 'late' deport request for slot %s. Ignoring it.\n", slot->name);
+ }
+ else if (slot->slotToBorderMap == NULL)
+ {
+ FATAL("Attempt to deport from slot %s but slotToBorderMap was NULL.", slot->name);
+ }
+ else if (!slot->role()->plugin)
+ {
+ FATAL("Attempt to deport from slot %s but slot was not a plugin slot.", slot->name);
+ }
+ else
+ {
+ const UMLRTCommsPort * * borderPorts = slot->capsule->getBorderPorts();
+
+ controllerDeportUnbind(slot, borderPorts);
+
+ delete[] slot->slotToBorderMap;
+ slot->slotToBorderMap = NULL;
+
+ slot->capsuleClass = slot->role()->capsuleClass; // restore the original capsuleClass
+ slot->capsule = NULL; // Removes record of this instance, but the instance lives on in its original optional slot.
+ }
+ // Get lock if we don't have it.
+ if (!lockAcquired)
+ {
+ rtsUnlock();
+ }
+}
+
+void UMLRTFrameService::controllerDeportUnbind ( UMLRTSlot * slot, const UMLRTCommsPort * * borderPorts )
+{
+ // Assumes global RTS lock already acquired.
+
+ // Examine the capsule's class slotToBorderMap to see if any of the capsules border ports were mapped to this slot.
+ for (size_t slot_i = 0; slot_i < slot->capsule->getSlot()->role()->capsuleClass->numPortRolesBorder; ++slot_i)
+ {
+ int border_i = slot->slotToBorderMap[slot_i];
+
+ if (border_i != -1)
+ {
+ // This capsule border port slot had bindings on this slot port - notify the capsule about the unbinds from this slot.
+ if (borderPorts[border_i] == &slot->ports[slot_i])
+ {
+ // Capsule has a border port mapped directly to the slot port.
+ BDEBUG(BD_BIND, "capsule %s (class %s) unbind entire slot port %d during deport\n", slot->name, slot->capsuleClass->name, border_i);
+
+ for (size_t j = 0; j < slot->ports[slot_i].numFarEnd; ++j)
+ {
+ if (slot->ports[slot_i].farEnds[j].port != NULL)
+ {
+ // Notify the capsule.
+ slot->capsule->unbindPort(true/*isBorder*/, slot_i, j);
+
+ // Connect the slot port far-ends back to the slot.
+ connectPorts(&slot->ports[slot_i], j, &slot->ports[slot_i], j);
+ }
+ }
+ }
+ else
+ {
+ // This is a proxy port with individual instances that would have been bound during import binding.
+ if (!borderPorts[border_i]->proxy)
+ {
+ FATAL("Unbinding capsule %s from slot %s and mapped slot port[%d] != border port[%d] and port was not a proxy?",
+ slot->capsule->getSlot()->name, slot->name, slot_i, border_i );
+ }
+ // slotToBorderMap[border_i] should be -1 for unbound ports.
+ if (borderPorts[border_i]->unbound)
+ {
+ FATAL("Unbinding capsule %s from slot %s and mapped slot port[%d] != border port[%d] and port was unbound ?",
+ slot->capsule->getSlot()->name, slot->name, slot_i, border_i );
+ }
+ for (size_t j = 0; j < slot->ports[slot_i].numFarEnd; ++j)
+ {
+ if (slot->ports[slot_i].farEnds[j].port != NULL)
+ {
+ int instance_i;
+
+ if ((instance_i = findFarEndInstance(slot->ports[slot_i].farEnds[j].port, slot->ports[slot_i].farEnds[j].farEndIndex, borderPorts[border_i])) < 0)
+ {
+ FATAL("slot %s deport of capsule %s port %s bound to far end %s not found on border proxy port id(%d)",
+ slot->name, slot->capsule->getName(), slot->ports[slot_i].role()->name, slot->ports[slot_i].farEnds[j].port->slot->name, border_i);
+ }
+ else
+ {
+ // Callback into the capsule instance for unbinding the border port.
+ BDEBUG(BD_BIND, "capsule %s (class %s) unbind proxy port instance %d %d during deport\n", slot->name, slot->capsuleClass->name, border_i, instance_i);
+
+ // Notify capsule.
+ slot->capsule->unbindPort(true/*isBorder*/, border_i, instance_i);
+
+ // Reconnect the slot port instance to itself - it's far-end gets connected to the slot.
+ connectPorts( &slot->ports[slot_i], j, &slot->ports[slot_i], j);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/*static*/ void UMLRTFrameService::controllerDestroy ( UMLRTSlot * slot, bool isTopSlot, bool synchronous, bool lockAcquired )
+{
+ // Get RTS lock if we don't have it.
+ if (!lockAcquired)
+ {
+ rtsLock();
+ }
+ BDEBUG(BD_DESTROY, "Controller %s destroying slot %s. Capsule instance %s. The slot itself %s.\n",
+ slot->controller->getName(),
+ slot->name,
+ (slot->capsule != NULL) ? "will be deported or deleted" : "not present",
+ isTopSlot ? "will persist" : "will be destroyed");
+
+ if (slot->role() == NULL)
+ {
+ FATAL("Attempting to destroy a slot with no role. Destroying Top?");
+ }
+ if (slot->generated && !isTopSlot)
+ {
+ FATAL("Attempt to destroy a generated slot name(%d) class(%s) role(%s)",
+ slot->name, slot->capsuleClass->name, slot->role()->name);
+ }
+ if (isTopSlot)
+ {
+ // This is the top slot in a destruction. Special processing is required.
+ // The destruction of sub-slots of the top slot do not branch into this code.
+
+ // Mark all sub-slots as 'condemned' to cease signal activity on these.
+ condemnParts(slot);
+
+ if (slot->capsule != NULL)
+ {
+ // Send rtUnbound for top capsule ports.
+ sendBoundUnboundForCapsule(slot->capsule, false/*isBind*/);
+
+ // Reconnect the slot port far-ends with the slot ports.
+ for (size_t i = 0; i < slot->capsule->getClass()->numPortRolesBorder; ++i)
+ {
+ for (size_t j = 0; j < slot->ports[i].numFarEnd; ++j)
+ {
+ // Purge any deferred messages owned by capsule being destroyed.
+ slot->ports[i].purge();
+ slot->ports[i].relay = false; // Empty slots have no relay ports regardless of slot capsule class.
+ connectPorts( &slot->ports[i], j, &slot->ports[i], j );
+ }
+ }
+ }
+ // Recurse into sub-structure, either deporting or sending DEPORT Controller Commands.
+ deportParts(slot);
+
+ // Recurse into sub-structure, either destroying or sending DESTROY Controller Commands.
+ for (size_t i = 0; i < slot->numParts; ++i)
+ {
+ for (size_t j = 0; j < slot->parts[i].numSlot; ++j)
+ {
+ requestControllerDestroy(slot->parts[i].slots[j], false/*isTopSlot*/, true/*lockAcquired*/);
+ }
+ }
+ }
+ if (slot->capsule != NULL)
+ {
+ // If the slot had a running instance, delete it.
+
+ // Get a copy of the ports to destroy them here.
+ const UMLRTCommsPort * * borderPorts = slot->capsule->getBorderPorts();
+ const UMLRTCommsPort * internalPorts = slot->capsule->getInternalPorts();
+ const UMLRTCapsuleClass * capsuleClass = slot->capsule->getClass();
+
+ delete slot->capsule;
+ destroyPortList(capsuleClass->numPortRolesBorder, borderPorts);
+ destroyPorts(capsuleClass->numPortRolesInternal, internalPorts);
+
+ slot->capsule = NULL;
+ slot->capsuleClass = slot->role()->capsuleClass; // restore the original capsuleClass
+
+ // Destroy parts data-structures. The destroy requests have already been enqueued for sub-slots, but need to clean up this slot's part structures.
+ destroyPartsBookkeeping(slot->numParts, slot->parts);
+ slot->parts = NULL;
+ slot->numParts = 0;
+ }
+ else if ((slot->numParts != 0) || (slot->parts != NULL))
+ {
+ // Sanity check - slot didn't have a running instance, so there should be no bookkeeping.
+ FATAL("slot (%s) had no running instance, but had numParts(%d) != 0 or parts != NULL", slot->name, slot->numParts);
+ }
+ // If the slot ports were bound at run-time, there will be a map.
+ if (slot->slotToBorderMap != NULL)
+ {
+ delete[] slot->slotToBorderMap;
+ slot->slotToBorderMap = NULL;
+ }
+ // Delete messages bound for the slot and timers bound for the slot.
+ slot->controller->deallocateSlotResources( slot );
+
+ if (!isTopSlot)
+ {
+ // Is a sub-slot in a condemned sub-capsule structure - delete the slot itself.
+ destroyPorts(slot->numPorts, slot->ports);
+ free((void*)slot->name);
+ delete slot;
+ }
+ else
+ {
+ slot->condemned = false; // Top capsule slot no longer 'condemned' - it has no instance, but is not condemned.
+ }
+ // Unlock RTS if we locked it here.
+ if (!lockAcquired)
+ {
+ rtsUnlock();
+ }
+}
+
+// Import a capsule - run from the controller who owns the capsule instance being imported.
+/*static*/ bool UMLRTFrameService::controllerImport ( UMLRTSlot * slot, UMLRTCapsule * capsule, bool synchronous, bool lockAcquired )
+{
+ bool ok = false;
+
+ if (slot == NULL)
+ {
+ FATAL("attempt to import into NULL slot");
+ }
+ if (capsule == NULL)
+ {
+ FATAL("attempt to import NULL capsule into slot %s", slot->name);
+ }
+ BDEBUG(BD_IMPORT, "controller import capsule %s into slot %s.\n", capsule->getName(), slot->name);
+
+ if (slot->capsule)
+ {
+ if (synchronous)
+ {
+ // We're running this code from the initial import request thread - need to set the requestor's controller error code.
+ slot->controller->setError(UMLRTController::E_IMPORT_OCCUPIED);
+ }
+ else
+ {
+ // May occur if this was an IMPORT Controller Command and some previous command caused the slot to be occupied.
+ // SWERR for now. Whether this can actually happen is not known at this time.
+ BDEBUG(BD_SWERR, "slot(%s) import of capsule(%s) slot is occupied by capsule %s", slot->name, capsule->getName(), slot->capsule->getName());
+ }
+ }
+ else
+ {
+ if (!lockAcquired)
+ {
+ rtsLock();
+ }
+ const UMLRTCommsPort * * borderPorts = NULL; // Returned ports are not used - just used as an indication of the binding success.
+
+ if ((borderPorts = bindPorts(slot, capsule, capsule->getSlot()->capsuleClass, slot->role()->capsuleClass, capsule->getBorderPorts(), true/*bind*/, true/*import*/)) == NULL)
+ {
+ // Failed compatibility check.
+ if (synchronous)
+ {
+ // Need to set the requestor's error code.
+ slot->controller->setError(UMLRTController::E_IMPORT_COMPAT);
+ }
+ else
+ {
+ // May occur if this was an IMPORT Controller Command and some previous previous activity made the slot incompatible?
+ // SWERR for now.
+ BDEBUG(BD_SWERR, "Controller bind of ports during import failed into slot %s - capsule's original slot %s.", slot->name, capsule->getName());
+ }
+ }
+ else
+ {
+ // Import succeeded.
+ delete[] borderPorts; // Do not need this copy for import - the capsule's border ports are updated.
+
+ // Update the capsule instance here and it's capsule class. They are restored during deport. This controller owns the slot now.
+ slot->controller = capsule->getSlot()->controller;
+ slot->capsule = capsule;
+ slot->capsuleClass = slot->capsule->getSlot()->capsuleClass;
+ ok = true;
+ }
+ if (!lockAcquired)
+ {
+ rtsUnlock();
+ }
+ }
+ return ok;
+}
+
+// Recurse into sub-structure requesting a destroy of the slot from the associated controller.
+/*static*/ void UMLRTFrameService::controllerIncarnate ( UMLRTCapsule * capsule, size_t sizeSerializedData, void * serializedData )
+{
+ // Can't acquire the RTS lock for this, since the capsule initialize signal can result in signal sends from the capsule.
+ if (capsule == NULL)
+ {
+ FATAL("attempt to initialize NULL capsule");
+ }
+ initializeCapsule(capsule, sizeSerializedData, serializedData);
+}
+
+/*static*/ char * UMLRTFrameService::createName ( const char * prefix, const char * suffix, size_t index, bool appendIndex )
+{
+ char ind[23]; // 18,446,744,073,709,551,615 is largest possible index in a 64-bit architecture.
+ if (appendIndex)
+ {
+ snprintf(ind, sizeof(ind), "[%lu]", index);
+ }
+ char * name = (char*)malloc(strlen(prefix) + strlen(suffix) + strlen(ind) + 2);
+
+ if (!name)
+ {
+ FATAL("could not allocate part name.");
+ }
+ strcpy(name, prefix);
+ strcat(name, ".");
+ strcat(name, suffix);
+ if (appendIndex)
+ {
+ strcat(name, ind);
+ }
+ return name;
+}
+
+/*static*/ UMLRTCapsulePart * UMLRTFrameService::createParts ( const char * containerName, const UMLRTCapsuleClass * containerClass )
+{
+ UMLRTCapsulePart * parts = NULL;
+
+ if (containerClass->numSubcapsuleRoles)
+ {
+ parts = new UMLRTCapsulePart[containerClass->numSubcapsuleRoles];
+
+ for (size_t i = 0; i < containerClass->numSubcapsuleRoles; ++i)
+ {
+ const UMLRTCapsuleRole * role = &containerClass->subcapsuleRoles[i];
+ parts[i].containerClass = containerClass;
+ parts[i].numSlot = role->multiplicityUpper;
+ parts[i].slots = new UMLRTSlot * [role->multiplicityUpper];
+ parts[i].roleIndex = i;
+
+ for (size_t j = 0; j < role->multiplicityUpper; ++j)
+ {
+ // define the slot for this part
+ char * name = createName(containerName, role->name, j, (role->multiplicityUpper > 1)/*appendIndex*/);
+
+ const UMLRTCapsulePart * subcapsuleParts = createParts( name, role->capsuleClass );
+ UMLRTSlot templateSlot = {
+ name,
+ j, // capsuleIndex
+ role->capsuleClass,
+ containerClass,
+ i,
+ NULL, // capsule,
+ NULL, // controller
+ role->capsuleClass->numSubcapsuleRoles, // numParts
+ subcapsuleParts,
+ role->capsuleClass->numPortRolesBorder,
+ NULL, // ports
+ NULL, // slotToBorderMap
+ 0, // generated
+ 0, // condemned
+ };
+ parts[i].slots[j] = new UMLRTSlot(templateSlot);
+ parts[i].slots[j]->ports = createPorts( parts[i].slots[j], role->capsuleClass, role->capsuleClass->numPortRolesBorder, role->capsuleClass->portRolesBorder, true/*border*/);
+ }
+ }
+ }
+ return parts;
+}
+
+/*static*/ const UMLRTCommsPort * UMLRTFrameService::createPorts ( UMLRTSlot * slot, const UMLRTCapsuleClass * capsuleClass, size_t numPortRoles, const UMLRTCommsPortRole portRoles[],
+ bool border, bool importProxy, int roleIndexOffset, bool isUnbound )
+{
+ UMLRTCommsPort * ports = new UMLRTCommsPort[numPortRoles];
+
+ for (size_t i = 0; i < numPortRoles; ++i)
+ {
+ ports[i].containerClass = capsuleClass;
+ ports[i].roleIndex = i + roleIndexOffset;
+ ports[i].slot = slot;
+ ports[i].numFarEnd = isUnbound ? 0 : portRoles[i].numFarEnd;
+
+ ports[i].automatic = portRoles[i].automatic;
+ ports[i].border = border;
+ ports[i].generated = false;
+ ports[i].locked = portRoles[i].locked;
+ ports[i].notification = portRoles[i].notification;
+ ports[i].proxy = importProxy;
+ ports[i].relay = false; // Set true during port binding, if appropriate.
+ ports[i].sap = portRoles[i].sap;
+ ports[i].spp = portRoles[i].spp;
+ ports[i].unbound = isUnbound;
+ ports[i].wired = portRoles[i].wired;
+
+ if (portRoles[i].registeredName != NULL)
+ {
+ ports[i].registeredName = strdup(portRoles[i].registeredName);
+ }
+ else
+ {
+ ports[i].registeredName = NULL;
+ }
+ ports[i].generated = false;
+ if (ports[i].numFarEnd == 0)
+ {
+ ports[i].farEnds = NULL;
+ ports[i].deferQueue = NULL;
+ }
+ else
+ {
+ ports[i].farEnds = new UMLRTCommsPortFarEnd[portRoles[i].numFarEnd];
+ ports[i].deferQueue = new UMLRTMessageQueue();
+ }
+ // Leave all ports disconnected for now. We connect them after the structure is built.
+ for (size_t j = 0; j < ports[i].numFarEnd; ++j)
+ {
+ ports[i].farEnds[j].farEndIndex = 0;
+ ports[i].farEnds[j].port = NULL;
+ }
+ }
+ return ports;
+}
+
+/*static*/ const UMLRTCommsPort * * UMLRTFrameService::createBorderPorts ( UMLRTSlot * slot, size_t numPorts )
+{
+ const UMLRTCommsPort * * borderPorts = new const UMLRTCommsPort * [numPorts];
+
+ for (size_t i = 0; i < numPorts; ++i)
+ {
+ borderPorts[i] = &slot->ports[i];
+ }
+ return borderPorts;
+}
+
+/*static*/ void UMLRTFrameService::defineSlotControllers ( UMLRTSlot * slot, const UMLRTCapsulePart * part, const UMLRTCapsuleClass * capsuleClass,
+ const char * logThread, UMLRTController * assignedController, UMLRTController *defaultController, int index )
+{
+ // Use the logical thread name, controller instance, or the default controller to determine the assigned controller.
+ slot->controller = getCapsuleController(slot, part, logThread, assignedController, defaultController, index);
+
+ if (slot->controller == NULL)
+ {
+ FATAL("slot %s controller NULL", slot->name);
+ }
+ // Recurse into sub-structure
+ for (size_t i = 0; i < slot->numParts; ++i)
+ {
+ for (size_t j = 0; j < slot->parts[i].numSlot; ++j)
+ {
+ defineSlotControllers(slot->parts->slots[j],
+ &slot->parts[i],
+ slot->parts->slots[j]->capsuleClass,
+ logThread,
+ assignedController,
+ slot->controller,
+ j);
+ }
+ }
+}
+
+/*static*/ void UMLRTFrameService::deportParts ( UMLRTSlot * slot )
+{
+ // Assumes global RTS lock already acquired.
+
+ requestControllerDeport(slot, true/*lockAcquired*/);
+
+ for (size_t i = 0; i < slot->numParts; ++i)
+ {
+ for (size_t j = 0; j < slot->parts[i].numSlot; ++j)
+ {
+ // set 'condemned' on sub-structure.
+ deportParts(slot->parts[i].slots[j]);
+ }
+ }
+}
+
+/*static*/ void UMLRTFrameService::destroyPartsBookkeeping ( size_t numParts, const UMLRTCapsulePart * parts )
+{
+ // Assumes global RTS lock already acquired.
+
+ // Deletes the array of slot pointers, but the not the slots themselves.
+ for (size_t i = 0; i < numParts; ++i)
+ {
+ delete[] parts[i].slots;
+ }
+ delete[] parts;
+}
+
+/*static*/ void UMLRTFrameService::destroyPorts ( const size_t numPorts, const UMLRTCommsPort * ports )
+{
+ // Assumes global RTS lock already acquired.
+
+ if (ports != NULL)
+ {
+ for (size_t i = 0; i < numPorts; ++i)
+ {
+ if (ports[i].spp)
+ {
+ UMLRTProtocol::deregisterSppPort(&ports[i]);
+ }
+ else if (ports[i].sap)
+ {
+ UMLRTProtocol::deregisterSapPort(&ports[i]);
+ }
+ if (ports[i].generated)
+ {
+ FATAL("trying to destroy a static slot %s port %s", ports[i].slot, ports[i].role()->name);
+ }
+ if (ports[i].numFarEnd > 0)
+ {
+ delete[] ports[i].farEnds;
+ }
+ if (ports[i].deferQueue)
+ {
+ ports[i].purge();
+ delete ports[i].deferQueue;
+ }
+ if (ports[i].registeredName != NULL)
+ {
+ free(ports[i].registeredName);
+ }
+ }
+ delete[] ports;
+ }
+}
+
+/*static*/ void UMLRTFrameService::destroyPortList ( size_t numPorts, const UMLRTCommsPort * * ports )
+{
+ // Assumes global RTS lock already acquired.
+
+ if (ports != NULL)
+ {
+ for (size_t i = 0; i < numPorts; ++i)
+ {
+ if (ports[i])
+ {
+ if (ports[i]->proxy)
+ {
+ destroyPorts(1, ports[i]);
+ }
+ }
+ }
+ delete[] ports;
+ }
+}
+
+/*static*/ void UMLRTFrameService::disconnectPort ( const UMLRTCommsPort * port, int index )
+{
+ // Assumes global RTS lock already acquired.
+
+ if (port)
+ {
+ if (!port->unbound)
+ {
+ if (port->farEnds[index].port != NULL)
+ {
+ port->farEnds[index].port = NULL;
+ port->farEnds[index].farEndIndex = 0;
+ }
+ }
+ }
+}
+
+// Count the number of free far-ends on a port.
+/*static*/ size_t UMLRTFrameService::freeFarEndsCount ( const UMLRTCommsPort * port )
+{
+ size_t count = 0;
+
+ if (port != NULL)
+ {
+ for (size_t i = 0; i < port->numFarEnd; ++i)
+ {
+ if (port->farEnds[i].port == NULL)
+ {
+ ++count;
+ }
+ }
+ }
+ return count;
+}
+
+
+/*static*/ int UMLRTFrameService::findFarEndInstance ( const UMLRTCommsPort * farEnd, size_t farEndIndex, const UMLRTCommsPort * port )
+{
+ // Assumes global RTS lock already acquired.
+
+ int instance_i = -1;
+
+ if (farEnd)
+ {
+ if (!port->unbound)
+ {
+ for (size_t i = 0; (i < port->role()->numFarEnd) && (instance_i == -1); ++i)
+ {
+ if ((farEnd == port->farEnds[i].port) && (farEndIndex == port->farEnds[i].farEndIndex))
+ {
+ instance_i = i;
+ }
+ }
+ }
+ }
+ return instance_i;
+}
+
+// Assumes AT MOST one of 'logThread' or 'assignedController' is non-NULL. (Both may be NULL.)
+/*static*/ UMLRTController * UMLRTFrameService::getCapsuleController ( const UMLRTSlot * slot, const UMLRTCapsulePart * part, const char * logThread,
+ UMLRTController * assignedController, UMLRTController * defaultController, int slotIndex )
+{
+ UMLRTController * capsuleController = NULL;
+
+ if ((logThread != NULL) && (assignedController != NULL))
+ {
+ FATAL("Only one of logThread and controller may be non-NULL when assigning a controller to capsule %s.",
+ part->slots[slotIndex]->name);
+ }
+ else if (defaultController == NULL)
+ {
+ FATAL("Default controller NULL assigning a controller to capsule %s.",
+ part->slots[slotIndex]->name);
+ }
+ else if ((capsuleController = UMLRTCapsuleToControllerMap::getControllerForCapsule(part->slots[slotIndex]->name, slot->capsuleClass)) == NULL)
+ {
+ if ((logThread == NULL) && (assignedController == NULL))
+ {
+ capsuleController = defaultController; // Default to current controller.
+ }
+ else if (logThread != NULL)
+ {
+ if ((capsuleController = UMLRTCapsuleToControllerMap::getControllerFromName(logThread)) == NULL)
+ {
+ capsuleController = defaultController; // Default to current controller.
+ }
+ }
+ else if ((capsuleController = assignedController) == NULL)
+ {
+ capsuleController = defaultController; // Default to current controller.
+ }
+ }
+ BDEBUG(BD_CONTROLLER, "getCapsuleController capsule %s (index %d) input controller(%s) logThread(%s) assigned to %s\n",
+ part->slots[slotIndex]->name,
+ slotIndex,
+ assignedController == NULL ? "NULL" : assignedController->getName(),
+ logThread == NULL ? "NULL" : logThread, capsuleController->getName());
+
+ return capsuleController;
+}
+
+// Get the next free capsule slot - return -1 if there's none.
+/*static*/ int UMLRTFrameService::getNextFreeCapsuleSlot ( const UMLRTCapsulePart * part )
+{
+ int nextFree = -1;
+
+ // Look for a slot with no controller assigned.
+
+ for (size_t i = 0; (i < part->numSlot) && (nextFree == -1); ++i)
+ {
+ if (part->slots[i]->capsule == NULL)
+ {
+ nextFree = i;
+ }
+ }
+ return nextFree;
+}
+
+/*static*/ const UMLRTRtsInterface * UMLRTFrameService::getRtsInterface ( )
+{
+ return &rtsifUmlrt;
+}
+
+
+/*static*/ bool UMLRTFrameService::importCapsule ( const UMLRTCommsPort * srcPort, UMLRTCapsule * capsule, const UMLRTCapsulePart * destPart, int index )
+{
+ bool ok = false;
+
+ //const UMLRTSlot * srcSlot = capsule->getSlot();
+
+ if (!destPart->role()->plugin)
+ {
+ // Returned 'ok' will be false.
+ srcPort->slot->controller->setError(UMLRTController::E_IMPORT_NONPLUG);
+ }
+ // Validate the index if it was explicitly set.
+ else if ((index < -1) || (index >= (int)destPart->numSlot))
+ {
+ // Returned 'ok' will be false.
+ srcPort->slot->controller->setError(UMLRTController::E_IMPORT_INV_IDX);
+ }
+ else
+ {
+ if (index < 0)
+ {
+ index = getNextFreeCapsuleSlot(destPart);
+ }
+ if (index < 0)
+ {
+ // Returned 'ok' will be false.
+ srcPort->slot->controller->setError(UMLRTController::E_IMPORT_NO_FREE_SLOT);
+ }
+ else if (index >= (int)destPart->numSlot)
+ {
+ FATAL("internal error obtaining next free capsule index(%d) max(%d)", index, destPart->numSlot);
+ }
+ else
+ {
+ // Obtain RTS lock.
+ rtsLock();
+
+ UMLRTSlot * destSlot = destPart->slots[index];
+
+ const UMLRTCommsPort * * borderPorts = NULL; // Not used to modify capsule instance - only used to check binding.
+
+ bool compatible = true; // Assume we're compatible unless we've proven otherwise.
+
+ if (destSlot->capsule != NULL)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_IMPORT_OCCUPIED);
+ compatible = false;
+ }
+ else if (!capsule->getSlot()->controller->isMyThread())
+ {
+ // We are not running in the context of the controller that owns the capsule, all we'll do is check compatibility.
+ // We check the binding, but don't actually bind - that's done in the context of the slot's controller.
+
+ if ((borderPorts = bindPorts(destSlot, capsule, capsule->getSlot()->capsuleClass, destPart->role()->capsuleClass, capsule->getBorderPorts(), false/*bind*/, true/*import*/)) == NULL)
+ {
+ // Requested capsule type does not have port-compatibility.
+ srcPort->slot->controller->setError(UMLRTController::E_IMPORT_COMPAT);
+ compatible = false;
+ }
+ }
+ if (compatible)
+ {
+ // Either we're running in the context of the controller that owns the capsule (and port binding has not been verified,
+ // and the error code will be set by the following if it fails), or we're running in another thread, in which case the binding
+ // has been checked and is ok.
+ ok = requestControllerImport(destSlot, capsule, true/*lockAcquired*/);
+ }
+
+ // Unlock RTS
+ rtsUnlock();
+
+ // We don't need this copy of the border ports - it was used to check port binding success only.
+ if (borderPorts != NULL)
+ {
+ delete[] borderPorts;
+ }
+ }
+ }
+ return ok;
+}
+
+/*static*/ const UMLRTCapsuleId UMLRTFrameService::incarnateCapsule ( const UMLRTCommsPort * srcPort, const UMLRTCapsulePart * part, const UMLRTCapsuleClass * capsuleClass,
+ const void * userData, const UMLRTObject_class * type, const char * logThread, UMLRTController * assignedController, int index )
+{
+ UMLRTCapsuleId id(NULL);
+
+ // Default to part's capsuleClass.
+ if (capsuleClass == NULL)
+ {
+ capsuleClass = part->role()->capsuleClass;
+ }
+ // Verify type supplied if data supplied.
+ if ((userData) && (!type))
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_INC_DATATYPE);
+ }
+ // Validate the index if it was explicitly set.
+ else if ((index < -1) || (index >= (int)part->numSlot))
+ {
+ // Returned id will be the 'invalid capsule id'.
+ srcPort->slot->controller->setError(UMLRTController::E_INC_INV_IDX);
+ }
+ else if (!part->role()->optional)
+ {
+ // Attempting to incarnate into a non-optional slot.
+ srcPort->slot->controller->setError(UMLRTController::E_INC_NOT_OPT);
+ }
+ else
+ {
+ if (index < 0)
+ {
+ index = getNextFreeCapsuleSlot(part);
+ }
+ if (index < 0)
+ {
+ // Returned id will be the 'invalid capsule id'.
+ srcPort->slot->controller->setError(UMLRTController::E_INC_NO_FREE_SLOT);
+ }
+ else if (index >= (int)part->numSlot)
+ {
+ FATAL("internal error obtaining next free capsule index(%d) max(%d)", index, part->numSlot);
+ }
+ else
+ {
+ // Obtain global RTS lock
+ rtsLock();
+
+ UMLRTSlot * slot = part->slots[index];
+
+ const UMLRTCommsPort * * borderPorts = NULL; // Defines the border port mapping.
+
+ bool compatible = false;
+
+ if (slot == NULL)
+ {
+ FATAL("slot %s has NULL part slot[%d]", srcPort->slot->name, index);
+ }
+ if (slot->capsule != NULL)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_INC_SLOT_OCC);
+ }
+ else if (slot->parts != NULL)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_INC_PARTS_ERROR);
+ }
+ else if (slot->capsuleClass == capsuleClass)
+ {
+ compatible = true; // The border ports are the slot ports.
+ borderPorts = createBorderPorts(slot, capsuleClass->numPortRolesBorder);
+ }
+ else if ((borderPorts = bindPorts(slot, NULL, capsuleClass, part->role()->capsuleClass, NULL /*borderPorts*/, true/*bind*/, false/*import*/)) != NULL)
+ {
+ // Bound OK - and the capsule's borderPorts are defined.
+ compatible = true;
+ }
+ else
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_INC_COMPAT);
+ }
+ if (!compatible)
+ {
+ // Error already set - occupied, parts error, requested capsule type does not have port-compatibility.
+ if (borderPorts)
+ {
+ delete[] borderPorts;
+ }
+ }
+ else
+ {
+ // Over-write with occupier's type.
+ slot->capsuleClass = capsuleClass;
+
+ // Recurse into sub-structure creating parts.
+ slot->parts = createParts(slot->name, capsuleClass);
+ slot->numParts = capsuleClass->numSubcapsuleRoles;
+
+ // Recurse into sub-structure assigning the controllers to the capsule and parts.
+ defineSlotControllers(slot, part, capsuleClass, logThread, assignedController, srcPort->slot->controller, index);
+
+ BDEBUG(BD_INSTANTIATE, "instantiate capsule class %s into slot %s\n", slot->capsuleClass->name, slot->name);
+
+ slot->capsuleClass->instantiate(&rtsifUmlrt, slot, borderPorts);
+
+ if (slot->capsule)
+ {
+ // Recurse into sub-structure requesting the associated controllers to send the capsule initialize messages.
+ requestControllerIncarnate(slot, userData, type);
+ }
+ // Send rtBound to slot far-ends that are not relay ports.
+ incarnateSendBoundSlotFarEnd(slot);
+ id = UMLRTCapsuleId(slot->capsule);
+ }
+ rtsUnlock();
+ }
+ }
+ return id;
+}
+
+/*static*/ void UMLRTFrameService::incarnateSendBoundSlotFarEnd ( UMLRTSlot * slot )
+{
+ // Assumes global RTS lock already acquired.
+ const UMLRTCommsPort * * borderPorts = slot->capsule->getBorderPorts();
+
+ // Examine the capsule's class slotToBorderMap to see if any of the capsules border ports were mapped to this slot.
+ for (size_t slot_i = 0; (borderPorts != NULL) && (slot_i < slot->capsule->getSlot()->role()->capsuleClass->numPortRolesBorder); ++slot_i)
+ {
+ int border_i = slot->slotToBorderMap[slot_i];
+
+ if (border_i != -1)
+ {
+ // This capsule border port slot had bindings on this slot port - notify those far-ends of the rtBound.
+ if (borderPorts[border_i] == &slot->ports[slot_i])
+ {
+ // Capsule has a border port mapped directly to the slot port.
+ for (size_t j = 0; !borderPorts[border_i]->relay && (j < slot->ports[slot_i].numFarEnd); ++j)
+ {
+ // If port is non-relay, notify the far-end of rtBound.
+ if ((slot->ports[slot_i].farEnds[j].port != NULL) && !slot->ports[slot_i].relay)
+ {
+ // Send rtBound to far-end.
+ sendBoundUnbound( slot->ports[slot_i].farEnds[j].port, slot->ports[slot_i].farEnds[j].farEndIndex,
+ &slot->ports[slot_i], slot_i, true/*isBind*/);
+ }
+ }
+ }
+ // slotToBorderMap[border_i] should be -1 for unbound ports.
+ else if (borderPorts[border_i]->unbound)
+ {
+ FATAL("Sending rtBound to slot far end. slot %s and mapped slot port[%d] != border port[%d] and port was unbound ?",
+ slot->name, slot_i, border_i );
+ }
+ else
+ {
+ for (size_t j = 0; !borderPorts[border_i]->relay && (j < slot->ports[slot_i].numFarEnd); ++j)
+ {
+ // Non relay port.
+ if (slot->ports[slot_i].farEnds[j].port != NULL)
+ {
+ int instance_i;
+ // This slot port instance has a far-end.
+ if ((instance_i = findFarEndInstance(slot->ports[slot_i].farEnds[j].port, slot->ports[slot_i].farEnds[j].farEndIndex, borderPorts[border_i])) < 0)
+ {
+ FATAL("slot %s port %s bound to far end %s not found on border proxy port id(%d)",
+ slot->name, slot->ports[slot_i].role()->name, slot->ports[slot_i].farEnds[j].port->slot->name, border_i);
+ }
+ else
+ {
+ // Send rtUnbound to slot port far-end - the local port (as seen from the far-end) remains the slot port instance.
+ sendBoundUnbound( slot->ports[slot_i].farEnds[j].port, slot->ports[slot_i].farEnds[j].farEndIndex, &slot->ports[slot_i], slot_i, true/*isBind*/);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/*static*/ void UMLRTFrameService::initializeCapsule ( UMLRTCapsule * capsule, size_t sizeSerializedData, void * serializedData )
+{
+ // Assumes rts lock not acquired. capsule->initialize() may send messages.
+
+ BDEBUG(BD_INSTANTIATE, "initialize capsule %s\n", capsule->getName());
+
+ UMLRTMessage initializeMsg;
+
+ initializeMsg.signal.initialize(sizeSerializedData);
+
+ // Put the data in the signal.
+ memcpy(initializeMsg.signal.getPayload(), serializedData, sizeSerializedData);
+
+ // Deallocate serialized data - it was allocated immediately prior to the call to this method and is deallcoated here.
+ if (serializedData != NULL)
+ {
+ free(serializedData);
+ }
+ // Register automatic SAPs and SPPs.
+ initializePorts(capsule);
+
+ // Send rtBound/rtUnbound for capsule ports - far-ends not handled here.
+ sendBoundUnboundForCapsule(capsule, true/*isBind*/);
+
+ // Initialize the capsule, sending the initial message.
+ capsule->initialize(initializeMsg);
+}
+
+/*static*/ void UMLRTFrameService::initializePorts ( UMLRTCapsule * capsule )
+{
+ // Assume RTS lock not acquired, but acquires it for this.
+ rtsLock();
+
+ const UMLRTCommsPort * * borderPorts = capsule->getBorderPorts();
+ if (borderPorts != NULL)
+ {
+ for (size_t i = 0; i < capsule->getClass()->numPortRolesBorder; ++i)
+ {
+ if (borderPorts[i])
+ {
+ // Create defer queue for appropriate ports.
+ if ((borderPorts[i]->deferQueue == NULL) && !borderPorts[i]->unbound && (borderPorts[i]->wired || borderPorts[i]->sap || borderPorts[i]->spp))
+ {
+ borderPorts[i]->deferQueue = new UMLRTMessageQueue();
+ }
+ // Handle automatic service port registration.
+ if (borderPorts[i]->sap && (borderPorts[i]->registeredName != NULL) && (borderPorts[i]->automatic))
+ {
+ UMLRTProtocol::registerSapPort(borderPorts[i], borderPorts[i]->registeredName);
+ }
+ else if (borderPorts[i]->spp && (borderPorts[i]->registeredName != NULL) && (borderPorts[i]->automatic))
+ {
+ UMLRTProtocol::registerSppPort(borderPorts[i], borderPorts[i]->registeredName);
+ }
+ }
+ }
+ }
+ const UMLRTCommsPort * internalPorts = capsule->getInternalPorts();
+ if (internalPorts != NULL)
+ {
+ for (size_t i = 0; i < capsule->getClass()->numPortRolesInternal; ++i)
+ {
+ // Create defer queue for appropriate ports.
+ if ((internalPorts[i].deferQueue == NULL) && !internalPorts[i].unbound && (internalPorts[i].wired || internalPorts[i].sap || internalPorts[i].spp))
+ {
+ internalPorts[i].deferQueue = new UMLRTMessageQueue();
+ }
+ if (internalPorts[i].sap && (internalPorts[i].registeredName != NULL) && (internalPorts[i].automatic))
+ {
+ UMLRTProtocol::registerSapPort(&internalPorts[i], internalPorts[i].registeredName);
+ }
+ else if (internalPorts[i].spp && (internalPorts[i].registeredName != NULL) && (internalPorts[i].automatic))
+ {
+ UMLRTProtocol::registerSppPort(&internalPorts[i], internalPorts[i].registeredName);
+ }
+ }
+ }
+ rtsUnlock();
+}
+
+/*static*/ void UMLRTFrameService::instantiate ( UMLRTSlot * slot, const UMLRTCapsuleClass * capsuleClass )
+{
+ // Utility method for generated code to instantiate a static sub-capsule whose border ports are the slot ports.
+
+ // Assumes RTS lock acquired.
+ capsuleClass->instantiate(&rtsifUmlrt, slot, createBorderPorts(slot, capsuleClass->numPortRolesBorder));
+}
+
+/*static*/ const UMLRTCommsPort * UMLRTFrameService::isBound ( const UMLRTCommsPort * port, int index, int * farEndIndexP )
+{
+ // Assumes local slot is occupied by a capsule instance.
+
+ const UMLRTCommsPort * farEndPort;
+
+ if (!port->unbound && ((farEndPort = port->farEnds[index].port) != NULL))
+ {
+ *farEndIndexP = port->farEnds[index].farEndIndex;
+
+ if (!farEndPort->slot->capsule)
+ {
+ // Far-end slot is not occupied.
+ farEndPort = NULL;
+ *farEndIndexP = 0;
+ }
+ }
+ return farEndPort;
+}
+
+// Request a deport.
+/*static*/ void UMLRTFrameService::requestControllerDeport ( UMLRTSlot * slot, bool lockAcquired )
+{
+ if (slot->role()->plugin && (slot->capsule != NULL))
+ {
+ if (slot->controller->isMyThread())
+ {
+ // We can perform the deport now because we're running in the context of the capsule's controller.
+ controllerDeport(slot, true/*synchronous*/, lockAcquired);
+ }
+ else
+ {
+ // We send a DEPORT Controller Command to have the capsule's controller execute the deport.
+ slot->controller->enqueueDeport(slot);
+ }
+ }
+}
+
+/*static*/ void UMLRTFrameService::requestControllerDestroy ( UMLRTSlot * slot, bool isTopSlot, bool lockAcquired )
+{
+ if (slot->controller->isMyThread())
+ {
+ // We can perform the destroy now because we're running in the context of the destroyed capsule's controller.
+ controllerDestroy(slot, isTopSlot, true/*synchronous*/, lockAcquired);
+ }
+ else
+ {
+ // We send a DESTROY Controller Command to have the capsule's controller execute the destroy.
+ slot->controller->enqueueDestroy(slot, isTopSlot);
+ }
+ if (!isTopSlot)
+ {
+ // This is NOT the top-capsule in the destruction.
+ // Go ahead and recurse into sub-structure performing destroy requests (i.e capsules along with the slots themselves).
+ for (size_t i = 0; i < slot->numParts; ++i)
+ {
+ for (size_t j = 0; j < slot->parts[i].numSlot; ++j)
+ {
+ requestControllerDestroy(slot->parts[i].slots[j], false/*isTopSlot*/, lockAcquired);
+ }
+ }
+ }
+}
+
+/*static*/ bool UMLRTFrameService::requestControllerImport ( UMLRTSlot * slot, UMLRTCapsule * capsule, bool lockAcquired )
+{
+ bool ok = true;
+
+ if (capsule->getSlot()->controller->isMyThread())
+ {
+ // The controller who owns the capsule being imported is running - the import can be performed directly now.
+ // The capsule instance being imported into the slot must run on the same controller it was originally incarnated on.
+
+ ok = controllerImport(slot, capsule, true/*synchronous*/, lockAcquired);
+ }
+ else
+ {
+ // The capsule's controller is not running - must send an IMPORT Controller Command. We've checked port binding once.
+ // We can reassign the slot to the capsule's controller and request an import to the controller.
+ slot->controller = capsule->getSlot()->controller;
+
+ slot->controller->enqueueImport(slot, capsule);
+ }
+ return ok;
+}
+
+/*static*/ void UMLRTFrameService::requestControllerIncarnate ( UMLRTSlot * slot, const void * userData, const UMLRTObject_class * type )
+{
+ // We've got the RTS lock - we must queue the initialize signals - even if the running controller is the capsule's controller.
+ if (slot->capsule != NULL)
+ {
+ // Must queue an INCARNATE Controller Command to send the initialize message.
+ slot->controller->enqueueIncarnate(slot->capsule, userData, type );
+ }
+ // Recurse into sub-structure, incarnating sub-slots where appropriate.
+ for (size_t i = 0; i < slot->numParts; ++i)
+ {
+ for (size_t j = 0; j < slot->parts[i].numSlot; ++j)
+ {
+ requestControllerIncarnate(slot->parts->slots[j], NULL, NULL);
+ }
+ }
+}
+
+/*static*/ void UMLRTFrameService::rtsLock ( )
+{
+ BDEBUG(BD_LOCK,"rtsLock attempt\n");
+
+ // Outputting the instance model may take a long time - wait a minute and then die.
+ if (!rtsGlobalLock.take(60000))
+ {
+ FATAL("Failed to acquire global RTS lock after waiting a full minute.");
+ }
+ BDEBUG(BD_LOCK,"rtsLock acquired\n");
+}
+
+/*static*/ void UMLRTFrameService::rtsUnlock ( )
+{
+ BDEBUG(BD_LOCK,"rtsUnlock\n");
+
+ rtsGlobalLock.give();
+}
+
+/*static*/ void UMLRTFrameService::sendBoundUnbound( const UMLRTCommsPort * port, int index, const UMLRTCommsPort * farEndPort, int farEndIndex, bool isBind )
+{
+ if ((!port->unbound) && (!port->relay) && (port->slot->capsule != NULL) && (port->notification) && (!port->slot->condemned))
+ {
+ UMLRTSignal signal;
+ signal.initialize( isBind ? UMLRTSignal::rtBound : UMLRTSignal::rtUnbound, farEndPort);
+ port->slot->controller->deliver(port, signal, farEndIndex);
+
+ BDEBUG(BD_BIND,"send %s to slot %s port %s[%d] from slot %s port %s[%d]\n",
+ isBind ? "rtBound" : "rtUnbound", port->slot->name, port->role()->name, index,
+ farEndPort->slot->name, farEndPort->role()->name, farEndIndex );
+ }
+}
+
+/*static*/ void UMLRTFrameService::sendBoundUnboundForCapsule ( UMLRTCapsule * capsule, bool isBind )
+{
+ const UMLRTCommsPort * * borderPorts = capsule->getBorderPorts();
+
+ // Only do wired ports. Service port notifications are handled during registration/de-registration.
+
+ for (size_t port_i = 0; (borderPorts != NULL) && (port_i < capsule->getClass()->numPortRolesBorder); ++port_i)
+ {
+ for (size_t index = 0; (index < borderPorts[port_i]->numFarEnd) && !borderPorts[port_i]->relay && borderPorts[port_i]->wired; ++index)
+ {
+ const UMLRTCommsPort * farEndPort;
+ int farEndIndex;
+ if ((farEndPort = isBound(borderPorts[port_i], index, &farEndIndex)) != NULL)
+ {
+ if (isBind)
+ {
+ // rtBound - is going to local port.
+ sendBoundUnbound(borderPorts[port_i], index, farEndPort, farEndIndex, isBind);
+ }
+ else
+ {
+ // rtUnbound - is going to far-end port.
+ sendBoundUnbound(farEndPort, farEndIndex, borderPorts[port_i], index, isBind);
+ }
+ }
+ }
+ }
+ const UMLRTCommsPort * internalPorts = capsule->getInternalPorts();
+ for (size_t port_i = 0; (internalPorts != NULL) && (port_i < capsule->getClass()->numPortRolesInternal); ++port_i)
+ {
+ for (size_t index = 0; (index < internalPorts[port_i].numFarEnd) && !internalPorts[port_i].relay && internalPorts[port_i].wired; ++index)
+ {
+ const UMLRTCommsPort * farEndPort;
+ int farEndIndex;
+ if ((farEndPort = isBound(&internalPorts[port_i], index, &farEndIndex)) != NULL)
+ {
+ if (isBind)
+ {
+ // rtBound - is going to local port.
+ sendBoundUnbound(&internalPorts[port_i], index, farEndPort, farEndIndex, isBind);
+ }
+ else
+ {
+ // rtUnbound - is going to far-end port.
+ sendBoundUnbound(farEndPort, farEndIndex, &internalPorts[port_i], index, isBind);
+ }
+ }
+ }
+ }
+}
+
+/*static*/ void UMLRTFrameService::unbindSubcapsulePort ( bool isBorder, UMLRTCapsule * subcapsule, int portIndex, int farEndIndex )
+{
+ if (subcapsule != NULL)
+ {
+ BDEBUG(BD_CONNECT, "unbind subcapsule %s portIndex %d fei %d\n", subcapsule->getName(), portIndex, farEndIndex);
+
+ subcapsule->unbindPort(isBorder, portIndex, farEndIndex);
+ }
+}
+
+/*static*/ bool UMLRTFrameService::unbindServicePort ( const UMLRTCommsPort * sapPort, const UMLRTCommsPort * sppPort )
+{
+ bool ok = false;
+
+ if (sapPort && sppPort)
+ {
+ if (sapPort->numFarEnd == 1)
+ {
+ if (sapPort->farEnds[0].port != NULL)
+ {
+ for (size_t i = 0; (i < sppPort->numFarEnd) && !ok; ++i)
+ {
+ if (sppPort->farEnds[i].port == sapPort)
+ {
+ sendBoundUnbound(sapPort, 0, sppPort, i, false/*isBind*/);
+ sendBoundUnbound(sppPort, i, sapPort, 0, false/*isBind*/);
+ disconnectPort(sapPort, 0);
+ disconnectPort(sppPort, i);
+ ok = true;
+ }
+ }
+ }
+ }
+ }
+ return ok;
+}
+
diff --git a/rts/umlrt/umlrthashmap.cc b/rts/umlrt/umlrthashmap.cc
new file mode 100644
index 0000000..e0189ea
--- /dev/null
+++ b/rts/umlrt/umlrthashmap.cc
@@ -0,0 +1,290 @@
+// umlrthashmap.hh
+
+/*******************************************************************************
+* Copyright (c) 2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrthashmap.hh"
+#include "umlrtguard.hh"
+#include "basefatal.hh"
+#include <stdlib.h>
+#include <string.h>
+
+UMLRTHashMap::~UMLRTHashMap()
+{
+ mutex.take();
+
+ for (int i = 0; i < mapSize; ++i)
+ {
+ if (map[i].object != NULL)
+ {
+ free( (void *)map[i].object );
+ }
+ }
+ if (mapSize)
+ {
+ free( map );
+ }
+}
+
+/*static*/ int UMLRTHashMap::compareString( const void * k1, const void * k2 )
+{
+ if ((k1 == NULL) && (k2 != NULL))
+ {
+ return -1;
+ }
+ else if ((k1 != NULL) && (k2 == NULL))
+ {
+ return 1;
+ }
+ else if ((k1 == NULL) && (k2 == NULL))
+ {
+ return 0;
+ }
+ return strcmp((const char *)k1, (const char *)k2);
+}
+
+/*static*/ int UMLRTHashMap::compareValue( const void * k1, const void * k2 )
+{
+ return (char *)k1 - (char *)k2;
+}
+
+UMLRTHashMap::MapEntry * UMLRTHashMap::getEntry( const void * key ) const
+{
+ // Assumes mutex is taken.
+ MapEntry * entry = NULL;
+
+ int location = locate(key);
+
+ if ((location >= 0) && (location < mapSize))
+ {
+ if (compare(map[location].key, key) == 0)
+ {
+ entry = &map[location];
+ }
+ }
+ return entry;
+}
+
+void * UMLRTHashMap::getFirstObject() const
+{
+ // Return object associated with key. Return NULL if entry not found.
+ UMLRTGuard g(mutex);
+
+ void * object = NULL;
+ if (mapSize > 0)
+ {
+ object = map[0].object;
+ }
+ return object;
+}
+
+void * UMLRTHashMap::getObject( const void * key ) const
+{
+ // Return object associated with key. Return NULL if entry not found.
+ UMLRTGuard g(mutex);
+
+ void * object = NULL;
+ MapEntry * entry = getEntry(key);
+
+ if (entry != NULL)
+ {
+ object = entry->object;
+ }
+ return object;
+}
+
+const void * UMLRTHashMap::getKey( int location ) const
+{
+ // Return key associated with entry at location.
+ UMLRTGuard g(mutex);
+
+ const void * key = NULL;
+
+ if ((location >= 0) && (location < mapSize))
+ {
+ key = map[location].key;
+ }
+ return key;
+}
+
+void * UMLRTHashMap::getObject( int location ) const
+{
+ // Return key associated with entry at location.
+ UMLRTGuard g(mutex);
+
+ void * object = NULL;
+
+ if ((location >= 0) && (location < mapSize))
+ {
+ object = map[location].object;
+ }
+ return object;
+}
+
+void UMLRTHashMap::insert( const void * key, void * object )
+{
+ UMLRTGuard g(mutex);
+
+ MapEntry * entry = getEntry(key);
+ if (entry)
+ {
+ entry->object = object;
+ }
+ else
+ {
+ // Entry not find. Insert it.
+ int location = locate(key);
+
+ if (mapSize == 0)
+ {
+ map = (MapEntry *)malloc(sizeof(MapEntry));
+ }
+ else
+ {
+ map = (MapEntry *)realloc(map, (mapSize + 1) * sizeof(MapEntry));
+ memmove(&map[location + 1], &map[location], (mapSize - location) * sizeof(MapEntry));
+ }
+ ++mapSize;
+ map[location].key = key;
+ map[location].object = object;
+ }
+}
+
+int UMLRTHashMap::locate( const void * key ) const
+{
+ // Assumes mutex is taken.
+ // Returns either location of entry holding 'key' or the location where 'key' belongs (first entry where entry->key > input key).
+ int min, max, mid;
+ int location = -1;
+
+ min = 0;
+ max = mapSize - 1;
+
+ while (location == -1)
+ {
+ if (min > max)
+ {
+ // Zeroed in on location. It's 'min' or the one after.
+ location = min;
+ if (min < (mapSize-1))
+ {
+ if (compare(map[min].key, key) < 0)
+ {
+ // 'min' was one before location we want.
+ location = min + 1;
+ }
+ }
+ }
+ else
+ {
+ mid = (min + max) / 2;
+ if (compare(map[mid].key, key) == 0)
+ {
+ // located it
+ location = mid;
+ }
+ else if (compare(map[mid].key, key) < 0)
+ {
+ // location must be after 'mid'.
+ if ((min = (mid + 1)) >= mapSize)
+ {
+ // location falls after last item
+ location = mapSize;
+ }
+ }
+ else if ((max = (mid - 1)) < 0)
+ {
+ // location falls before first item
+ location = 0;
+ }
+ }
+ }
+ return location;
+}
+
+const void * UMLRTHashMap::remove( const void * key )
+{
+ UMLRTGuard g(mutex);
+
+ int location = locate(key);
+ const void * keyRemoved = NULL;
+
+ if (mapSize)
+ {
+ if (location < mapSize)
+ {
+ if (compare(map[location].key, key) == 0)
+ {
+ keyRemoved = map[location].key;
+ memmove(&map[location], &map[location + 1], (mapSize - location) * sizeof(MapEntry));
+ if ((--mapSize) == 0)
+ {
+ free(map);
+ map = NULL;
+ }
+ }
+ }
+ }
+ return keyRemoved;
+}
+
+UMLRTHashMap::Iterator UMLRTHashMap::getIterator() const
+{
+ if (!mapSize)
+ {
+ return Iterator(this, -1);
+ }
+ return Iterator(this, 0);
+}
+
+UMLRTHashMap::Iterator UMLRTHashMap::Iterator::end() const
+{
+ return Iterator(map, -1);
+}
+
+UMLRTHashMap::Iterator UMLRTHashMap::Iterator::next() const
+{
+ if (index == -1)
+ {
+ return end();
+ }
+ else if (map->isEmpty())
+ {
+ return end();
+ }
+ else if (index == (map->getSize() - 1))
+ {
+ return end();
+ }
+ return Iterator(map, index + 1);
+}
+
+const void * UMLRTHashMap::Iterator::getKey() const
+{
+ const void * key = NULL;
+
+ if (index != -1)
+ {
+ key = map->getKey(index);
+ }
+ return key;
+}
+
+void * UMLRTHashMap::Iterator::getObject() const
+{
+ void * object = NULL;
+
+ if (index != -1)
+ {
+ if (map->getSize() > 0)
+ {
+ object = map->getObject(index);
+ }
+ }
+ return object;
+}
diff --git a/rts/umlrt/umlrtinsignal.cc b/rts/umlrt/umlrtinsignal.cc
new file mode 100644
index 0000000..5142eb3
--- /dev/null
+++ b/rts/umlrt/umlrtinsignal.cc
@@ -0,0 +1,139 @@
+// umlrtinsignal.cc
+
+/*******************************************************************************
+* Copyright (c) 2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtinsignal.hh"
+#include "basefatal.hh"
+
+UMLRTInSignal::UMLRTInSignal()
+{
+
+}
+
+UMLRTInSignal::UMLRTInSignal(const UMLRTInSignal &signal)
+{
+
+}
+
+UMLRTInSignal& UMLRTInSignal::operator=(const UMLRTInSignal &signal)
+{
+ UMLRTSignal::operator = (signal);
+ return *this;
+}
+
+UMLRTInSignal::~UMLRTInSignal()
+{
+
+}
+
+// See documentation in UMLRTSignal.
+void UMLRTInSignal::decode( void * * decodeInfo, void * data, const UMLRTObject_class * desc, int arraySize ) const
+{
+ if (!element)
+ {
+ FATAL("decode attempted on invalid signal.");
+ }
+ element->decode(decodeInfo, desc, data, arraySize);
+}
+
+// See documentation in UMLRTSignal.
+void UMLRTInSignal::decode( void * * decodeInfo, int ptrIndirection, void * data, const UMLRTObject_class * desc, int arraySize ) const
+{
+ FATAL("encode ptr indirection not supported");
+}
+
+int UMLRTInSignal::purge()
+{
+ int count = 0;
+ if (element == NULL)
+ {
+ FATAL("purge element is NULL");
+ }
+ const UMLRTCommsPort * srcPort = element->getSrcPort();
+ if (srcPort != NULL)
+ {
+ count = srcPort->purge(-1/*index*/, element->getId());
+ }
+ return count;
+}
+
+int UMLRTInSignal::purgeAt( int index )
+{
+ int count = 0;
+ if (element == NULL)
+ {
+ FATAL("purge element is NULL");
+ }
+ const UMLRTCommsPort * srcPort = element->getSrcPort();
+ if ((srcPort != NULL) && (index >= 0) && (index < (int)srcPort->numFarEnd))
+ {
+ count = srcPort->purge(index, element->getId());
+ }
+ return count;
+}
+
+int UMLRTInSignal::recall( bool front )
+{
+ int count = 0;
+ if (element == NULL)
+ {
+ FATAL("purge element is NULL");
+ }
+ const UMLRTCommsPort * srcPort = element->getSrcPort();
+ if (srcPort != NULL)
+ {
+ count = srcPort->recall(-1/*index*/, front, true/*one*/, element->getId());
+ }
+ return count;
+}
+
+int UMLRTInSignal::recallAll( bool front )
+{
+ int count = 0;
+ if (element == NULL)
+ {
+ FATAL("purge element is NULL");
+ }
+ const UMLRTCommsPort * srcPort = element->getSrcPort();
+ if (srcPort != NULL)
+ {
+ count = srcPort->recall(-1/*index*/, front, false/*one*/, element->getId());
+ }
+ return count;
+}
+
+int UMLRTInSignal::recallAllAt( int index, bool front )
+{
+ int count = 0;
+ if (element == NULL)
+ {
+ FATAL("purge element is NULL");
+ }
+ const UMLRTCommsPort * srcPort = element->getSrcPort();
+ if ((srcPort != NULL) && (index >= 0) && (index < (int)srcPort->numFarEnd))
+ {
+ count = srcPort->recall(index, front, false/*one*/, element->getId());
+ }
+ return count;
+}
+
+int UMLRTInSignal::recallAt( int index, bool front )
+{
+ int count = 0;
+ if (element == NULL)
+ {
+ FATAL("purge element is NULL");
+ }
+ const UMLRTCommsPort * srcPort = element->getSrcPort();
+ if ((srcPort != NULL) && (index >= 0) && (index < (int)srcPort->numFarEnd))
+ {
+ count = srcPort->recall(index, front, true/*one*/, element->getId());
+ }
+ return count;
+}
diff --git a/rts/umlrt/umlrtlogprotocol.cc b/rts/umlrt/umlrtlogprotocol.cc
new file mode 100644
index 0000000..dc9c738
--- /dev/null
+++ b/rts/umlrt/umlrtlogprotocol.cc
@@ -0,0 +1,541 @@
+// umlrtlogprotocol.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtobjectclass.hh"
+#include "umlrtlogprotocol.hh"
+#include "umlrtmutex.hh"
+
+// Avoid concurrent output stdout and stderr. The application is responsible for mutual exclusion all other streams.
+/*static*/ UMLRTMutex UMLRTLogProtocol_baserole::stdoutMutex;
+/*static*/ UMLRTMutex UMLRTLogProtocol_baserole::stderrMutex;
+
+// Gain exclusive access to stdout or stderr - return NULL if stream is not one of these two.
+UMLRTMutex * UMLRTLogProtocol_baserole::takeMutex() const
+{
+ UMLRTMutex * mutex = NULL;
+ if (ostream)
+ {
+ if (ostream == stdout)
+ {
+ UMLRTLogProtocol_baserole::stdoutMutex.take();
+ mutex = &UMLRTLogProtocol_baserole::stdoutMutex;
+ }
+ else if (ostream == stderr)
+ {
+ UMLRTLogProtocol_baserole::stderrMutex.take();
+ mutex = &UMLRTLogProtocol_baserole::stderrMutex;
+ }
+ }
+ return mutex;
+}
+
+void UMLRTLogProtocol_baserole::giveMutex ( UMLRTMutex * mutex ) const
+{
+ if (mutex != NULL)
+ {
+ mutex->give();
+ }
+}
+
+// Output data with the new-line appended.
+int UMLRTLogProtocol_baserole::log ( const char * fmt, ... ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ va_list ap;
+ va_start(ap, fmt);
+ UMLRTMutex * mutex = takeMutex();
+ nchar = vfprintf(ostream, fmt, ap);
+ nchar += fprintf(ostream, "\n");
+ giveMutex(mutex);
+ va_end(ap);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( char c ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%c\n", c);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( short s ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%d\n", s);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( int i ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%d\n", i);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( long l ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%ld\n", l);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( long long ll ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%lld\n", ll);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( unsigned char uc ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%c\n", uc);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( unsigned short us ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%u\n", us);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( unsigned int ui ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%u\n", ui);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( unsigned long ul ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%lu\n", ul);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( unsigned long long ull ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%llu\n", ull);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( float f ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%f\n", f);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( double d ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%f\n", d);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( const void * userData, const UMLRTObject_class * type ) const
+{
+ int nchar;
+
+ nchar = show(userData, type);
+ nchar += log("");
+
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::log ( const UMLRTTypedValue & value ) const
+{
+ return log( value.data, value.type );
+}
+
+// Output data with no new-line appended.
+int UMLRTLogProtocol_baserole::show ( const char * fmt, ... ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ va_list ap;
+ va_start(ap, fmt);
+ UMLRTMutex * mutex = takeMutex();
+ nchar = vfprintf(ostream, fmt, ap);
+ giveMutex(mutex);
+ va_end(ap);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( char c ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%c", c);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( short s ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%d", s);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( int i ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%d", i);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( long l ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%ld", l);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( long long ll ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%lld", ll);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( unsigned char uc ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%c", uc);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( unsigned short us ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%u", us);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( unsigned int ui ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%u", ui);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( unsigned long ul ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%lu", ul);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( unsigned long long ull ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%llu", ull);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( float f ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%f", f);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( double d ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "%f", d);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( const void * userData, const UMLRTObject_class * type ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = UMLRTObject_fprintf(ostream, type, userData);
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::show ( const UMLRTTypedValue & value ) const
+{
+ return show( value.data, value.type );
+}
+
+int UMLRTLogProtocol_baserole::cr ( int numCr ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ while (numCr > 0)
+ {
+ nchar += fprintf(ostream, "\n");
+ --numCr;
+ }
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::crtab ( int numTab ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ nchar = fprintf(ostream, "\n");
+ while (numTab > 0)
+ {
+ nchar += fprintf(ostream, "\t");
+ --numTab;
+ }
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::space ( int numSpace ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ while (numSpace > 0)
+ {
+ nchar = fprintf(ostream, " ");
+ --numSpace;
+ }
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::tab ( int numTab ) const
+{
+ int nchar = 0;
+
+ if (ostream != NULL)
+ {
+ UMLRTMutex * mutex = takeMutex();
+ while (numTab > 0)
+ {
+ nchar += fprintf(ostream, "\t");
+ --numTab;
+ }
+ giveMutex(mutex);
+ }
+ return nchar;
+}
+
+int UMLRTLogProtocol_baserole::commit ( ) const
+{
+ if (ostream != NULL)
+ {
+ fflush(ostream);
+ }
+ return 0;
+}
+
+// Redirect logging output - closes previous output stream if it was opened via redirect(fname). Returns false if error.
+bool UMLRTLogProtocol_baserole::redirect ( FILE * ostream_ ) const
+{
+ if (isOpenFile)
+ {
+ fclose(ostream);
+ isOpenFile = false;
+ }
+ ostream = ostream_;
+ return true;
+}
+
+// Redirect to a file by name - removes previous output stream(s). Returns false if error.
+bool UMLRTLogProtocol_baserole::redirect ( const char * fname ) const
+{
+ bool ok = false;
+ if (isOpenFile)
+ {
+ fclose(ostream);
+ isOpenFile = false;
+ }
+ if (fname)
+ {
+ if ((ostream = fopen(fname, "w")) == NULL)
+ {
+ // Redirect to stderr in case file open fails.
+ ostream = stderr;
+ }
+ else
+ {
+ isOpenFile = true;
+ ok = true;
+ }
+ }
+ return ok;
+}
+
diff --git a/rts/umlrt/umlrtmain.cc b/rts/umlrt/umlrtmain.cc
new file mode 100644
index 0000000..998902a
--- /dev/null
+++ b/rts/umlrt/umlrtmain.cc
@@ -0,0 +1,335 @@
+// umlrtmain.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdio.h>
+#include "umlrtmain.hh"
+#include "basefatal.hh"
+#include "basedebug.hh"
+#include "umlrtcapsuletocontrollermap.hh"
+#include "umlrtcontroller.hh"
+
+// See umlrtmain.hh for documentation.
+
+// This set of argc, argv only contain user-arguments and do not contain arguments
+// intended for the RTS-library.
+int UMLRTMain::argc = 0;
+const char * * UMLRTMain::argv = NULL;
+bool UMLRTMain::argsDefined = false;
+
+typedef struct {
+
+ const char * const longopt;
+ const char shortopt;
+ const char * const value;
+ const char * const help;
+
+} t_usage_option_help;
+
+// Help text for usage output
+static t_usage_option_help optionhelp[] = {
+
+ { "General RTS options", 0, "", "" },
+ { "help", 'h', "", "Print usage and exit." },
+ { "userargs", 'u', "", "Marks the start of application options." },
+ { "controllers", 'c', "<controllers-file>", "Specify a capsule-to-controller map file." },
+ { "Debug feature enable options", 0, "", "" },
+ { "debug", 'D', "0/1", "Overall debug log enable." },
+ { "debugcolor", 'C', "0/1", "Disable/enable terminal text color escape sequences." },
+ { "debugsummary", 's', "", "Output debug summary." },
+ { "debugtypeon", 'T', "<types>", "Enable debug types. See <types> below." },
+ { "debugtypeoff", 't', "<types>", "Disable debug types. See <types> below." },
+ { "debugmodel", 'M', "", "Output instance UML-RT model after startup. (Usually also requires '-T model')" },
+ { "Enabling invidual log message components", 0, "", "" },
+ { "debugtime", 'S', "0/1", "Disable/enable log time-stamp." },
+ { "debugname", 'N', "0/1", "Disable/enable log type name." },
+ { "debugfile", 'F', "0/1", "Disable/enable log file-name." },
+ { "debugline", 'L', "0/1", "Disable/enable log file line #." },
+ { "debugthread", 'n', "0/1", "Disable/enable log thread name (or id)." },
+ { "debugmethod", 'r', "0/1", "Disable/enable log method name." },
+ { "debugmsg", 'm', "0/1", "Disable/enable log application message." },
+ { NULL, 0, NULL, NULL } // MUST BE LAST to terminate loop
+};
+
+// Output usage information and exit
+/*static*/ void UMLRTMain::usage( const char * const program )
+{
+ // Compute field widths for column-aligned output
+ int maxlongoptlen = 0;
+ int maxvaluelen = 0;
+ for (int i = 0; optionhelp[i].longopt; ++i)
+ {
+ int len;
+ if (optionhelp[i].shortopt == 0)
+ {
+ // Is a title for a group of options. Length doesn't apply.
+ }
+ else
+ {
+ if ((len = strlen(optionhelp[i].longopt)) > maxlongoptlen)
+ {
+ maxlongoptlen = len;
+ }
+ if ((len = strlen(optionhelp[i].value)) > maxvaluelen)
+ {
+ maxvaluelen = len;
+ }
+ }
+ }
+ // Terminal colour - must be reset at end.
+ base::debugColourBrightRed();
+
+ // Summary usage line
+ printf("usage:\n");
+ printf(" %s ", program);
+ for (int i = 0; optionhelp[i].longopt; ++i)
+ {
+ if (optionhelp[i].shortopt == 0)
+ {
+ // Is not an option, but contains a title for a group of options.
+ }
+ else if (optionhelp[i].value[0] == '\0')
+ {
+ printf("[-%c] ", optionhelp[i].shortopt);
+ }
+ else
+ {
+ printf("[-%c%s] ", optionhelp[i].shortopt, optionhelp[i].value);
+ }
+ }
+ printf("\n");
+
+ // Option summary
+ for (int i = 0; optionhelp[i].longopt; ++i)
+ {
+ int sofar;
+ if (optionhelp[i].shortopt == 0)
+ {
+ printf("\n%s:\n", optionhelp[i].longopt);
+ }
+ else
+ {
+ if (optionhelp[i].value[0] == '\0')
+ {
+ sofar = printf("[-%c]", optionhelp[i].shortopt);
+ }
+ else
+ {
+ sofar = printf("[-%c %s]", optionhelp[i].shortopt, optionhelp[i].value);
+ }
+ for (int j = sofar; j < (maxvaluelen + 6); ++j)
+ {
+ printf(" ");
+ }
+ if (optionhelp[i].value[0] == '\0')
+ {
+ sofar = printf(" or [--%s]", optionhelp[i].longopt);
+ }
+ else
+ {
+ sofar = printf(" or [--%s=%s]", optionhelp[i].longopt, optionhelp[i].value);
+ }
+ for (int j = sofar; j < (maxlongoptlen + maxvaluelen + 10); ++j)
+ {
+ printf(" ");
+ }
+ printf("%s\n", optionhelp[i].help);
+ }
+ }
+ printf("\n<types> is a string containing one or more debug-types.\n"
+ "Current types are ");
+ base::debugTypeSummary();
+ printf("\n");
+ base::debugColourReset();
+
+ exit(EXIT_FAILURE);
+}
+
+// Extract user arguments and define argument list and count for user.
+// Currently doesn't do anything with arguments specified before '--userargs'.
+/*static*/ void UMLRTMain::setArgs( int argc_, char * const * argv_ )
+{
+ static struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "controllers", required_argument, NULL, 'c' },
+ { "debug", required_argument, NULL, 'D' },
+ { "debugcolor", required_argument, NULL, 'C' },
+ { "debugtypeon", required_argument, NULL, 'T' },
+ { "debugtypeoff", required_argument, NULL, 't' },
+ { "debugname", required_argument, NULL, 'N' },
+ { "debugtime", required_argument, NULL, 'S' },
+ { "debugthread", required_argument, NULL, 'n' },
+ { "debugfile", required_argument, NULL, 'F' },
+ { "debugline", required_argument, NULL, 'L' },
+ { "debugmethod", required_argument, NULL, 'f' },
+ { "debugmsg", required_argument, NULL, 'm' },
+ { "debugsummary", no_argument, NULL, 's' },
+ { "debugmodel", no_argument, NULL, 'M' },
+ { "userargs", no_argument, NULL, 'u' },
+ };
+
+ int userargstart = argc_; // No user arguments unless '--userargs' is found.
+ bool userargsfound = false;
+ int opti = 1; // getopt_long skips program name.
+ int optchar;
+
+ bool debugsummary = false;
+ bool displayusage = false;
+ bool displaymodel = false;
+ char * controllerfile = NULL;
+ int longindex = 0;
+
+ while (((optchar = getopt_long( argc_, argv_, "hc:D:C:T:t:N:S:n:F:L:f:m:Msu", options, &longindex)) != -1) && !userargsfound)
+ {
+ switch(optchar)
+ {
+ case 'h':
+ displayusage = true;
+ break;
+ case 'c':
+ controllerfile = optarg;
+ break;
+ case 'D':
+ base::debugEnableSet(atoi(optarg) != 0);
+ break;
+ case 'C':
+ base::debugEnableColorSet(atoi(optarg) != 0);
+ break;
+ case 'T':
+ if (!base::debugEnableTypeMaskSpecSet(optarg, true /*will enable*/))
+ {
+ usage(argv_[0]);
+ }
+ break;
+ case 't':
+ if (!base::debugEnableTypeMaskSpecSet(optarg, false /*will disable*/))
+ {
+ usage(argv_[0]);
+ }
+ break;
+ case 'N':
+ base::debugEnableTypeNameDisplaySet(atoi(optarg) != 0);
+ break;
+ case 'S':
+ base::debugEnableTimeDisplaySet(atoi(optarg) != 0);
+ break;
+ case 'n':
+ base::debugEnableThreadDisplaySet(atoi(optarg) != 0);
+ break;
+ case 'F':
+ base::debugEnableFilenameDisplaySet(atoi(optarg) != 0);
+ break;
+ case 'L':
+ base::debugEnableLineNumberDisplaySet(atoi(optarg) != 0);
+ break;
+ case 'f':
+ base::debugEnableMethodDisplaySet(atoi(optarg) != 0);
+ break;
+ case 'm':
+ base::debugEnableUserMsgDisplaySet(atoi(optarg) != 0);
+ break;
+ case 'M':
+ displaymodel = true;
+ break;
+ case 's':
+ debugsummary = true;
+ break;
+ case 'u':
+ userargsfound = true;
+ userargstart = opti + 1;
+ break;
+ default:
+ usage(argv_[0]);
+ }
+ opti++;
+ }
+ if (!userargsfound && (optind < argc_))
+ {
+ printf("ERROR: Command-line argument '%s' not recognized.\n", argv_[optind]);
+ usage(argv_[0]);
+ }
+ if (controllerfile)
+ {
+ // If user is reassigning the capsule controllers with a file...
+ if (!UMLRTCapsuleToControllerMap::readCapsuleControllerMap(controllerfile))
+ {
+ printf("ERROR: failed to create capsule-to-controller map. Exiting.\n");
+ usage(argv_[0]);
+ }
+ UMLRTCapsuleToControllerMap::debugOutputCaspuleToControllerMap();
+ }
+
+ // If user requested debug options summary...
+ if (debugsummary)
+ {
+ base::debugOptionSummary();
+ }
+ // If the user requested help, print usage (exit)
+ if (displayusage)
+ {
+ usage(argv_[0]);
+ }
+ if (displaymodel)
+ {
+ if ((base::debugGetEnabledTypes() & (1 << BD_MODEL)) == 0)
+ {
+ printf("WARNING: specified -M (--debugmodel) and '-T model' is not enabled.\n");
+ }
+ else
+ {
+ UMLRTController * controller = (UMLRTController *)UMLRTCapsuleToControllerMap::getFirstController();
+ if (controller == NULL)
+ {
+ printf("WARNING: no controllers instantiated?\n");
+ }
+ else
+ {
+ // Controllers are not actually running yet - use the synchronous output to capture the model before any run-time activity.
+ controller->debugOutputModel();
+ }
+ }
+ }
+ argc = argc_ - userargstart;
+ if (userargstart < argc_)
+ {
+ argv = new const char *[argc];
+ }
+ for (int i = 0, j = userargstart; userargsfound && (j < argc_); ++j)
+ {
+ argv[i++] = argv_[j];
+ }
+ argsDefined = true; // Sanity checking on user fetching of arguments.
+}
+
+// Get the number of user command-line arguments.
+/*static*/ int UMLRTMain::getArgCount()
+{
+ if (!argsDefined)
+ {
+ FATAL("getArgCount() called before setArgs()");
+ }
+ return argc;
+}
+
+// Get the i-th command-line argument.
+/*static*/ const char * UMLRTMain::getArg( int index )
+{
+ if (!argsDefined)
+ {
+ FATAL("getArg(%d) called before setArgs()", index);
+ }
+ if ((index < 0) || (index > (argc-1)))
+ {
+ FATAL("getArg(%d) called but max index is %d", index, (argc-1));
+ }
+ return argv[index];
+}
diff --git a/rts/umlrt/umlrtmainloop.cc b/rts/umlrt/umlrtmainloop.cc
new file mode 100644
index 0000000..2865b71
--- /dev/null
+++ b/rts/umlrt/umlrtmainloop.cc
@@ -0,0 +1,22 @@
+// umlrtmainloop.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtmain.hh"
+
+// See umlrtmain.hh for documentation.
+
+// mainLoop - a method that can be overridden by the user to implement a main thread.
+//
+// return 'false' to skip waiting for controllers to run-to-completion and call targetShutdown() immediately.
+// return 'true' to cause application to wait for controllers to complete before calling targetShutdown().
+/*static*/ bool UMLRTMain::mainLoop()
+{
+ return true; // By default, we wait for controllers to run-to-completion.
+}
diff --git a/rts/umlrt/umlrtmaintargetshutdown.cc b/rts/umlrt/umlrtmaintargetshutdown.cc
new file mode 100644
index 0000000..f5ecf36
--- /dev/null
+++ b/rts/umlrt/umlrtmaintargetshutdown.cc
@@ -0,0 +1,24 @@
+// umlrtmaintargetshutdown.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include <stdlib.h>
+#include "umlrtmain.hh"
+
+// See umlrtmain.hh for documentation.
+
+// targetShutdown - a method that can be overridden by the user to shutdown the execution platform.
+//
+// The input parameter is the return value of the mainLoop().
+//
+// Returns the 'exit status' of the application. This default version does nothing and returns EXIT_SUCCESS.
+/*static*/ int UMLRTMain::targetShutdown( bool mainLoopReturnValue )
+{
+ return EXIT_SUCCESS; // By default, we return 'ok' and application will spawn the controllers.
+}
diff --git a/rts/umlrt/umlrtmaintargetstartup.cc b/rts/umlrt/umlrtmaintargetstartup.cc
new file mode 100644
index 0000000..33b2141
--- /dev/null
+++ b/rts/umlrt/umlrtmaintargetstartup.cc
@@ -0,0 +1,22 @@
+// umlrtmaintargetstartup.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtmain.hh"
+
+// See umlrtmain.hh for documentation.
+
+// targetStartup - a method that can be overridden by the user to initialize prior to controllers starting.
+//
+// return 'false' to exit the application with a failure indication.
+// return 'true' to cause application to start controllers.
+/*static*/ bool UMLRTMain::targetStartup()
+{
+ return true; // By default, we return 'ok' and application will spawn the controllers.
+}
diff --git a/rts/umlrt/umlrtmessage.cc b/rts/umlrt/umlrtmessage.cc
new file mode 100644
index 0000000..3590940
--- /dev/null
+++ b/rts/umlrt/umlrtmessage.cc
@@ -0,0 +1,42 @@
+// umlrtmessage.cc
+
+/*******************************************************************************
+* Copyright (c) 2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtapi.hh"
+#include "umlrtcontroller.hh"
+#include "umlrtmessage.hh"
+#include "umlrtqueue.hh"
+
+// See umlrtmessagequeue.hh for documentation.
+
+bool UMLRTMessage::defer() const
+{
+ bool ok = false;
+
+ UMLRTMessage * msg = umlrt::MessageGetFromPool();
+ if (msg == NULL)
+ {
+ destPort->slot->controller->setError(UMLRTController::E_DEFER_ALLOC);
+ }
+ else
+ {
+ msg->srcPortIndex = srcPortIndex;
+ msg->sapIndex0 = sapIndex0;
+ msg->destPort = destPort;
+ msg->destSlot = destSlot;
+ msg->signal = signal; // This copy causes the reference count on signal to increment.
+ msg->isCommand = isCommand;
+
+ // Append to defer queue.
+ destPort->deferQueue->enqueue(msg);
+ ok = true;
+ }
+ return ok;
+}
+
diff --git a/rts/umlrt/umlrtmessagepool.cc b/rts/umlrt/umlrtmessagepool.cc
new file mode 100644
index 0000000..5a1b07f
--- /dev/null
+++ b/rts/umlrt/umlrtmessagepool.cc
@@ -0,0 +1,49 @@
+// umlrtmessagepool.cc
+
+/*******************************************************************************
+ * Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+// See umlrtmessagepool.hh for documentation.
+#include <stdlib.h>
+#include "basefatal.hh"
+#include "umlrtmessagepool.hh"
+#include "umlrtguard.hh"
+
+UMLRTMessagePool::UMLRTMessagePool(UMLRTMessage messages[], size_t arraySize, size_t incrementSize) : UMLRTPool(incrementSize)
+{
+ UMLRTGuard g(mutex);
+
+ if (arraySize && messages != NULL)
+ {
+ for (size_t i = arraySize - 1; i > 0; --i)
+ {
+ messages[i - 1].next = &messages[i];
+ messages[i - 1].qid = qid++;
+ }
+ head = &messages[0];
+ }
+}
+
+UMLRTMessagePool::UMLRTMessagePool(size_t incrementSize) : UMLRTPool(incrementSize)
+{
+
+}
+
+void UMLRTMessagePool::grow()
+{
+ UMLRTMessage * newElements = new UMLRTMessage[increment]();
+
+ for (size_t i = increment - 1; i > 0; --i)
+ {
+ newElements[i - 1].next = &newElements[i];
+ newElements[i - 1].qid = qid++;
+ }
+ newElements[increment - 1].next = head;
+ head = &newElements[0];
+}
+
diff --git a/rts/umlrt/umlrtmessagequeue.cc b/rts/umlrt/umlrtmessagequeue.cc
new file mode 100644
index 0000000..a6feda0
--- /dev/null
+++ b/rts/umlrt/umlrtmessagequeue.cc
@@ -0,0 +1,16 @@
+// umlrtmessagequeue.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtmessagequeue.hh"
+
+// See umlrtmessagequeue.hh for documentation.
+
+UMLRTMessageQueue::UMLRTMessageQueue() { }
+
diff --git a/rts/umlrt/umlrtmutex.cc b/rts/umlrt/umlrtmutex.cc
new file mode 100644
index 0000000..5a7ccd0
--- /dev/null
+++ b/rts/umlrt/umlrtmutex.cc
@@ -0,0 +1,79 @@
+// umlrtmutex.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+
+#include <errno.h>
+#include <time.h>
+#include "basefatal.hh"
+#include "umlrtmutex.hh"
+#include "umlrttimespec.hh"
+
+// UMLRTMutex is platform-independent mutual-exclusion.
+
+// Bug 32 tracks the move of this implementation to +/os/linux/osmutex.cc
+
+UMLRTMutex::UMLRTMutex()
+{
+ pthread_mutex_init(&mutex, NULL);
+}
+
+// Can create a mutex which starts life as taken already.
+UMLRTMutex::UMLRTMutex( bool taken )
+{
+ if (taken)
+ {
+ take();
+ }
+}
+
+UMLRTMutex::~UMLRTMutex()
+{
+ pthread_mutex_destroy(&mutex);
+}
+// Wait forever for mutex.
+void UMLRTMutex::take()
+{
+ if (pthread_mutex_lock(&mutex) < 0)
+ {
+ FATAL_ERRNO("pthread_mutex_lock");
+ }
+}
+
+// Timed - returns non-zero for success, zero for timeout.
+int UMLRTMutex::take( uint32_t msec )
+{
+ struct timespec timeout;
+ int success = !0;
+
+ UMLRTTimespec::timespecAbsAddMsec( &timeout, msec );
+ if (pthread_mutex_timedlock(&mutex, &timeout) < 0)
+ {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+ int errno_ = errno;
+#pragma GCC diagnostic pop
+ if (errno_ != ETIMEDOUT)
+ {
+ FATAL_ERRNO("pthread_mutex_timedlock");
+ }
+ success = 0;
+ }
+ return success;
+}
+
+// Give mutex back.
+void UMLRTMutex::give()
+{
+ if (pthread_mutex_unlock(&mutex) < 0)
+ {
+ FATAL_ERRNO("pthread_mutex_unlock");
+ }
+}
+
diff --git a/rts/umlrt/umlrtobjectclass.cc b/rts/umlrt/umlrtobjectclass.cc
new file mode 100644
index 0000000..7bb3294
--- /dev/null
+++ b/rts/umlrt/umlrtobjectclass.cc
@@ -0,0 +1,519 @@
+// umlrtobjectclass.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtobjectclass.hh"
+#include "basedebugtype.hh"
+#include "basedebug.hh"
+#include <stdlib.h>
+
+// Type descriptor used for encoding/decoding data of a given type.
+// A number of UMLRTType_xxx descriptors are pre-defined by the library.
+
+// Default initializer - zeros the data and returns a pointer to next byte.
+
+void * UMLRTObject_initialize( const UMLRTObject_class * desc, void * data )
+{
+ uint8_t * s = (uint8_t *)data;
+
+ if (desc->fields == NULL)
+ {
+ memset(s, 0, desc->size);
+ s += desc->size;
+ }
+ else
+ {
+ for (size_t i = 0; i < desc->size; ++i)
+ {
+ desc->fields[i].desc->initialize(desc->fields[i].desc, s + desc->fields[i].offset);
+ }
+ }
+ return (void *)s;
+}
+
+// Default copy - copies the data and returns a pointer to the byte following the src data.
+// The return value is the byte following the last byte copied to the destination.
+void * UMLRTObject_copy( const UMLRTObject_class * desc, const void * src, void * dst )
+{
+ uint8_t * s = (uint8_t *)src;
+ uint8_t * end_p = (uint8_t *)dst;
+ uint8_t * d = (uint8_t *)dst;
+
+ if (desc->fields == NULL)
+ {
+ memcpy(d, s, desc->size);
+ end_p += desc->size;
+ }
+ else
+ {
+ for (size_t i = 0; i < desc->size; ++i)
+ {
+ // overwrite end_p here - the return value for the last field is the one that is relevant.
+ end_p = (uint8_t *)desc->fields[i].desc->copy(desc->fields[i].desc, s + desc->fields[i].offset, d + desc->fields[i].offset);
+ }
+ }
+ return (void *)end_p;
+}
+
+// Default decode is a copy (returns a pointer to the byte after the src data).
+// For now, we'll use the return value of the copy (a value relative to dst) to compute what the return value should be.
+size_t UMLRTObject_getSize( const UMLRTObject_class * desc )
+{
+ size_t size = desc->size;
+
+ if (desc->fields != NULL)
+ {
+ size = desc->fields[desc->size - 1].offset +
+ desc->fields[desc->size - 1].desc->getSize(desc->fields[desc->size - 1].desc);
+ }
+ return size;
+}
+
+void * UMLRTObject_decode( const UMLRTObject_class * desc, const void * src, void * dst )
+{
+ desc->copy(desc, src, dst); // ignore return value.
+
+ return (void *)((uint8_t *)src + UMLRTObject_getSize(desc));
+}
+
+// Default encode is a copy (returns a pointer to the byte after the dst data).
+void * UMLRTObject_encode( const UMLRTObject_class * desc, const void * src, void * dst )
+{
+ return desc->copy(desc, src, dst);
+}
+
+// Default destroy does nothing (but returns a pointer to the byte after the data).
+void * UMLRTObject_destroy( const UMLRTObject_class * desc, void * data )
+{
+ uint8_t * s = (uint8_t *)data;
+ uint8_t * end_p = (uint8_t *)data;
+
+ if (desc->fields != NULL)
+ {
+ for (size_t i = 0; i < desc->size; ++i)
+ {
+ end_p = (uint8_t *)desc->fields[i].desc->destroy(desc->fields[i].desc, s + desc->fields[i].offset);
+ }
+ }
+ else
+ {
+ end_p += desc->size;
+ }
+ return (void *)end_p;
+}
+
+int UMLRTObject_fprintf( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ uint8_t * s = (uint8_t *)data;
+
+ if (desc->fields != NULL)
+ {
+ nchar += fprintf(ostream, "{%s:", desc->name);
+
+ for (size_t i = 0; i < desc->size; ++i)
+ {
+ nchar += fprintf(ostream, "{%s:", desc->fields[i].name);
+ nchar += UMLRTObject_fprintf(ostream, desc->fields[i].desc, s + desc->fields[i].offset);
+ nchar += fprintf(ostream, "}");
+ }
+ nchar += fprintf(ostream, "}");
+ }
+ else if (desc->fprintf != 0)
+ {
+ nchar += desc->fprintf(ostream, desc, data);
+ }
+ else
+ {
+ nchar += fprintf(ostream, "{%s: (unable to print)}", desc->name);
+ }
+ return nchar;
+}
+
+int UMLRTObject_fprintf_bool( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ bool b;
+ desc->copy(desc, data, &b);
+ nchar += fprintf(ostream, "{bool %s}", b ? "true" : "false");
+ return nchar;
+}
+
+int UMLRTObject_fprintf_char( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ char c;
+ desc->copy(desc, data, &c);
+ nchar += fprintf(ostream, "{char %c}", c);
+ return nchar;
+}
+
+int UMLRTObject_fprintf_double( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ double d;
+ desc->copy(desc, data, &d);
+ nchar += fprintf(ostream, "{double %f}", d);
+ return nchar;
+}
+
+int UMLRTObject_fprintf_float( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ float f;
+ desc->copy(desc, data, &f);
+ nchar += fprintf(ostream, "{float %f}", f);
+ return nchar;
+}
+
+int UMLRTObject_fprintf_int( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ int i;
+ desc->copy(desc, data, &i);
+ nchar += fprintf(ostream, "{int %d}", i);
+ return nchar;
+}
+
+int UMLRTObject_fprintf_long( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ long l;
+ desc->copy(desc, data, &l);
+ nchar += fprintf(ostream, "{long %ld}", l);
+ return nchar;
+}
+
+int UMLRTObject_fprintf_longlong( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ long long ll;
+ desc->copy(desc, data, &ll);
+ nchar += fprintf(ostream, "{longlong %lld}", ll);
+ return nchar;
+}
+
+int UMLRTObject_fprintf_ptr( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ void * p;
+ desc->copy(desc, data, &p);
+ // Pointer de-referencing tracked by Bug 97.
+ nchar += fprintf(ostream, "{ptr %p}", p);
+ return nchar;
+}
+
+int UMLRTObject_fprintf_short( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ short sh;
+ desc->copy(desc, data, &sh);
+ nchar += fprintf(ostream, "{short %d}", sh);
+ return nchar;
+}
+
+int UMLRTObject_fprintf_uchar( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ unsigned char c;
+ desc->copy(desc, data, &c);
+ nchar += fprintf(ostream, "{char %u}", c);
+ return nchar;
+}
+
+int UMLRTObject_fprintf_uint( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ unsigned int i;
+ desc->copy(desc, data, &i);
+ nchar += fprintf(ostream, "{uint %u}", i);
+ return nchar;
+}
+
+int UMLRTObject_fprintf_ulong( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ unsigned long l;
+ desc->copy(desc, data, &l);
+ nchar += fprintf(ostream, "{ulong %lu}", l);
+ return nchar;
+}
+
+int UMLRTObject_fprintf_ulonglong( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ unsigned long long ll;
+ desc->copy(desc, data, &ll);
+ nchar += fprintf(ostream, "{ulonglong %llu}", ll);
+ return nchar;
+}
+
+int UMLRTObject_fprintf_ushort( FILE *ostream, const UMLRTObject_class * desc, const void * data )
+{
+ int nchar = 0;
+ unsigned short sh;
+ desc->copy(desc, data, &sh);
+ nchar += fprintf(ostream, "{ushort %u}", sh);
+ return nchar;
+}
+
+const UMLRTObject_class UMLRTType_bool_
+= {
+ "bool",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_bool,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(bool),
+ NULL // fields
+};
+
+
+const UMLRTObject_class UMLRTType_char_
+= {
+ "char",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_char,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(char),
+ NULL, // fields
+};
+
+const UMLRTObject_class UMLRTType_double_
+= {
+ "double",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_double,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(double),
+ NULL, // fields
+};
+
+const UMLRTObject_class UMLRTType_float_
+= {
+ "float",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_float,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(float),
+ NULL, // fields
+};
+
+const UMLRTObject_class UMLRTType_int_
+= {
+ "int",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_int,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(int),
+ NULL, // fields
+};
+
+const UMLRTObject_class UMLRTType_long_
+= {
+ "long",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_long,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(long),
+ NULL, // fields
+};
+
+const UMLRTObject_class UMLRTType_longlong_
+= {
+ "longlong",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_longlong,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(long long),
+ NULL, // fields
+};
+
+const UMLRTObject_class UMLRTType_ptr_
+= {
+ "ptr",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_ptr,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(void *),
+ NULL // fields
+};
+
+const UMLRTObject_class UMLRTType_short_
+= {
+ "short",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_short,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(short),
+ NULL, // fields
+};
+
+const UMLRTObject_class UMLRTType_uchar_
+= {
+ "uchar",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_uchar,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(unsigned char),
+ NULL, // fields
+};
+
+const UMLRTObject_class UMLRTType_uint_
+= {
+ "uint",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_uint,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(unsigned int),
+ NULL, // fields
+};
+
+const UMLRTObject_class UMLRTType_ulong_
+= {
+ "ulong",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_ulong,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(unsigned long),
+ NULL, // fields
+};
+
+const UMLRTObject_class UMLRTType_ulonglong_
+= {
+ "ulonglong",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_ulonglong,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(unsigned long long),
+ NULL, // fields
+};
+
+const UMLRTObject_class UMLRTType_ushort_
+= {
+ "ushort",
+ UMLRTObject_initialize,
+ UMLRTObject_copy,
+ UMLRTObject_decode,
+ UMLRTObject_encode,
+ UMLRTObject_destroy,
+ UMLRTObject_getSize,
+ UMLRTObject_fprintf_ushort,
+ NULL, // super
+ UMLRTOBJECTCLASS_DEFAULT_VERSION, // version
+ UMLRTOBJECTCLASS_DEFAULT_BACKWARDS, // backwards
+ sizeof(unsigned short),
+ NULL, // fields
+};
+
+const UMLRTObject_class * const UMLRTType_bool = &UMLRTType_bool_;
+const UMLRTObject_class * const UMLRTType_char = &UMLRTType_char_;
+const UMLRTObject_class * const UMLRTType_double = &UMLRTType_double_;
+const UMLRTObject_class * const UMLRTType_float = &UMLRTType_float_;
+const UMLRTObject_class * const UMLRTType_int = &UMLRTType_int_;
+const UMLRTObject_class * const UMLRTType_long = &UMLRTType_long_;
+const UMLRTObject_class * const UMLRTType_longlong = &UMLRTType_longlong_;
+const UMLRTObject_class * const UMLRTType_ptr = &UMLRTType_ptr_;
+const UMLRTObject_class * const UMLRTType_short = &UMLRTType_short_;
+const UMLRTObject_class * const UMLRTType_uchar = &UMLRTType_uchar_;
+const UMLRTObject_class * const UMLRTType_uint = &UMLRTType_uint_;
+const UMLRTObject_class * const UMLRTType_ulong = &UMLRTType_ulong_;
+const UMLRTObject_class * const UMLRTType_ulonglong = &UMLRTType_ulonglong_;
+const UMLRTObject_class * const UMLRTType_ushort = &UMLRTType_ushort_;
+
diff --git a/rts/umlrt/umlrtoutsignal.cc b/rts/umlrt/umlrtoutsignal.cc
new file mode 100644
index 0000000..9a25c22
--- /dev/null
+++ b/rts/umlrt/umlrtoutsignal.cc
@@ -0,0 +1,211 @@
+// umlrtoutsignal.cc
+
+#include "umlrtcommsport.hh"
+#include "umlrtcommsportfarend.hh"
+#include "umlrtcontroller.hh"
+#include "umlrtoutsignal.hh"
+#include "umlrtframeservice.hh"
+#include "umlrtslot.hh"
+#include "basefatal.hh"
+#include "basedebugtype.hh"
+#include "basedebug.hh"
+
+/*******************************************************************************
+* Copyright (c) 2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+UMLRTOutSignal::UMLRTOutSignal()
+{
+
+}
+
+UMLRTOutSignal::UMLRTOutSignal(const UMLRTOutSignal &signal)
+{
+
+}
+
+UMLRTOutSignal& UMLRTOutSignal::operator=(const UMLRTOutSignal &signal)
+{
+ UMLRTSignal::operator = (signal);
+ return *this;
+}
+
+UMLRTOutSignal::~UMLRTOutSignal()
+{
+
+}
+
+
+// See documentation in UMLRTSignal.
+void UMLRTOutSignal::encode( const void * data, const UMLRTObject_class * desc, int arraySize )
+{
+ if (!element)
+ {
+ FATAL("encode attempted on invalid signal.");
+ }
+ element->encode(desc, data, arraySize);
+}
+
+// See documentation in UMLRTSignal.
+void UMLRTOutSignal::encode( int ptrIndirection, void * data, const UMLRTObject_class * desc, int arraySize )
+{
+ FATAL("encode ptr indirection not supported");
+}
+
+
+// Synchronous send out all port instances. Returns the number of replies (0 if fail).
+int UMLRTOutSignal::invoke( UMLRTMessage * replyMsgs )
+{
+ FATAL("invoke not implemented");
+ return 0;
+}
+
+// Synchronous send out a specific port instance. Returns the number of replies (0 or 1).
+int UMLRTOutSignal::invokeAt( int index, UMLRTMessage * replyMsg )
+{
+ FATAL("invokeAt not implemented");
+ return 0;
+}
+
+// Reply to a synchronous message. Return true if success.
+bool UMLRTOutSignal::reply()
+{
+ FATAL("reply not implemented");
+ return false;
+}
+
+bool UMLRTOutSignal::send( UMLRTSignalElement::Priority priority ) const
+{
+ int ok = false;
+
+ if (!element)
+ {
+ BDEBUG(BD_SWERR, "WARNING: attempt to send an invalid signal. Capsule and port information are not known.\n");
+ }
+ else
+ {
+ const UMLRTCommsPort * srcPort = element->getSrcPort();
+
+ if (!srcPort)
+ {
+ FATAL("No srcPort when sending a signal. Should be the unbound port at least.");
+ }
+ else if (srcPort->unbound)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_SEND_UNBOUND);
+ }
+ else
+ {
+ // Set priority here.
+ if ((priority < UMLRTSignalElement::PRIORITY_BACKGROUND) || (priority > UMLRTSignalElement::PRIORITY_PANIC))
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_SEND_PRIORITY);
+ }
+ else
+ {
+ element->setPriority(priority);
+
+ // Attempt all sends regardless of errors - one successful send will mean overall success.
+ for (size_t i = 0; (i < srcPort->numFarEnd); ++i)
+ {
+ ok |= signalDeliver(srcPort, i);
+ }
+ if (ok)
+ {
+ // If one succeeds, we'll return success - and remove error code from controller.
+ srcPort->slot->controller->setError(UMLRTController::E_OK);
+ }
+ }
+ }
+ }
+ return ok;
+}
+
+// Send the signal to one far end port. See umlrtsignal.hh for documentation.
+bool UMLRTOutSignal::sendAt( size_t portIndex, UMLRTSignalElement::Priority priority ) const
+{
+ bool ok = false;
+
+ if (!element)
+ {
+ BDEBUG(BD_SEND, "WARNING: attempt to send an invalid signal. Capsule and port information are not known.\n");
+ }
+ else
+ {
+ const UMLRTCommsPort * srcPort = element->getSrcPort();
+
+ if (!srcPort)
+ {
+ FATAL("No srcPort when sending a signal. Should be the unbound port at least.");
+ }
+ else if (srcPort->unbound)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_SEND_UNBOUND);
+ }
+ else if ((priority < UMLRTSignalElement::PRIORITY_BACKGROUND) || (priority > UMLRTSignalElement::PRIORITY_PANIC))
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_SEND_PRIORITY);
+ }
+ else
+ {
+ // Set priority here.
+ element->setPriority(priority);
+
+ ok = signalDeliver(srcPort, portIndex);
+ }
+ }
+ return ok;
+}
+
+// Deliver the signal to a destination.
+bool UMLRTOutSignal::signalDeliver( const UMLRTCommsPort * srcPort, size_t portIndex ) const
+{
+ bool ok = false;
+
+ // Lock the RTS
+ UMLRTFrameService::rtsLock();
+
+ const UMLRTCommsPort * destPort;
+
+ if (srcPort->slot->condemned)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_SEND_FROM_DSTR);
+ }
+ else if (portIndex >= srcPort->numFarEnd)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_SEND_NO_PORT_INST);
+ }
+ else if ((destPort = srcPort->farEnds[portIndex].port) == NULL)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_SEND_PRT_NOT_CON);
+ }
+ else if (destPort->slot->condemned)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_SEND_TO_DSTR);
+ }
+ else if (destPort->slot->capsule == NULL)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_SEND_NO_CAP_INST);
+ }
+ else if (destPort->slot->controller == NULL)
+ {
+ FATAL("Destination slot %s has no controller when sending from capsule %s via port %s[%d].",
+ destPort->slot,
+ srcPort->slot->name,
+ srcPort->role()->name,
+ portIndex);
+ }
+ else
+ {
+ ok = destPort->slot->controller->deliver( destPort, *this, portIndex );
+ // Error code set by 'deliver' if an error occurred.
+ }
+ // Unlock the RTS
+ UMLRTFrameService::rtsUnlock();
+
+ return ok;
+}
diff --git a/rts/umlrt/umlrtpool.cc b/rts/umlrt/umlrtpool.cc
new file mode 100644
index 0000000..2fd996f
--- /dev/null
+++ b/rts/umlrt/umlrtpool.cc
@@ -0,0 +1,55 @@
+// umlrtpool.cc
+
+/*******************************************************************************
+ * Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+#include <stdlib.h>
+#include "basefatal.hh"
+#include "basedebug.hh"
+#include "umlrtguard.hh"
+#include "umlrtpool.hh"
+#include "umlrtqueueelement.hh"
+#include "umlrtuserconfig.hh"
+
+// See umlrtqueue.hh for documentation.
+
+// Create an empty queue.
+UMLRTPool::UMLRTPool(size_t incrementSize) :
+ head(0), increment(incrementSize), qid(0)
+{
+}
+
+UMLRTPool::~UMLRTPool()
+{
+}
+
+// Remove the first element on the pool.
+// Grow the list if it is empty
+const UMLRTQueueElement * UMLRTPool::get()
+{
+ UMLRTGuard g(mutex);
+
+ if (head == NULL)
+ {
+ grow();
+ }
+ const UMLRTQueueElement * element = head;
+ head = element->next;
+ return element;
+}
+
+// Add element back to pool (at front).
+void UMLRTPool::put(const UMLRTQueueElement * element)
+{
+ UMLRTGuard g(mutex);
+ if (element)
+ {
+ element->next = head;
+ head = element;
+ }
+}
diff --git a/rts/umlrt/umlrtprioritymessagequeue.cc b/rts/umlrt/umlrtprioritymessagequeue.cc
new file mode 100644
index 0000000..f49ee11
--- /dev/null
+++ b/rts/umlrt/umlrtprioritymessagequeue.cc
@@ -0,0 +1,269 @@
+// umlrtprioritymessagequeue.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "basedebug.hh"
+#include "basedebugtype.hh"
+#include "basefatal.hh"
+#include "umlrtapi.hh"
+#include "umlrtslot.hh"
+#include "umlrtcommsportrole.hh"
+#include "umlrtprioritymessagequeue.hh"
+#include "umlrtmessage.hh"
+#include "umlrttimer.hh"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+// See umlrtprioritymessagequeue.hh for documentation.
+
+UMLRTPriorityMessageQueue::UMLRTPriorityMessageQueue( const char * owner_ ) : owner(owner_)
+{
+ // Appenders write the pipe. Controllers 'wait' on a select() on the pipe with
+ // a timeout associated with any running timer. A write on the pipe wakes the controller.
+ if (pipe(notifyFd) < 0)
+ {
+ FATAL_ERRNO("pipe");
+ }
+ // Bytes available in the pipe wake up the controller as 'notify'. The controller
+ // has to be able to clean out the pipe (read) without blocking.
+ int flags = fcntl(notifyFd[0], F_GETFL, 0);
+ if (flags < 0)
+ {
+ FATAL_ERRNO("fcntl F_GETFL");
+ }
+ if (fcntl(notifyFd[0], F_SETFL, flags | O_NONBLOCK) < 0)
+ {
+ FATAL_ERRNO("fcntl F_SETFL");
+ }
+}
+
+// Return the queue associated with a priority.
+UMLRTMessageQueue & UMLRTPriorityMessageQueue::getQueue( UMLRTSignalElement::Priority priority )
+{
+ return queue[priority];
+}
+
+// Used to transfer all messages from each priority queue to
+// their associated priority-queue
+
+void UMLRTPriorityMessageQueue::moveAll( UMLRTPriorityMessageQueue & fromQueue )
+{
+ for (UMLRTSignalElement::Priority priority = 0; priority < UMLRTSignalElement::PRIORITY_MAX; ++priority)
+ {
+ const UMLRTQueueElement * last; // These are UMLRTMessage's.
+ const UMLRTQueueElement * all = fromQueue.getQueue(priority).dequeueAll(&last);
+
+ queue[priority].enqueueAll(all, last);
+ }
+}
+
+// Get all expired timers from the timer-queue and enqueue these to this the capsule priority message queue.
+// The 'incomingQueue' is also a priority queue, but timers are never queued to the incomingQueue instance.
+void UMLRTPriorityMessageQueue::queueTimerMessages( UMLRTTimerQueue * timerQueue )
+{
+ UMLRTTimer * timer = timerQueue->dequeue();
+
+ while (timer != NULL)
+ {
+ if (!timer->allocated)
+ {
+ FATAL("%s:timer (%p) obtained from timer-queue is not allocated.", owner, timer);
+ }
+ UMLRTMessage * msg = umlrt::MessageGetFromPool();
+
+ if (!msg)
+ {
+ FATAL("message allocation failed during timout message creation.");
+ }
+ msg->signal = timer->signal;
+ msg->destPort = timer->destPort;
+ msg->destSlot = timer->destSlot;
+ msg->srcPortIndex = 0; // Timer ports are not replicated.
+ msg->isCommand = false;
+
+ char tmbuf[UMLRTTimespec::TIMESPEC_TOSTRING_SZ];
+ BDEBUG(BD_TIMER, "%s: queue timer msg signal id(%d) to %s(%s) isInterval(%d) due(%s)\n",
+ owner, msg->signal.getId(), msg->destPort->slot->name, msg->destPort->role()->name, timer->isInterval,
+ timer->isInterval ? timer->due.toStringRelative(tmbuf, sizeof(tmbuf)): timer->due.toString(tmbuf, sizeof(tmbuf)));
+
+ enqueue(msg);
+
+ if (timer->isInterval)
+ {
+ // This is an interval timer. Adjust it's due-time and requeue it.
+ timer->due += timer->interval;
+ timerQueue->enqueue(timer);
+ }
+ else
+ {
+ // This timer can be put back on the system pool.
+ umlrt::TimerPutToPool(timer);
+ }
+
+ // See if there's another one expired.
+ timer = timerQueue->dequeue();
+ }
+}
+
+// Get the highest priority message from the collection of queues.
+
+UMLRTMessage * UMLRTPriorityMessageQueue::dequeueHighestPriority()
+{
+ UMLRTMessage * msg = 0;
+
+ for (UMLRTSignalElement::Priority priority = (UMLRTSignalElement::PRIORITY_MAX-1);
+ (priority >= UMLRTSignalElement::PRIORITY_BACKGROUND) && (priority < (UMLRTSignalElement::PRIORITY_MAX)) && !msg; --priority)
+ {
+ msg = (UMLRTMessage *)queue[priority].dequeue();
+
+ }
+ if (msg != NULL)
+ {
+ // Source port may no longer exist.
+ BDEBUG(BD_MSG, "%s: msg dequeued priority(%d) -> %s %s[%d] signal id(%d) payloadSz(%d)\n",
+ owner,
+ msg->signal.getPriority(),
+ msg->isCommand ? "" : msg->destPort->slot->name,
+ msg->isCommand ? "isCommand" : msg->destPort->role()->name,
+ msg->isCommand ? 0 : msg->sapIndex0,
+ msg->signal.getId(),
+ msg->signal.getPayloadSize());
+ }
+ return msg;
+}
+
+// Put the message into its appropriate priority-queue.
+void UMLRTPriorityMessageQueue::enqueue( UMLRTMessage * msg, bool front )
+{
+ UMLRTSignalElement::Priority priority = msg->signal.getPriority();
+
+ if ((priority < 0) || (priority >= UMLRTSignalElement::PRIORITY_MAX))
+ {
+ FATAL("enqueue with bad priority (%d)", priority);
+ }
+ if (!msg->isCommand && msg->destPort == NULL)
+ {
+ FATAL("enqueuing a message with no destination port");
+ }
+ // Source port may no longer exist.
+ BDEBUG(BD_MSG, "%s: msg enqueued priority(%d) -> %s[%d] signal id(%d) payloadSz(%d)\n",
+ owner,
+ msg->signal.getPriority(),
+ msg->isCommand ? "isCommand" : msg->destPort->role()->name,
+ msg->isCommand ? 0 : msg->sapIndex0,
+ msg->signal.getId(),
+ msg->signal.getPayloadSize());
+
+ queue[priority].enqueue(msg, front);
+
+ sendNotification();
+}
+
+// See umlrtprioritymessagequeue.hh for documentation.
+void UMLRTPriorityMessageQueue::sendNotification()
+{
+ bool already_notified = true;
+ int bytes_ready;
+
+ if (ioctl(notifyFd[0], FIONREAD, &bytes_ready) < 0)
+ {
+ FATAL_ERRNO("ioctl");
+ }
+ if (!bytes_ready)
+ {
+ already_notified = false;
+
+ // Write a byte to the notification-pipe as a notification of a pending message.
+ uint8_t notifyByte = 0;
+
+ if (write(notifyFd[1], ¬ifyByte, 1) < 0)
+ {
+ FATAL_ERRNO("write");
+ }
+ }
+ BDEBUG(BD_CONTROLLER, "%s: send notification (%s)\n", owner, already_notified ? "notification already pending" : "added notify byte");
+}
+
+// See umlrtprioritymessagequeue.hh for documentation.
+void UMLRTPriorityMessageQueue::clearNotifyFd()
+{
+ uint8_t ignore;
+ int bytes;
+ if ((bytes = read(notifyFd[0], &ignore, 1)) < 0)
+ {
+ FATAL_ERRNO("initial read - synchronization mechanism implies a byte is waiting");
+ }
+ else if (!bytes)
+ {
+ // Indicates synchronization logic error. Should always get at least one byte.
+ FATAL("Should never find pipe empty when clearing a notification.");
+ }
+ // Clear out the notification-pipe.
+ // Can shrink this after notify is debugged.
+ bool done = false;
+ while (!done)
+ {
+ int bytes = read(notifyFd[0], &ignore, 1);
+ if (bytes < 0)
+ {
+ if (errno != EAGAIN)
+ {
+ FATAL_ERRNO("read");
+ }
+ else
+ {
+ done = true;
+ }
+ }
+ if (bytes > 0)
+ {
+ // BDEBUG(0, "read returned bytes(%d)\n", bytes);
+ }
+ else
+ {
+ done = true;
+ }
+ }
+}
+
+// See umlrtprioritymessagequeue.hh for documentation.
+int UMLRTPriorityMessageQueue::getNotifyFd()
+{
+ return notifyFd[0];
+}
+
+
+// See umlrtprioritymessagequeue.hh for documentation.
+bool UMLRTPriorityMessageQueue::isEmpty()
+{
+ bool isempty = true;
+
+ for (UMLRTSignalElement::Priority priority = (UMLRTSignalElement::PRIORITY_MAX-1);
+ (priority >= UMLRTSignalElement::PRIORITY_BACKGROUND) && (priority < (UMLRTSignalElement::PRIORITY_MAX)) && isempty; --priority)
+ {
+ if (!queue[priority].isEmpty())
+ {
+ isempty = false;
+ }
+ }
+ return isempty;
+}
+
+void UMLRTPriorityMessageQueue::remove( UMLRTQueue::match_compare_t callback, UMLRTQueue::match_notify_t notify, void * userData )
+{
+ for (UMLRTSignalElement::Priority priority = (UMLRTSignalElement::PRIORITY_MAX-1);
+ (priority >= UMLRTSignalElement::PRIORITY_BACKGROUND) && (priority < (UMLRTSignalElement::PRIORITY_MAX)); --priority)
+ {
+ queue[priority].remove(callback, notify, userData);
+ }
+}
+
diff --git a/rts/umlrt/umlrtprotocol.cc b/rts/umlrt/umlrtprotocol.cc
new file mode 100644
index 0000000..47faa2c
--- /dev/null
+++ b/rts/umlrt/umlrtprotocol.cc
@@ -0,0 +1,720 @@
+// umlrtprotocol.cc
+
+/*******************************************************************************
+ * Copyright (c) 2015 Zeligsoft (2009) Limited and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+#include "umlrtcommsport.hh"
+#include "umlrtcommsportfarend.hh"
+#include "umlrtcontroller.hh"
+#include "umlrtframeservice.hh"
+#include "umlrthashmap.hh"
+#include "umlrtprotocol.hh"
+#include "basefatal.hh"
+#include "basedebugtype.hh"
+#include "basedebug.hh"
+#include <string.h>
+
+/*static*/ UMLRTHashMap * UMLRTProtocol::portToName; // key and object are associated with the port - not allocated/deallocated.
+/*static*/ UMLRTHashMap * UMLRTProtocol::nameToSppPort; // key and object are associated with the port - not allocated/deallocated.
+/*static*/ UMLRTHashMap * UMLRTProtocol::nameToUnboundSapPorts; // key and object are both allocated and must be deallocated.
+
+/*static*/ void UMLRTProtocol::addSapToQueue( const char * service, const UMLRTCommsPort * sapPort )
+{
+ // Assumes RTS lock obtained.
+ UMLRTQueue * unboundSapPorts;
+ if ((unboundSapPorts = (UMLRTQueue *)getNameToUnboundSapPortsMap()->getObject(service)) == NULL)
+ {
+ // This service had no SAP queue - create it.
+ unboundSapPorts = new UMLRTQueue();
+
+ // Both key and object (queue) must be deallocated when we remove it from the map.
+ getNameToUnboundSapPortsMap()->insert(strdup(service), unboundSapPorts);
+ }
+ // Queue elements must be deleted when they are removed from the queue.
+ UnboundSapPort * element = new UnboundSapPort(sapPort);
+
+ BDEBUG(BD_SAP,"add slot %s port %s to SPP %s pending queue - ENQUEUE\n", sapPort->slot->name, sapPort->role()->name, service);
+ unboundSapPorts->enqueue(element);
+}
+
+void UMLRTProtocol::bindingNotification( bool on )
+{
+ srcPort->notification = on;
+}
+
+bool UMLRTProtocol::bindingNotificatioRequested() const
+{
+ return srcPort->notification;
+}
+
+/*static*/ void UMLRTProtocol::bindQueuedSapsToSpp( const UMLRTCommsPort * sppPort )
+{
+ // Assumes RTS lock obtained.
+ int freeFarEnds = UMLRTFrameService::freeFarEndsCount(sppPort);
+
+ // Get queue of waiting SAP ports.
+ UMLRTQueue * unboundSapPorts = (UMLRTQueue *)getNameToUnboundSapPortsMap()->getObject(sppPort->registeredName);
+
+ for (size_t i = 0; (unboundSapPorts != NULL) && (i < sppPort->numFarEnd) && (freeFarEnds > 0); ++i)
+ {
+ // Get next waiting SAP port.
+ UnboundSapPort * element;
+ if ((element = (UnboundSapPort *)unboundSapPorts->dequeue()) != NULL)
+ {
+ // Bind the next SAP.
+ UMLRTFrameService::bindServicePort(element->port, sppPort);
+ delete element;
+ // One less free port on SPP.
+ --freeFarEnds;
+ }
+ }
+ if (unboundSapPorts != NULL)
+ {
+ if (unboundSapPorts->isEmpty())
+ {
+ // No more queued SAP ports for this SPP - remove the queue from the unbound-map.
+ // Both the key and the object (queue) must be deallocated.
+ const void * key = getNameToUnboundSapPortsMap()->remove(sppPort->registeredName);
+ if (key != NULL)
+ {
+ free((void *)key);
+ }
+ delete unboundSapPorts;
+ }
+ }
+}
+
+/*static*/ void UMLRTProtocol::completeRegistration( const UMLRTCommsPort * port, const char * service )
+{
+ // Assumes RTS lock obtained.
+ if (port->registeredName == NULL)
+ {
+ port->registeredName = strdup(service);
+ }
+ if (strcmp(port->registeredName, service))
+ {
+ if (!port->generated)
+ {
+ free((void *)port->registeredName);
+ }
+ port->registeredName = strdup(service);
+ }
+ // Memory for key/object in portToNameMap not managed with map.
+ getPortToNameMap()->insert(port, port->registeredName);
+}
+
+/*static*/ void UMLRTProtocol::completeDeregistration( const UMLRTCommsPort * port )
+{
+ // Assumes RTS lock obtained.
+ // Memory for key/object in portToNameMap not managed with map.
+ getPortToNameMap()->remove(port);
+ if (port->registeredName != NULL)
+ {
+ if (!port->generated)
+ {
+ free((void *)port->registeredName);
+ port->registeredName = NULL;
+ }
+ }
+}
+
+/*static*/ bool UMLRTProtocol::deregisterSapPort( const UMLRTCommsPort * sapPort )
+{
+ bool ok = false;
+ if (!sapPort->sap)
+ {
+ sapPort->slot->controller->setError(UMLRTController::E_SAPDREG_NOT_SAP);
+ }
+ else if (sapPort->registeredName == NULL)
+ {
+ ok = true; // Wasn't registered.
+ }
+ else
+ {
+ const char * service;
+ if ((service = (const char *)getPortToNameMap()->getObject(sapPort)) != NULL)
+ {
+ // Was registered. Remove this SAP if it was queued for the SPP.
+ removeSapFromQueue(sapPort);
+
+ if (sapPort->numFarEnd > 0)
+ {
+ const UMLRTCommsPort * sppPort;
+ if ((sppPort = sapPort->farEnds[0].port) != NULL)
+ {
+ // If this SAP was bound to the SPP, unbind it.
+ UMLRTFrameService::unbindServicePort(sapPort, sppPort);
+
+ // Go to pending SAP queue for this SPP and bind another SAP.
+ bindQueuedSapsToSpp(sppPort);
+ }
+ }
+ completeDeregistration(sapPort);
+ }
+ ok = true;
+ }
+ BDEBUG(BD_SAP, "SAP de-register slot %s port %s %s %s\n", sapPort->slot->name, sapPort->role()->name, ok ? "ok" : "failed", ok ? "" : sapPort->slot->controller->strerror());
+ return ok;
+}
+
+bool UMLRTProtocol::deregisterSAP()
+{
+ bool ok = false;
+ if (srcPort->locked)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_SAPDREG_SAP_LOCKED);
+ }
+ else
+ {
+ UMLRTFrameService::rtsLock();
+
+ ok = deregisterSapPort(srcPort);
+
+ UMLRTFrameService::rtsUnlock();
+ }
+ return ok;
+}
+
+/*static*/ bool UMLRTProtocol::deregisterSppPort( const UMLRTCommsPort * sppPort )
+{
+ bool ok = false;
+
+ if (!sppPort->spp)
+ {
+ sppPort->slot->controller->setError(UMLRTController::E_SPPDREG_NOT_SPP);
+ }
+ else
+ {
+ const char * service;
+ if ((service = (const char *)getPortToNameMap()->getObject(sppPort)) != NULL)
+ {
+ // For every currently bound SAP port, unbind it and queue for re-binding later.
+ for (size_t i = 0; i < sppPort->numFarEnd; ++i)
+ {
+ const UMLRTCommsPort * sapPort = sppPort->farEnds[i].port;
+ if (sapPort != NULL)
+ {
+ // A SAP was bound on this port. Unbind it and queue it to the service.
+ UMLRTFrameService::unbindServicePort(sapPort, sppPort);
+ addSapToQueue(sppPort->registeredName, sapPort);
+ }
+ }
+ // This port no longer registered as an SPP. Memory is associated with the port.
+ getNameToSppPortMap()->remove(sppPort->registeredName);
+ completeDeregistration(sppPort);
+ }
+ ok = true;
+ }
+ BDEBUG(BD_SAP, "SPP de-register slot %s port %s %s %s\n", sppPort->slot->name, sppPort->role()->name, ok ? "ok" : "failed", ok ? "" : sppPort->slot->controller->strerror());
+ return ok;
+}
+
+bool UMLRTProtocol::deregisterSPP()
+{
+ bool ok = false;
+ if (srcPort->locked)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_SPPDREG_SPP_LOCKED);
+ }
+ else
+ {
+ UMLRTFrameService::rtsLock();
+
+ ok = deregisterSppPort(srcPort);
+
+ UMLRTFrameService::rtsUnlock();
+ }
+ return ok;
+}
+
+/*static*/ UMLRTHashMap * UMLRTProtocol::getPortToNameMap()
+{
+ if (portToName == NULL)
+ {
+ portToName = new UMLRTHashMap("portToName", UMLRTHashMap::compareValue);
+ }
+ return portToName;
+}
+
+/*static*/ UMLRTHashMap * UMLRTProtocol::getNameToSppPortMap()
+{
+ if (nameToSppPort == NULL)
+ {
+ nameToSppPort = new UMLRTHashMap("nameToSppPort", UMLRTHashMap::compareString);
+ }
+ return nameToSppPort;
+}
+
+/*static*/ UMLRTHashMap * UMLRTProtocol::getNameToUnboundSapPortsMap()
+{
+ if (nameToUnboundSapPorts == NULL)
+ {
+ nameToUnboundSapPorts = new UMLRTHashMap("nameToUnboundSapPorts", UMLRTHashMap::compareString);
+ }
+ return nameToUnboundSapPorts;
+}
+
+const char * UMLRTProtocol::getRegisteredName() const
+{
+ return srcPort->registeredName;
+}
+
+/*static*/ bool UMLRTProtocol::isRegistered( const UMLRTCommsPort * port )
+{
+ UMLRTFrameService::rtsLock();
+
+ bool registered = (getPortToNameMap()->getObject(port) != NULL);
+
+ UMLRTFrameService::rtsUnlock();
+
+ return registered;
+}
+
+bool UMLRTProtocol::isRegistered() const
+{
+ return isRegistered(srcPort);
+}
+
+bool UMLRTProtocol::isBoundAt( int index ) const
+{
+ bool bound = false;
+
+ UMLRTFrameService::rtsLock();
+
+ if ((index >= 0) && (index < (int)srcPort->numFarEnd))
+ {
+ bound = (srcPort->farEnds[index].port != NULL);
+ }
+ UMLRTFrameService::rtsUnlock();
+
+ return bound;
+}
+
+int UMLRTProtocol::indexTo( const UMLRTCapsule * capsule ) const
+{
+ int index = -1;
+
+ UMLRTFrameService::rtsLock();
+
+ for (size_t i = 0; (i < srcPort->numFarEnd) && (index == -1); ++i)
+ {
+ if (srcPort->farEnds[i].port)
+ {
+ if (srcPort->farEnds[i].port->slot->capsule == capsule)
+ {
+ index = i;
+ }
+ }
+ }
+ UMLRTFrameService::rtsUnlock();
+
+ return index;
+}
+
+bool UMLRTProtocol::isIndexTo( int index, const UMLRTCapsule * capsule ) const
+{
+ bool isIndex = false;
+
+ UMLRTFrameService::rtsLock();
+
+ if ((index >= 0) && (index <= (int)srcPort->numFarEnd))
+ {
+ isIndex = (srcPort->farEnds[index].port->slot->capsule == capsule);
+ }
+ UMLRTFrameService::rtsUnlock();
+
+ return isIndex;
+}
+
+int UMLRTProtocol::purge()
+{
+ int count;
+
+ UMLRTFrameService::rtsLock();
+
+ count = srcPort->purge();
+
+ UMLRTFrameService::rtsUnlock();
+
+ return count;
+}
+
+int UMLRTProtocol::purgeAt( int index )
+{
+ int count;
+
+ UMLRTFrameService::rtsLock();
+
+ count = srcPort->purge(index);
+
+ UMLRTFrameService::rtsUnlock();
+
+ return count;
+}
+
+int UMLRTProtocol::recall()
+{
+ int count;
+
+ UMLRTFrameService::rtsLock();
+
+ count = srcPort->recall(-1/*index*/, false/*front*/, true/*one*/);
+
+ UMLRTFrameService::rtsUnlock();
+
+ return count;
+}
+
+int UMLRTProtocol::recallAll()
+{
+ int count;
+
+ UMLRTFrameService::rtsLock();
+
+ count = srcPort->recall(-1/*index*/, false/*front*/, false/*one*/);
+
+ UMLRTFrameService::rtsUnlock();
+
+ return count;
+}
+
+int UMLRTProtocol::recallAllAt( int index, bool front )
+{
+ int count;
+
+ UMLRTFrameService::rtsLock();
+
+ count = srcPort->recall(index, front, false/*one*/);
+
+ UMLRTFrameService::rtsUnlock();
+
+ return count;
+}
+
+int UMLRTProtocol::recallAllFront()
+{
+ int count;
+
+ UMLRTFrameService::rtsLock();
+
+ count = srcPort->recall(-1/*index*/, true/*front*/, false/*one*/);
+
+ UMLRTFrameService::rtsUnlock();
+
+ return count;
+}
+
+int UMLRTProtocol::recallAt( int index, bool front )
+{
+ int count;
+
+ UMLRTFrameService::rtsLock();
+
+ count = srcPort->recall(index, front, true/*one*/);
+
+ UMLRTFrameService::rtsUnlock();
+
+ return count;
+}
+
+int UMLRTProtocol::recallFront()
+{
+ int count;
+
+ UMLRTFrameService::rtsLock();
+
+ count = srcPort->recall(-1/*index*/, true/*front*/, true/*one*/);
+
+ UMLRTFrameService::rtsUnlock();
+
+ return count;
+}
+
+/*static*/ bool UMLRTProtocol::registerSapPort( const UMLRTCommsPort * sapPort, const char * service )
+{
+ bool ok = false;
+
+ if (!sapPort->sap)
+ {
+ sapPort->slot->controller->setError(UMLRTController::E_SAPREG_NOT_SAP);
+ }
+ else
+ {
+ bool continueRegistration = false; // Set true if registration needs to be performed.
+
+ char * registeredName = (char *)getPortToNameMap()->getObject(sapPort);
+ if (registeredName != NULL)
+ {
+ // Already registered. See if it's registered to the same service.
+ if (strcmp(registeredName, service))
+ {
+ // Was registered to an another SPP - deregister it first, then continue.
+ deregisterSapPort(sapPort);
+ continueRegistration = true;
+ }
+ // else it was already registered to this service and we can skip the remainder of registration.
+ }
+ else
+ {
+ // Wasn't registered.
+ continueRegistration = true;
+ }
+ if (continueRegistration)
+ {
+ // Bind to SPP if possible.
+ const UMLRTCommsPort * sppPort = (const UMLRTCommsPort *)getNameToSppPortMap()->getObject(service);
+ bool bound = false;
+
+ if (sppPort != NULL)
+ {
+ // SPP found. Attempt to bind.
+ bound = UMLRTFrameService::bindServicePort(sapPort, sppPort);
+ }
+ if (!bound)
+ {
+ // SAP unable to bind to SPP. Queue for binding when an SPP port comes free.
+ addSapToQueue(service, sapPort);
+ }
+ completeRegistration(sapPort, service);
+ }
+ ok = true;
+ }
+ BDEBUG(BD_SAP, "SAP register slot %s port %s %s %s\n", sapPort->slot->name, sapPort->role()->name, ok ? "ok" : "failed", ok ? "" : sapPort->slot->controller->strerror());
+ return ok;
+}
+
+bool UMLRTProtocol::registerSAP( const char * service )
+{
+ UMLRTFrameService::rtsLock();
+
+ bool ok = registerSapPort(srcPort, service);
+
+ UMLRTFrameService::rtsUnlock();
+
+ return ok;
+}
+
+/*static*/ bool UMLRTProtocol::registerSppPort( const UMLRTCommsPort * sppPort, const char * service )
+{
+ bool ok = false;
+
+ if (!sppPort->spp)
+ {
+ sppPort->slot->controller->setError(UMLRTController::E_SPPREG_NOT_SPP);
+ }
+ else
+ {
+ // See if another port has this service registered already.
+ const UMLRTCommsPort * otherSpp = (const UMLRTCommsPort *)getNameToSppPortMap()->getObject(service);
+ if ((otherSpp != NULL) && (otherSpp != sppPort))
+ {
+ sppPort->slot->controller->setError(UMLRTController::E_SPPREG_PREVIOUS);
+ }
+ else if (otherSpp != NULL)
+ {
+ // This SPP was already registered under this service. Nothing more to do.
+ ok = true;
+ }
+ else
+ {
+ char * registeredName = (char *)getPortToNameMap()->getObject(sppPort);
+ if (registeredName != NULL)
+ {
+ // Already registered. See if it's registered to the same service.
+ if (strcmp(registeredName, service))
+ {
+ // Registered to an another service - de-register it.
+ deregisterSppPort(sppPort);
+ }
+ }
+ // Complete the registration and we'll use the port registeredName for the name-to-spp-port map.
+ completeRegistration(sppPort, service);
+
+ // Make this the SPP service provider. Memory associated with the port.
+ getNameToSppPortMap()->insert(sppPort->registeredName, (void *)sppPort);
+
+ // Bind any queued SAP port.
+ bindQueuedSapsToSpp(sppPort);
+
+ ok = true;
+ }
+ }
+ BDEBUG(BD_SAP, "SPP register slot %s port %s %s %s\n", sppPort->slot->name, sppPort->role()->name, ok ? "ok" : "failed", ok ? "" : sppPort->slot->controller->strerror());
+
+ return ok;
+}
+
+bool UMLRTProtocol::registerSPP( const char * service )
+{
+ UMLRTFrameService::rtsLock();
+
+ bool ok = registerSppPort( srcPort, service );
+
+ UMLRTFrameService::rtsUnlock();
+
+ return ok;
+}
+
+/*static*/ void UMLRTProtocol::removeSapFromQueue( const UMLRTCommsPort * sapPort )
+{
+ // Assumes RTS lock obtained.
+ UMLRTQueue * unboundSapPorts = (UMLRTQueue *)getNameToUnboundSapPortsMap()->getObject(sapPort->registeredName);
+ if (unboundSapPorts != NULL)
+ {
+ // This SPP had queued SAPs. Remove the de-registering SAP from the queue.
+ unboundSapPorts->remove((UMLRTQueue::match_compare_t)removeSapFromQueueMatchCompare, (UMLRTQueue::match_notify_t)removeSapFromQueueMatchNotify, (void *)sapPort, true/*one*/);
+ if (unboundSapPorts->isEmpty())
+ {
+ // If this was the last queued SAP, remove the queue and delete it. The name key has to be freed.
+ char * key = (char *)getNameToUnboundSapPortsMap()->remove(sapPort->registeredName);
+ if (key)
+ {
+ free(key);
+ }
+ delete unboundSapPorts;
+ }
+ }
+}
+
+/*static*/ bool UMLRTProtocol::removeSapFromQueueMatchCompare( UnboundSapPort * element, const UMLRTCommsPort * port )
+{
+ return element->port == port;
+}
+
+/*static*/ void UMLRTProtocol::removeSapFromQueueMatchNotify( UnboundSapPort * element, const UMLRTCommsPort * port )
+{
+ delete element;
+}
+
+/*static*/ void UMLRTProtocol::debugOutputPortToNameMap()
+{
+ // Assumes RTS lock obtained.
+ BDEBUG(BD_MODEL, "SAP/SPP Port to Service Name Registration: { <slot>, <port>, <service name>, <SAP/SPP>}\n");
+
+ UMLRTHashMap::Iterator iter = getPortToNameMap()->getIterator();
+
+ if (iter == iter.end())
+ {
+ BDEBUG(BD_MODEL, " No registered SAP/SPP ports.\n");
+ }
+ else
+ {
+ while (iter != iter.end())
+ {
+ const UMLRTCommsPort * port = (const UMLRTCommsPort *)iter.getKey();
+ const char * service = (const char *)iter.getObject();
+ BDEBUG(BD_MODEL, " { %s, %s, %s, %s }\n",
+ port->slot->name,
+ port->role()->name,
+ port->registeredName == NULL ? "???" : port->registeredName,
+ port->spp ? "SPP" : (port->sap ? "SAP" : "???"));
+
+ if (strcmp(service, port->registeredName))
+ {
+ FATAL("slot %s port %s registered name %s != port-to-name-map service %s",
+ port->slot->name,
+ port->role()->name,
+ port->registeredName,
+ service );
+ }
+ iter = iter.next();
+ }
+ }
+}
+
+/*static*/ void UMLRTProtocol::debugOutputNameToSppPortMap()
+{
+ // Assumes RTS lock obtained.
+ BDEBUG(BD_MODEL, "Service Name to SPP Port: { <service name>, <slot>, <port> }\n");
+
+ UMLRTHashMap::Iterator iter = getNameToSppPortMap()->getIterator();
+
+ if (iter == iter.end())
+ {
+ BDEBUG(BD_MODEL, " No registered SPP ports.\n");
+ }
+ else
+ {
+ while (iter != iter.end())
+ {
+ const UMLRTCommsPort * port = (const UMLRTCommsPort *)iter.getObject();
+ const char * service = (const char *)iter.getKey();
+ BDEBUG(BD_MODEL, " { %s, %s, %s }\n",
+ service,
+ port->slot->name,
+ port->role()->name);
+ if (strcmp(service, port->registeredName))
+ {
+ FATAL("slot %s port %s registered name %s != name-to-spp-port-map service %s",
+ port->slot->name,
+ port->role()->name,
+ port->registeredName,
+ service );
+ }
+ iter = iter.next();
+ }
+ }
+}
+
+/*static*/ bool UMLRTProtocol::debugOutputQueuedSap( UnboundSapPort * unboundSapPort, const char * service )
+{
+ // Assumes RTS lock obtained.
+ const UMLRTCommsPort * port = unboundSapPort->port;
+
+ if (port == NULL)
+ {
+ FATAL("found NULL port in nameToUnboundSapPortsMap queue");
+ }
+
+ BDEBUG(BD_MODEL, " { %s, %s }\n", port->slot->name, port->role()->name);
+
+ if (strcmp(service, port->registeredName))
+ {
+ FATAL("slot %s port %s registered name %s != unbound-sap-ports-map service %s",
+ port->slot->name,
+ port->role()->name,
+ port->registeredName,
+ service );
+ }
+ return false; // Never abort.
+}
+
+/*static*/ void UMLRTProtocol::debugOutputNameToUnboundSapPortsMap()
+{
+ // Assumes RTS lock obtained.
+ BDEBUG(BD_MODEL, "Service Name to unbound SAP Ports:\n");
+
+ UMLRTHashMap::Iterator iter = getNameToUnboundSapPortsMap()->getIterator();
+
+ if (iter == iter.end())
+ {
+ BDEBUG(BD_MODEL, " No unbound SAP ports.\n");
+ }
+ else
+ {
+ while (iter != iter.end())
+ {
+ const char * service = (const char *)iter.getKey();
+ UMLRTQueue * unboundSapPorts = (UMLRTQueue *)iter.getObject();
+ BDEBUG(BD_MODEL, " service '%s' queued SAP ports: { <slot>, <port> }\n", service );
+ if (!unboundSapPorts->walk((UMLRTQueue::walk_callback_t)debugOutputQueuedSap, (void *)service))
+ {
+ BDEBUG(BD_MODEL, " No queued SAP ports.\n");
+ }
+ iter = iter.next();
+ }
+ }
+}
+
+/*static*/ void UMLRTProtocol::debugOutputServiceRegistration()
+{
+ // Assumes RTS lock obtained.
+ debugOutputPortToNameMap();
+ debugOutputNameToSppPortMap();
+ debugOutputNameToUnboundSapPortsMap();
+}
diff --git a/rts/umlrt/umlrtqueue.cc b/rts/umlrt/umlrtqueue.cc
new file mode 100644
index 0000000..c591959
--- /dev/null
+++ b/rts/umlrt/umlrtqueue.cc
@@ -0,0 +1,223 @@
+// umlrtqueue.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include "basefatal.hh"
+#include "basedebug.hh"
+#include "umlrtguard.hh"
+#include "umlrtqueue.hh"
+#include "umlrtqueueelement.hh"
+
+// See umlrtqueue.hh for documentation.
+
+// Create an empty queue.
+UMLRTQueue::UMLRTQueue() : head(0), tail(0), qid(0) {}
+
+// Queue creation can (optionally) add elements.
+UMLRTQueue::UMLRTQueue( UMLRTQueueElement * first, size_t arraySize, size_t elementSize) : head(0), tail(0), qid(0)
+{
+ if (arraySize)
+ {
+ head = first;
+
+ uint8_t * p = (uint8_t *)first;
+
+ tail = (UMLRTQueueElement *)(p + elementSize*(arraySize-1));
+
+ // Need to do pointer arithmetic to point to elements
+ UMLRTQueueElement * element = (UMLRTQueueElement *)p;
+ for (size_t i = 0; i < arraySize; ++i)
+ {
+ memset(p, 0, elementSize); // In case elements were not from BSS.
+ p += elementSize;
+ element->next = (UMLRTQueueElement *)p;
+ element->qid = qid++;
+ element = (UMLRTQueueElement *)p;
+
+ }
+ tail->next = NULL; // Above loop left tail->next pointing to first byte after pool.
+ }
+}
+
+// Remove the first element on the queue.
+const UMLRTQueueElement * UMLRTQueue::dequeue()
+{
+ UMLRTGuard g( mutex );
+ const UMLRTQueueElement * element = head;
+ if (element)
+ {
+ head = element->next;
+ }
+ return element;
+}
+
+// Add element on the tail of the queue.
+void UMLRTQueue::enqueue( const UMLRTQueueElement * element, bool front )
+{
+ UMLRTGuard g( mutex );
+
+ if (!front)
+ {
+ // Queue to tail.
+ element->next = NULL;
+ if (head == NULL)
+ {
+ // Queue was empty.
+ head = tail = element;
+ }
+ else
+ {
+ // Append to tail.
+ tail->next = element;
+ tail = element;
+ }
+ }
+ else
+ {
+ // Queue to front.
+ element->next = head;
+ if (head == NULL)
+ {
+ // Queue was empty.
+ tail = element;
+ }
+ head = element;
+ }
+}
+
+// Return all elements from the queue, still linked, and empty the queue.
+
+// Also returns the tail (as 'last') so the returned elements can be
+// efficiently appended to another queue.
+
+const UMLRTQueueElement * UMLRTQueue::dequeueAll( const UMLRTQueueElement * * last )
+{
+ UMLRTGuard g( mutex );
+
+ const UMLRTQueueElement * all = head;
+
+ *last = tail;
+
+ tail = head = NULL;
+
+ return( all );
+}
+
+// Append a list of elements (that are already linked together) to the queue.
+
+void UMLRTQueue::enqueueAll( const UMLRTQueueElement * all, const UMLRTQueueElement * last )
+{
+ UMLRTGuard g( mutex );
+
+ if (all)
+ {
+ if (!last)
+ {
+ FATAL("all != 0, last == 0");
+ }
+ if (!head)
+ {
+ // This queue was empty - input list is the new queue.
+ head = all;
+ tail = last;
+ }
+ else
+ {
+ // Append input list to the tail of this queue.
+ tail->next = all;
+ tail = last;
+ }
+ }
+}
+
+int UMLRTQueue::remove( match_compare_t compare, match_notify_t notify, void * userData, bool one )
+{
+ int count = 0;
+
+ UMLRTGuard g( mutex );
+
+ // Start at the head and delete until the head no longer needs deleting.
+ const UMLRTQueueElement * headcopy;
+ bool done = false;
+ bool notified = false;
+ while (((headcopy = head) != NULL) && !done)
+ {
+ if (compare(head, userData))
+ {
+ // This head has to go. Unlink it - we've remembered it for notify.
+ head = head->next;
+
+ notify(headcopy, userData);
+
+ notified = true;
+ ++count;
+ if (one)
+ {
+ done = true;
+ }
+ }
+ else
+ {
+ done = true;
+ }
+ }
+ // Head is either NULL or not to be purged (a 'keeper')- we've checked it above.
+ // Go through queue and potentially delete the ones after the last 'keeper'.
+ done = (one && notified);
+
+ if (head != NULL && !done)
+ {
+ const UMLRTQueueElement * keeper = head;
+ const UMLRTQueueElement * candidate;
+
+ while ((candidate = keeper->next) != NULL)
+ {
+ // See if this candidate needs removal.
+ if (compare(candidate, userData))
+ {
+ // Unlink candidate from queue.
+ keeper->next = candidate->next;
+
+ // Notify caller that we've removed it.
+ notify(candidate, userData);
+ ++count;
+ if (one)
+ {
+ done = true;
+ }
+ }
+ else
+ {
+ // Didn't delete candidate. It's the next 'keeper'.
+ keeper = candidate;
+ }
+ }
+ tail = keeper;
+ }
+ return count;
+}
+
+int UMLRTQueue::walk( walk_callback_t callback, void * userData ) const
+{
+ UMLRTGuard g( mutex );
+
+ int count = 0;
+
+ const UMLRTQueueElement * element = head;
+
+ while (element != NULL)
+ {
+ callback( element, userData );
+ count++;
+ element = element->next;
+ }
+ return count;
+}
diff --git a/rts/umlrt/umlrtrtsinterfaceumlrt.cc b/rts/umlrt/umlrtrtsinterfaceumlrt.cc
new file mode 100644
index 0000000..a4773c5
--- /dev/null
+++ b/rts/umlrt/umlrtrtsinterfaceumlrt.cc
@@ -0,0 +1,57 @@
+// umlrtrtsinterfaceumlrt.cc
+
+/*******************************************************************************
+* Copyright (c) 2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtcommsport.hh"
+#include "umlrtcommsportfarend.hh"
+#include "umlrtframeservice.hh"
+#include "umlrtrtsinterfaceumlrt.hh"
+
+void UMLRTRtsInterfaceUmlrt::connectPorts ( const UMLRTCommsPort * p1, size_t p1Index, const UMLRTCommsPort * p2, size_t p2Index ) const
+{
+ UMLRTFrameService::connectPorts(p1, p1Index, p2, p2Index);
+}
+
+void UMLRTRtsInterfaceUmlrt::connectRelayPort ( const UMLRTCommsPort * relayPort, size_t relayIndex, const UMLRTCommsPort * destPort, size_t destIndex ) const
+{
+ UMLRTFrameService::connectRelayPort(relayPort, relayIndex, destPort, destIndex);
+}
+
+const UMLRTCommsPort * * UMLRTRtsInterfaceUmlrt::createBorderPorts ( UMLRTSlot * slot, size_t numPorts ) const
+{
+ return UMLRTFrameService::createBorderPorts(slot, numPorts);
+}
+
+const UMLRTCommsPort * UMLRTRtsInterfaceUmlrt::createInternalPorts ( UMLRTSlot * slot, const UMLRTCapsuleClass * capsuleClass, size_t numPortRoles, const UMLRTCommsPortRole portRoles[] ) const
+{
+ return UMLRTFrameService::createPorts(slot, capsuleClass, numPortRoles, portRoles, false/*border*/);
+}
+
+void UMLRTRtsInterfaceUmlrt::bindPort ( const UMLRTCommsPort * port, int index ) const
+{
+ UMLRTFrameService::sendBoundUnbound(port, index, port->farEnds[index].port, port->farEnds[index].farEndIndex, true/*isBind*/);
+ UMLRTFrameService::sendBoundUnbound(port->farEnds[index].port, port->farEnds[index].farEndIndex, port, index, true/*isBind*/);
+}
+
+void UMLRTRtsInterfaceUmlrt::bindSubcapsulePort ( bool isBorder, UMLRTCapsule * subcapsule, int portIndex, int farEndIndex ) const
+{
+ UMLRTFrameService::bindSubcapsulePort(isBorder, subcapsule, portIndex, farEndIndex);
+}
+
+void UMLRTRtsInterfaceUmlrt::unbindPort ( const UMLRTCommsPort * port, int index ) const
+{
+ UMLRTFrameService::sendBoundUnbound(port, index, port->farEnds[index].port, port->farEnds[index].farEndIndex, false/*isBind*/);
+ UMLRTFrameService::sendBoundUnbound(port->farEnds[index].port, port->farEnds[index].farEndIndex, port, index, false/*isBind*/);
+ UMLRTFrameService::disconnectPort(port, index);
+}
+
+void UMLRTRtsInterfaceUmlrt::unbindSubcapsulePort ( bool isBorder, UMLRTCapsule * subcapsule, int portIndex, int farEndIndex ) const
+{
+ UMLRTFrameService::unbindSubcapsulePort(isBorder, subcapsule, portIndex, farEndIndex);
+}
diff --git a/rts/umlrt/umlrtsemaphore.cc b/rts/umlrt/umlrtsemaphore.cc
new file mode 100644
index 0000000..7768567
--- /dev/null
+++ b/rts/umlrt/umlrtsemaphore.cc
@@ -0,0 +1,77 @@
+// umlrtmutex.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+
+#include <errno.h>
+#include <stdio.h>
+#include "basefatal.hh"
+#include "umlrtsemaphore.hh"
+#include "umlrttimespec.hh"
+
+// UMLRTSemaphore is platform-independent semaphore.
+
+// Bug 32 tracks the move of this implementation to +/os/linux/ossemaphore.cc
+
+UMLRTSemaphore::UMLRTSemaphore( int value )
+{
+ if (sem_init(&sem, 0/*threads only*/, value) < 0)
+ {
+ FATAL_ERRNO("sem_init");
+ }
+}
+
+UMLRTSemaphore::~UMLRTSemaphore()
+{
+ if (sem_destroy(&sem) < 0)
+ {
+ FATAL_ERRNO("sem_destroy");
+ }
+}
+
+// Wait forever for mutex.
+void UMLRTSemaphore::wait()
+{
+ if (sem_wait(&sem) < 0)
+ {
+ FATAL_ERRNO("sem_wait");
+ }
+}
+
+// Timed - returns non-zero for success, zero for timeout.
+int UMLRTSemaphore::wait( uint32_t msec )
+{
+ struct timespec timeout;
+ int success = !0;
+
+ UMLRTTimespec::timespecAbsAddMsec( &timeout, msec );
+ if (sem_timedwait(&sem, &timeout) < 0)
+ {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+ int errno_ = errno;
+#pragma GCC diagnostic pop
+ if (errno_ != ETIMEDOUT)
+ {
+ perror("UMLRTSemaphore wait");
+ FATAL("sem_timedwait (had waited %d msec)", msec);
+ }
+ success = 0;
+ }
+ return success;
+}
+
+// Give mutex back.
+void UMLRTSemaphore::post()
+{
+ if (sem_post(&sem) < 0)
+ {
+ FATAL_ERRNO("sem_wait");
+ }
+}
diff --git a/rts/umlrt/umlrtsignal.cc b/rts/umlrt/umlrtsignal.cc
new file mode 100644
index 0000000..e874918
--- /dev/null
+++ b/rts/umlrt/umlrtsignal.cc
@@ -0,0 +1,169 @@
+// umlrtsignal.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include <stdlib.h>
+#include "umlrtapi.hh"
+#include "umlrtcommsport.hh"
+#include "umlrtcommsportfarend.hh"
+#include "umlrtcontroller.hh"
+#include "umlrtframeservice.hh"
+#include "umlrtsignal.hh"
+#include "umlrtsignalelement.hh"
+#include "umlrtuserconfig.hh"
+#include "basefatal.hh"
+#include "basedebugtype.hh"
+#include "basedebug.hh"
+
+// See umlrtsignal.hh for documentation.
+
+// No signal element by default - equivalent to the 'invalid signal'.
+// An element is allocated when signal is 'initialized'.
+UMLRTSignal::UMLRTSignal() : element(NULL)
+{
+}
+
+UMLRTSignal::~UMLRTSignal()
+{
+ if (element)
+ {
+ element->decrementRefCount();
+ }
+}
+
+UMLRTSignal::UMLRTSignal( const UMLRTSignal &signal ) : element(signal.element)
+{
+ if (element)
+ {
+ element->incrementRefCount();
+ }
+}
+
+UMLRTSignal &UMLRTSignal::operator=( const UMLRTSignal &signal )
+{
+ if (&signal != this)
+ {
+ if (element)
+ {
+ element->decrementRefCount();
+ }
+ if (signal.element)
+ {
+ signal.element->incrementRefCount();
+ }
+ element = signal.element;
+ }
+ return *this;
+}
+
+bool UMLRTSignal::initialize( Id id, const UMLRTCommsPort * srcPort, size_t payloadSize, UMLRTSignalElement::Priority priority )
+{
+ BDEBUG(BD_SIGNAL, "initialize signal-qid[%d] id %d for slot %s port %s payloadSize %d\n",
+ getQid(),
+ id,
+ (srcPort != NULL) ? srcPort->slot->name : "(slot unknown)",
+ (srcPort != NULL) ? srcPort->role()->name : "(no source port)",
+ payloadSize);
+
+ // Initialize for a wired port. If the srcPort is 'the unbound port', there is no need for a signal element with a payload
+ // as nothing will be sent. The current protocol code pattern requires the element with a payload, so we allocate one.
+ // When Bug 231 is resolved, we can avoid allocating an element and payload when the srcPort is 'the unbound port'.
+ if (element)
+ {
+ FATAL("initializing signal that already has an element signal-qid[%d] allocated.", element->qid);
+ }
+ element = umlrt::SignalElementGetFromPool();
+
+ if (!element)
+ {
+ FATAL("failed to allocate signal element");
+ }
+ element->initialize( id, srcPort, payloadSize, priority );
+
+ if (srcPort == NULL)
+ {
+ FATAL("initializing a signal that has no srcPort - should at least be the 'unbound port'");
+ }
+ return !srcPort->unbound;
+}
+
+// Basic initialization. Currently used for initialize message to capsules.
+void UMLRTSignal::initialize( size_t payloadSize, UMLRTSignalElement::Priority priority )
+{
+ if (element)
+ {
+ FATAL("initializing signal that already has an element allocated.");
+ }
+ element = umlrt::SignalElementGetFromPool();
+
+ if (!element)
+ {
+ FATAL("failed to allocate signal element");
+ }
+ BDEBUG(BD_SIGNAL, "allocate no id, srcPort or payloadSize\n");
+
+ element->initialize( payloadSize, priority );
+}
+
+// Basic initialization. Currently used for in-signals.
+void UMLRTSignal::initialize( Id id, const UMLRTCommsPort * srcPort )
+{
+ if (element)
+ {
+ FATAL("initializing signal that already has an element allocated.");
+ }
+ element = umlrt::SignalElementGetFromPool();
+
+ if (!element)
+ {
+ FATAL("failed to allocate signal element");
+ }
+ BDEBUG(BD_SIGNAL, "allocate no id, srcPort or payloadSize\n");
+
+ element->initialize(id, srcPort);
+}
+
+
+// Get the payload buffer.
+uint8_t *UMLRTSignal::getPayload() const
+{
+ if (!element)
+ {
+ return NULL;
+ }
+ return element->getPayload();
+}
+
+// Get the payload buffer.
+size_t UMLRTSignal::getPayloadSize() const
+{
+ if (!element)
+ {
+ return 0;
+ }
+ return element->getPayloadSize();
+}
+
+const UMLRTCommsPort * UMLRTSignal::getSrcPort() const
+{
+ if (!element)
+ {
+ return NULL;
+ }
+ return element->getSrcPort();
+}
+
+UMLRTSignal::Id UMLRTSignal::getId() const
+{
+ if (!element)
+ {
+ return -1; // Another example of using '-1' when we don't have a signal id.
+ }
+ return element->getId();
+}
diff --git a/rts/umlrt/umlrtsignalelement.cc b/rts/umlrt/umlrtsignalelement.cc
new file mode 100644
index 0000000..3b4f879
--- /dev/null
+++ b/rts/umlrt/umlrtsignalelement.cc
@@ -0,0 +1,229 @@
+// umlrtsignalelement.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include <stdlib.h>
+#include "basedebug.hh"
+#include "basedebugtype.hh"
+#include "basefatal.hh"
+#include "umlrtapi.hh"
+#include "umlrtslot.hh"
+#include "umlrtcommsportrole.hh"
+#include "umlrtguard.hh"
+#include "umlrtobjectclass.hh"
+#include "umlrtsignalelement.hh"
+#include "umlrtuserconfig.hh"
+
+// See umlrtsignalelement.hh for documentation.
+
+UMLRTSignalElement::UMLRTSignalElement() : id(0), srcPort(0), defaultPayload(0), payload(0), priority(PRIORITY_NORMAL), appPayloadSize(0),
+ maxPayloadSize(USER_CONFIG_SIGNAL_DEFAULT_PAYLOAD_SIZE), nonDefaultPayload(false), allocated(false), refCount(0)
+{
+ // DEFAULT PAYLOAD SIZE IS A CONSTANT HERE, BUT HAS TO BE OBTAINED AT RUN-TIME.
+ if (!(defaultPayload = payload = (uint8_t*)malloc(USER_CONFIG_SIGNAL_DEFAULT_PAYLOAD_SIZE)))
+ {
+ FATAL("(%p) payload malloc(%d)",this, USER_CONFIG_SIGNAL_DEFAULT_PAYLOAD_SIZE);
+ }
+ BDEBUG(BD_SIGNALINIT, "(%p) signal element payload(%p)\n", this, payload);
+}
+
+// Initialize a signal from the pool and make sure the payload is large enough.
+
+void UMLRTSignalElement::initialize( size_t payloadSize_, Priority priority_ )
+{
+ BDEBUG(BD_SIGNAL, "[%p] initialize no id, srcPort or payloadSize\n", this);
+
+ srcPort = NULL; // No srcPort by default.
+
+ id = -1; // The signal id of '-1' used for 'uninitialized id value'.
+ priority = priority_;
+
+ if (refCount)
+ {
+ FATAL("(%p) signal element alloc refcount non-zero(%d)", this, refCount);
+ }
+ // If the user is requesting more space than is available in the
+ // default payload buffer, allocate it here.
+
+ // THIS CONSTANT MAY HAVE TO BE OBTAINED AT RUN-TIME FROM THE
+ // GENERATED USER-CONFIGURATION PARAMETERS, IF THE VALUES FROM umlrtapi.hh ARE NOT USED.
+ if (payloadSize_ > USER_CONFIG_SIGNAL_DEFAULT_PAYLOAD_SIZE)
+ {
+ if (!(payload = (uint8_t *)malloc(payloadSize_)))
+ {
+ FATAL("(%p) non-default payload malloc(%d)", this, payloadSize_ );
+ }
+ nonDefaultPayload = true;
+ maxPayloadSize = payloadSize_;
+ }
+ appPayloadSize = payloadSize_;
+
+ // Initialize encoding.
+ encodeInfo = payload;
+
+ incrementRefCount();
+}
+// Initialize a signal from the pool and make sure the payload is large enough.
+
+void UMLRTSignalElement::initialize( Id id_, const UMLRTCommsPort * srcPort_, size_t payloadSize_, Priority priority_ )
+{
+ if (!srcPort_)
+ {
+ SWERR("(%p) signal allocate src port == 0.", this);
+ }
+
+ initialize(payloadSize_); // Default initialization.
+
+ id = id_;
+ srcPort = srcPort_;
+ priority = priority_;
+
+ BDEBUG(BD_SIGNAL, "[%p] initialize id(%d) %s(%s) appPayloadSize(%d) maxPayloadSize(%d)\n",
+ this, id,
+ (srcPort_ != NULL) ? srcPort->slot->name : "(NULL src port)",
+ (srcPort_ != NULL) ? srcPort->role()->name : "(NULL src port)",
+ appPayloadSize,
+ maxPayloadSize);
+}
+
+void UMLRTSignalElement::initialize( Id id_, const UMLRTCommsPort * srcPort_ )
+{
+ if (!srcPort_)
+ {
+ SWERR("(%p) signal allocate src port == 0.", this);
+ }
+
+ initialize(0); // Default initialization.
+
+ id = id_;
+ srcPort = srcPort_;
+
+ BDEBUG(BD_SIGNAL, "[%p] initialize id(%d) %s(%s) appPayloadSize(%d) maxPayloadSize(%d)\n",
+ this, id,
+ (srcPort_ != NULL) ? srcPort->slot->name : "(NULL src port)",
+ (srcPort_ != NULL) ? srcPort->role()->name : "(NULL src port)",
+ appPayloadSize,
+ maxPayloadSize);
+}
+
+// Free a signal element (return to the pool).
+
+/*static*/ void UMLRTSignalElement::dealloc( UMLRTSignalElement * element )
+{
+ if (!element)
+ {
+ FATAL("element NULL");
+ }
+ BDEBUG(BD_SIGNAL, "[%p] deallocate id(%d) srcPort(%s) appPayloadSize(%d) maxPayloadSize(%d)\n",
+ element, element->id, element->srcPort ? element->srcPort->role()->name : "NONE", element->appPayloadSize, element->maxPayloadSize);
+
+ if (element->refCount)
+ {
+ FATAL("(%p) signal element dealloc refcount non-zero(%d)", element, element->refCount);
+ }
+
+ if (element->nonDefaultPayload)
+ {
+ // Return non-default payload.
+ free(element->payload);
+
+ // Restore default payload.
+ element->payload = element->defaultPayload;
+ element->maxPayloadSize = USER_CONFIG_SIGNAL_DEFAULT_PAYLOAD_SIZE;
+ element->appPayloadSize = 0; // Should be unused while deallocated.
+ }
+ umlrt::SignalElementPutToPool(element);
+}
+
+void UMLRTSignalElement::incrementRefCount() const
+{
+ BDEBUG(BD_SIGNALREF, "(%p) signal element inc ref count(%d)\n", this, refCount);
+
+ UMLRTGuard g(refCountMutex);
+
+ ++refCount;
+}
+
+void UMLRTSignalElement::decrementRefCount() const
+{
+ BDEBUG(BD_SIGNALREF, "(%p) signal element dec ref count(%d)\n", this, refCount);
+
+ UMLRTGuard g(refCountMutex);
+ if (refCount <= 0)
+ {
+ FATAL("(%p) signal element dec ref count that is already zero id(%d)", this, id);
+ }
+ else if (!(--refCount))
+ {
+ BDEBUG(BD_SIGNALREF, "(%p) signal element dec ref count == 0 dealloc\n", this);
+
+ UMLRTSignalElement::dealloc(const_cast<UMLRTSignalElement *>(this));
+ }
+}
+
+// See documentation in UMLRTSignal.
+void UMLRTSignalElement::encode( const UMLRTObject_class * desc, const void * data, int arraySize )
+{
+ // encodeInfo is intitialized to be 'payload' when the signal is initialized.
+ // It is moved forward during successive #encode calls.
+ for (int i = 0; i < arraySize; ++i)
+ {
+ encodeInfo = desc->encode(desc, data, encodeInfo);
+ }
+}
+
+// See documentation in UMLRTSignal.
+void UMLRTSignalElement::encode( const UMLRTObject_class * desc, int ptrIndirection, const void * data, int arraySize )
+{
+ FATAL("encode ptr indirection not implemented.");
+}
+
+// See documentation in UMLRTSignal.
+void UMLRTSignalElement::decode( void * * decodeInfo, const UMLRTObject_class * desc, void * data, int arraySize )
+{
+ // decodeInfo is initialized to be NULL by a decoder - it is owned by the decoder and not by
+ // the signal (contrary to the encodeInfo, which can be owned by the signal, since there is always
+ // only ever one encoder).
+
+ if (!(*decodeInfo))
+ {
+ *decodeInfo = payload;
+ }
+ for (int i = 0; i < arraySize; ++i)
+ {
+ *decodeInfo = desc->decode(desc, *decodeInfo, data);
+ }
+}
+
+// See documentation in UMLRTSignal.
+void UMLRTSignalElement::decode( void * * decodeInfo, const UMLRTObject_class * desc, int ptrIndirection, void * data, int arraySize )
+{
+ FATAL("decode ptr indirection not implemented.");
+}
+
+void UMLRTSignalElement::setAllocated( bool allocated_ )
+{
+ if (allocated_)
+ {
+ if (allocated)
+ {
+ FATAL("signal-qid[%d] was already allocated", qid);
+ }
+ allocated = true;
+ }
+ else
+ {
+ if (!allocated)
+ {
+ FATAL("signal-qid[%d] was already deallocated", qid);
+ }
+ allocated = false;
+ }
+}
+
diff --git a/rts/umlrt/umlrtsignalelementpool.cc b/rts/umlrt/umlrtsignalelementpool.cc
new file mode 100644
index 0000000..cc6661e
--- /dev/null
+++ b/rts/umlrt/umlrtsignalelementpool.cc
@@ -0,0 +1,51 @@
+// umlrtsignalelementpool.cc
+
+/*******************************************************************************
+ * Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+// See umlrtsignalpool.hh for documentation.
+#include <stdlib.h>
+#include "basefatal.hh"
+#include "umlrtguard.hh"
+#include "umlrtsignalelementpool.hh"
+
+UMLRTSignalElementPool::UMLRTSignalElementPool(UMLRTSignalElement signalElements[],
+ size_t arraySize, size_t incrementSize) :
+ UMLRTPool(incrementSize)
+{
+ UMLRTGuard g(mutex);
+
+ if (arraySize && signalElements != NULL)
+ {
+ for (size_t i = arraySize - 1; i > 0; --i)
+ {
+ signalElements[i - 1].next = &signalElements[i];
+ signalElements[i - 1].qid = qid++;
+ }
+ head = &signalElements[0];
+ }
+}
+
+UMLRTSignalElementPool::UMLRTSignalElementPool(size_t incrementSize) :
+ UMLRTPool(incrementSize)
+{
+
+}
+
+void UMLRTSignalElementPool::grow()
+{
+ UMLRTSignalElement * newElements = new UMLRTSignalElement[increment]();
+
+ for (size_t i = increment - 1; i > 0; --i)
+ {
+ newElements[i - 1].next = &newElements[i];
+ }
+ newElements[increment - 1].next = head;
+ head = &newElements[0];
+}
+
diff --git a/rts/umlrt/umlrttimerid.cc b/rts/umlrt/umlrttimerid.cc
new file mode 100644
index 0000000..ff414a9
--- /dev/null
+++ b/rts/umlrt/umlrttimerid.cc
@@ -0,0 +1,23 @@
+// umlrttimerid.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrttimer.hh"
+#include "umlrttimerid.hh"
+#include "basefatal.hh"
+
+// Must have previously confirmed the timer #isValid.
+const UMLRTTimer * UMLRTTimerId::getTimer() const
+{
+ if (!timer)
+ {
+ FATAL("timer is invalid");
+ }
+ return timer;
+}
diff --git a/rts/umlrt/umlrttimerpool.cc b/rts/umlrt/umlrttimerpool.cc
new file mode 100644
index 0000000..bf43bad
--- /dev/null
+++ b/rts/umlrt/umlrttimerpool.cc
@@ -0,0 +1,50 @@
+// umlrttimerpool.cc
+
+/*******************************************************************************
+ * Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+// See umlrttimer.hh for documentation.
+#include <stdlib.h>
+#include "basefatal.hh"
+#include "umlrttimerpool.hh"
+#include "umlrtguard.hh"
+
+UMLRTTimerPool::UMLRTTimerPool(UMLRTTimer timerElements[], size_t arraySize, size_t incrementSize) :
+ UMLRTPool(incrementSize)
+{
+ UMLRTGuard g(mutex);
+
+ if (arraySize && timerElements != NULL)
+ {
+ for (size_t i = arraySize - 1; i > 0; --i)
+ {
+ timerElements[i - 1].next = &timerElements[i];
+ timerElements[i - 1].qid = qid++;
+ }
+ head = &timerElements[0];
+ }
+}
+
+UMLRTTimerPool::UMLRTTimerPool(size_t incrementSize) :
+ UMLRTPool(incrementSize)
+{
+
+}
+
+void UMLRTTimerPool::grow()
+{
+ UMLRTTimer * newElements = new UMLRTTimer[increment]();
+
+ for (size_t i = increment - 1; i > 0; --i)
+ {
+ newElements[i - 1].next = &newElements[i];
+ }
+ newElements[increment - 1].next = head;
+ head = &newElements[0];
+}
+
diff --git a/rts/umlrt/umlrttimerprotocol.cc b/rts/umlrt/umlrttimerprotocol.cc
new file mode 100644
index 0000000..6c7e293
--- /dev/null
+++ b/rts/umlrt/umlrttimerprotocol.cc
@@ -0,0 +1,204 @@
+// umlrttimerprotocol.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrtslot.hh"
+#include "umlrtcommsport.hh"
+#include "umlrtcommsportrole.hh"
+#include "umlrtcontroller.hh"
+#include "umlrttimerprotocol.hh"
+#include "umlrttimer.hh"
+#include "umlrttimerid.hh"
+#include "umlrtapi.hh"
+#include "basefatal.hh"
+#include "basedebugtype.hh"
+#include "basedebug.hh"
+#include <stdlib.h>
+
+// Protocol implementation for timer ports.
+
+// Allocate a timer.
+/*static*/ UMLRTTimerId UMLRTTimerProtocol::allocateTimer( const UMLRTCommsPort * srcPort, bool isRelative, bool isInterval, const UMLRTTimespec & due,
+ UMLRTSignalElement::Priority priority, const void * userData, const UMLRTObject_class * type )
+{
+ UMLRTTimer * timer = umlrt::TimerGetFromPool();
+
+ if (!timer)
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_TIMER_GET);
+ }
+ else if (!timer->allocated)
+ {
+ FATAL("allocated timer not deemed allocated.");
+ }
+ else
+ {
+ if (isRelative)
+ {
+ UMLRTTimespec now;
+ UMLRTTimespec::getClock(&now);
+ timer->due = now + due;
+ }
+ else
+ {
+ timer->due = due;
+ }
+ timer->isInterval = isInterval;
+ if (isInterval)
+ {
+ timer->interval = due;
+ }
+ timer->destPort = srcPort;
+ timer->destSlot = srcPort->slot;
+
+ size_t payloadSize = 0;
+ if (userData)
+ {
+ if (!type)
+ {
+ FATAL("user data requires a type descriptor");
+ }
+ payloadSize = type->getSize(type);
+ }
+ timer->signal.initialize(UMLRTTimerProtocol::signal_timeout, srcPort, payloadSize, priority);
+
+ char buf[UMLRTTimespec::TIMESPEC_TOSTRING_SZ];
+
+ BDEBUG(BD_TIMER, "allocate timer(%p) destPort(%s) isInterval(%d) priority(%d) payloadSize(%d) due(%s)\n",
+ timer, timer->destPort->role()->name, timer->isInterval, timer->signal.getPriority(), timer->signal.getPayloadSize(),
+ timer->isInterval ? timer->due.toStringRelative(buf, sizeof(buf)) : timer->due.toString(buf, sizeof(buf)));
+
+ if (userData)
+ {
+ timer->signal.encode(userData, type);
+ }
+ }
+ return UMLRTTimerId(timer);
+}
+
+const UMLRTTimerId UMLRTTimerProtocol::OutSignals::informAt( const UMLRTCommsPort * srcPort, const UMLRTTimespec & clockTime, UMLRTSignalElement::Priority priority ) const
+{
+ UMLRTTimerId id = allocateTimer(srcPort, false, false, clockTime, priority, NULL, NULL);
+
+ if (id.isValid())
+ {
+ // Pass the timer to the controller to monitor it.
+ srcPort->slot->controller->startTimer(id.getTimer());
+ }
+ return id;
+}
+const UMLRTTimerId UMLRTTimerProtocol::OutSignals::informAt( const UMLRTCommsPort * srcPort, const UMLRTTimespec & clockTime, const void * userData, const UMLRTObject_class * type, UMLRTSignalElement::Priority priority ) const
+{
+ UMLRTTimerId id = allocateTimer(srcPort, false, false, clockTime, priority, userData, type);
+
+ if (id.isValid())
+ {
+ // Pass the timer to the controller to monitor it.
+ srcPort->slot->controller->startTimer(id.getTimer());
+ }
+ return id;
+}
+const UMLRTTimerId UMLRTTimerProtocol::OutSignals::informAt( const UMLRTCommsPort * srcPort, const UMLRTTimespec & clockTime, const UMLRTTypedValue & typedValue, UMLRTSignalElement::Priority priority ) const
+{
+ UMLRTTimerId id = allocateTimer(srcPort, false, false, clockTime, priority, typedValue.data, typedValue.type);
+
+ if (id.isValid())
+ {
+ // Pass the timer to the controller to monitor it.
+ srcPort->slot->controller->startTimer(id.getTimer());
+ }
+ return id;
+}
+
+const UMLRTTimerId UMLRTTimerProtocol::OutSignals::informIn( const UMLRTCommsPort * srcPort, const UMLRTTimespec & relativeTime, UMLRTSignalElement::Priority priority ) const
+{
+ UMLRTTimerId id = allocateTimer(srcPort, true, false, relativeTime, priority, NULL, NULL);
+
+ if (id.isValid())
+ {
+ // Pass the timer to the controller to monitor it.
+ srcPort->slot->controller->startTimer(id.getTimer());
+ }
+ return id;
+}
+const UMLRTTimerId UMLRTTimerProtocol::OutSignals::informIn( const UMLRTCommsPort * srcPort, const UMLRTTimespec & relativeTime, void * userData, const UMLRTObject_class * type, UMLRTSignalElement::Priority priority ) const
+{
+ UMLRTTimerId id = allocateTimer(srcPort, true, false, relativeTime, priority, userData, type);
+
+ if (id.isValid())
+ {
+ // Pass the timer to the controller to monitor it.
+ srcPort->slot->controller->startTimer(id.getTimer());
+ }
+ return id;
+}
+const UMLRTTimerId UMLRTTimerProtocol::OutSignals::informIn( const UMLRTCommsPort * srcPort, const UMLRTTimespec & relativeTime, const UMLRTTypedValue & typedValue, UMLRTSignalElement::Priority priority ) const
+{
+ UMLRTTimerId id = allocateTimer(srcPort, true, false, relativeTime, priority, typedValue.data, typedValue.type);
+
+ if (id.isValid())
+ {
+ // Pass the timer to the controller to monitor it.
+ srcPort->slot->controller->startTimer(id.getTimer());
+ }
+ return id;
+}
+
+const UMLRTTimerId UMLRTTimerProtocol::OutSignals::informEvery( const UMLRTCommsPort * srcPort, const UMLRTTimespec & relativeTime, UMLRTSignalElement::Priority priority ) const
+{
+ UMLRTTimerId id = allocateTimer(srcPort, true, true, relativeTime, priority, NULL, NULL);
+
+ if (id.isValid())
+ {
+ // Pass the timer to the controller to monitor it.
+ srcPort->slot->controller->startTimer(id.getTimer());
+ }
+ return id;
+}
+const UMLRTTimerId UMLRTTimerProtocol::OutSignals::informEvery( const UMLRTCommsPort * srcPort, const UMLRTTimespec & relativeTime, void * userData, const UMLRTObject_class * type, UMLRTSignalElement::Priority priority ) const
+{
+ UMLRTTimerId id = allocateTimer(srcPort, true, true, relativeTime, priority, userData, type);
+
+ if (id.isValid())
+ {
+ // Pass the timer to the controller to monitor it.
+ srcPort->slot->controller->startTimer(id.getTimer());
+ }
+ return id;
+}
+const UMLRTTimerId UMLRTTimerProtocol::OutSignals::informEvery( const UMLRTCommsPort * srcPort, const UMLRTTimespec & relativeTime, const UMLRTTypedValue & typedValue, UMLRTSignalElement::Priority priority ) const
+{
+ UMLRTTimerId id = allocateTimer(srcPort, true, true, relativeTime, priority, typedValue.data, typedValue.type);
+
+ if (id.isValid())
+ {
+ // Pass the timer to the controller to monitor it.
+ srcPort->slot->controller->startTimer(id.getTimer());
+ }
+ return id;
+}
+
+bool UMLRTTimerProtocol::OutSignals::cancelTimer( const UMLRTCommsPort * srcPort, const UMLRTTimerId id ) const
+{
+ if (!id.isValid())
+ {
+ srcPort->slot->controller->setError(UMLRTController::E_TIMER_CANC_INV);
+ return false;
+ }
+ return srcPort->slot->controller->cancelTimer(id);
+}
+
+void UMLRTTimerProtocol::OutSignals::timeAdjustStart( const UMLRTCommsPort * srcPort ) const
+{
+ FATAL("timer adjust start not implemented");
+}
+void UMLRTTimerProtocol::OutSignals::timeAdjustComplete(const UMLRTCommsPort * srcPort, const UMLRTTimespec & delta ) const
+{
+ FATAL("timer adjust complete not implemented");
+}
diff --git a/rts/umlrt/umlrttimerqueue.cc b/rts/umlrt/umlrttimerqueue.cc
new file mode 100644
index 0000000..1160758
--- /dev/null
+++ b/rts/umlrt/umlrttimerqueue.cc
@@ -0,0 +1,273 @@
+// umlrttimerqueue.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "basefatal.hh"
+#include "basedebug.hh"
+#include "umlrtapi.hh"
+#include "umlrtguard.hh"
+#include "umlrttimer.hh"
+#include "umlrttimerqueue.hh"
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+// See umlrttimerqueue.hh for documentation.
+
+UMLRTTimerQueue::UMLRTTimerQueue() : UMLRTQueue()
+{
+ // Appenders write the pipe. Controllers 'wait' on a select() on the pipe with
+ // a timeout associated with any running timer. A write on the pipe wakes the controller.
+ if (pipe(notifyFd) < 0)
+ {
+ FATAL_ERRNO("pipe");
+ }
+ // Bytes available in the pipe wake up the controller as 'notify'. The controller
+ // has to be able to clean out the pipe (read) without blocking.
+ int flags = fcntl(notifyFd[0], F_GETFL, 0);
+ if (flags < 0)
+ {
+ FATAL_ERRNO("fcntl F_GETFL");
+ }
+ if (fcntl(notifyFd[0], F_SETFL, flags | O_NONBLOCK) < 0)
+ {
+ FATAL_ERRNO("fcntl F_SETFL");
+ }
+}
+
+// Remove the first timer on the queue. Returns NULL if first timer has not yet expired.
+UMLRTTimer * UMLRTTimerQueue::dequeue()
+{
+ UMLRTGuard(getMutex());
+
+ UMLRTTimer * first = (UMLRTTimer *)head;
+
+ if (first)
+ {
+ UMLRTTimespec now;
+ UMLRTTimespec::getClock(&now);
+
+ if (now >= first->due)
+ {
+ // First timer is due - dequeue it and return it.
+ head = first->next;
+ if (head == NULL)
+ {
+ tail = NULL; // Not required, but cleaner.
+ }
+ BDEBUG(BD_TIMER, "this(%p) dequeue found first timer due.\n", this);
+ }
+ else
+ {
+ // First timer still running - leave it there.
+ first = NULL;
+ BDEBUG(BD_TIMER, "this(%p) dequeue found first timer still running.\n", this);
+ }
+ }
+ else
+ {
+ BDEBUG(BD_TIMER, "this(%p) dequeue found no timers.\n", this);
+ }
+ return first;
+}
+
+// Add a timer to the queue in the order of when they will expire.
+void UMLRTTimerQueue::enqueue( const UMLRTTimer * timer )
+{
+ UMLRTGuard(getMutex());
+
+ UMLRTTimer * next = (UMLRTTimer *)head;
+ UMLRTTimer * previous = NULL;
+
+ char tmbuf[UMLRTTimespec::TIMESPEC_TOSTRING_SZ];
+ BDEBUG(BD_TIMER, "this(%p) timer-enqueue due(%s)\n", this, timer->due.toString(tmbuf,sizeof(tmbuf)));
+
+ timer->next = NULL; // Initialize as last-in-queue.
+
+ // Only need to notify the controller if the new timer ends up at the head of the queue.
+ // The wait mechanism is only interested in the time remaining for the timer at the head of the queue.
+ if (!head)
+ {
+ // List was empty. Put it in there as only element.
+ head = tail = timer;
+ sendNotification();
+ }
+ else
+ {
+ // Skip ahead until we meet a timer due after this one.
+ while (next && (next->due <= timer->due))
+ {
+ previous = next;
+ next = (UMLRTTimer *)next->next;
+ }
+ if (!next)
+ {
+ // We're appending this timer to the end of the queue.
+ tail->next = timer;
+ tail = timer;
+ }
+ else if (!previous)
+ {
+ // This timer is before the first element in the queue - prepend it.
+ timer->next = head;
+ head = timer;
+ sendNotification();
+ }
+ else
+ {
+ // This timer goes after 'previous' and before 'next'.
+ previous->next = timer;
+ timer->next = next;
+ }
+ }
+}
+
+// Calculate how much time left before timer on the head of the queue is due.
+UMLRTTimespec UMLRTTimerQueue::timeRemaining() const
+{
+ UMLRTGuard(getMutex());
+
+ // NOTE: Intended only for the consumer of the queue elements which has confirmed
+ // the queue was non-empty. An alternate implementation is required if an empty queue
+ // is possible.
+ if (isEmpty())
+ {
+ FATAL("timer queue was empty in timeRemaining()");
+ }
+ UMLRTTimespec now;
+ UMLRTTimespec::getClock(&now);
+
+ BDEBUG(BD_TIMER, "this(%p) head(%p) tail(%p)\n", this, head, tail);
+
+ UMLRTTimespec remain = ((UMLRTTimer*)head)->due - now;
+
+ char tmbuf[UMLRTTimespec::TIMESPEC_TOSTRING_SZ];
+ BDEBUG(BD_TIMER, "timeRemaining %s\n", remain.toStringRelative(tmbuf, sizeof(tmbuf)));
+
+ return remain;
+}
+
+// Add a timer to the queue in the order of when they will expire.
+bool UMLRTTimerQueue::cancel( UMLRTTimerId id )
+{
+ UMLRTGuard(getMutex());
+
+ bool ok = false;
+ UMLRTTimer * next = (UMLRTTimer *)head;
+ UMLRTTimer * previous = NULL;
+
+ while (next && (next != id.getTimer()))
+ {
+ previous = next;
+ next = (UMLRTTimer *)next->next;
+ }
+ // Only need to notify the controller if the cancelled timer was at the head of the queue.
+ if (next)
+ {
+ // Found the one to delete.
+ if (!previous)
+ {
+ // This timer was at the head of the queue.
+ head = (UMLRTTimer *)next->next;
+ if (head == NULL)
+ {
+ tail = NULL; // Not strictly required, but cleaner.
+ }
+ sendNotification();
+ }
+ else
+ {
+ // Unlink the timer being deallocated by setting next of previous to be
+ // the cancelled timer's next.
+ previous->next = next->next;
+
+ // If the timer was last in the queue, the tail has to be updated.
+ if (tail == next)
+ {
+ tail = previous;
+ }
+ }
+ // Return it to the pool.
+ umlrt::TimerPutToPool(next);
+
+ ok = true;
+ }
+ return ok;
+}
+
+void UMLRTTimerQueue::sendNotification()
+{
+ int bytes_ready;
+
+ if (ioctl(notifyFd[0], FIONREAD, &bytes_ready) < 0)
+ {
+ FATAL_ERRNO("ioctl");
+ }
+ if (!bytes_ready)
+ {
+ // Write a byte to the notification-pipe as a notification of a pending message.
+ uint8_t notifyByte = 0;
+
+ if (write(notifyFd[1], ¬ifyByte, 1) < 0)
+ {
+ FATAL_ERRNO("write");
+ }
+ }
+}
+
+// See umlrtprioritymessagequeue.hh for documentation.
+void UMLRTTimerQueue::clearNotifyFd()
+{
+ uint8_t ignore;
+ int bytes;
+ if ((bytes = read(notifyFd[0], &ignore, 1)) < 0)
+ {
+ FATAL_ERRNO("initial read - synchronization mechanism implies a byte is waiting");
+ }
+ else if (!bytes)
+ {
+ // Indicates synchronization logic error. Should always get at least one byte.
+ FATAL("Should never find pipe empty when clearing a notification.");
+ }
+ // Clear out the notification-pipe.
+ // Can shrink this after notify is debugged.
+ bool done = false;
+ while (!done)
+ {
+ int bytes = read(notifyFd[0], &ignore, 1);
+ if (bytes < 0)
+ {
+ if (errno != EAGAIN)
+ {
+ FATAL_ERRNO("read");
+ }
+ else
+ {
+ done = true;
+ }
+ }
+ if (bytes > 0)
+ {
+ // BDEBUG(0, "read returned bytes(%d)\n", bytes);
+ }
+ else
+ {
+ done = true;
+ }
+ }
+}
+
+// See umlrtprioritymessagequeue.hh for documentation.
+int UMLRTTimerQueue::getNotifyFd()
+{
+ return notifyFd[0];
+}
+
+
diff --git a/rts/umlrt/umlrttimespec.cc b/rts/umlrt/umlrttimespec.cc
new file mode 100644
index 0000000..9589b82
--- /dev/null
+++ b/rts/umlrt/umlrttimespec.cc
@@ -0,0 +1,205 @@
+// umlrttimerspec.cc
+
+/*******************************************************************************
+* Copyright (c) 2014-2015 Zeligsoft (2009) Limited and others.
+* All rights reserved. This program and the accompanying materials
+* are made available under the terms of the Eclipse Public License v1.0
+* which accompanies this distribution, and is available at
+* http://www.eclipse.org/legal/epl-v10.html
+*******************************************************************************/
+
+#include "umlrttimespec.hh"
+#include "basefatal.hh"
+#include <sys/time.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+UMLRTTimespec::UMLRTTimespec( const UMLRTTimespec & tm ) : tv_sec(tm.tv_sec), tv_nsec(tm.tv_nsec) { }
+
+UMLRTTimespec::UMLRTTimespec( long seconds, long nanoseconds ): tv_sec(seconds), tv_nsec(nanoseconds)
+{
+ timespecAdjust(this);
+}
+
+UMLRTTimespec::UMLRTTimespec() { UMLRTTimespec::getClock(this); }
+
+UMLRTTimespec & UMLRTTimespec::operator=( const UMLRTTimespec & tm )
+{
+ tv_sec = tm.tv_sec;
+ tv_nsec = tm.tv_nsec;
+
+ return *this;
+}
+
+bool UMLRTTimespec::operator==( const UMLRTTimespec & tm ) const
+{
+ return (tv_sec == tm.tv_sec) && (tv_nsec == tm.tv_nsec);
+}
+
+bool UMLRTTimespec::operator!=( const UMLRTTimespec & tm ) const
+{
+ return (tv_sec != tm.tv_sec) || (tv_nsec != tm.tv_nsec);
+}
+
+bool UMLRTTimespec::operator<( const UMLRTTimespec & tm ) const
+{
+ if (tv_sec < tm.tv_sec)
+ return true;
+
+ else if (tv_sec == tm.tv_sec)
+ return (tv_nsec < tm.tv_nsec);
+
+ return false;
+}
+
+bool UMLRTTimespec::operator<=( const UMLRTTimespec & tm ) const
+{
+ if (tv_sec < tm.tv_sec)
+ return true;
+
+ else if (tv_sec == tm.tv_sec)
+ return (tv_nsec <= tm.tv_nsec);
+
+ return false;
+}
+
+bool UMLRTTimespec::operator>( const UMLRTTimespec & tm ) const
+{
+ if (tv_sec > tm.tv_sec)
+ return true;
+
+ else if (tv_sec == tm.tv_sec)
+ return (tv_nsec > tm.tv_nsec);
+
+ return false;
+}
+
+bool UMLRTTimespec::operator>=( const UMLRTTimespec & tm ) const
+{
+ if (tv_sec > tm.tv_sec)
+ return true;
+
+ else if (tv_sec == tm.tv_sec)
+ return (tv_nsec >= tm.tv_nsec);
+
+ return false;
+}
+
+// Ensure tv_nsec is always 0..999999999.
+/*static*/ void UMLRTTimespec::timespecAdjust( UMLRTTimespec * tm )
+{
+ // Times must be adjusted after a single add/subtract, so tv_nsec is assumed to be
+ // between -999999999 and 2*999999999 before it is adjusted.
+ // We do a sanity check after the adjustment to confirm our assumptions are correct.
+ if (tm->tv_nsec > ONE_BILLION)
+ {
+ tm->tv_sec += 1;
+ tm->tv_nsec -= ONE_BILLION;
+ }
+ if (tm->tv_nsec < 0)
+ {
+ tm->tv_sec -= 1;
+ tm->tv_nsec += ONE_BILLION;
+ }
+ // For now, we FATAL() if tm->tv_nsec was not sufficiently adjusted.
+ if (tm->tv_nsec > ONE_BILLION)
+ {
+ FATAL("time tv_nsec(%ld) > ONE_BILLION", tm->tv_nsec);
+ }
+ if (tm->tv_nsec < 0)
+ {
+ FATAL("time tv_nsec(%ld) < 0", tm->tv_nsec);
+ }
+}
+
+UMLRTTimespec UMLRTTimespec::operator+( const UMLRTTimespec & tm ) const
+{
+ return UMLRTTimespec(tv_sec + tm.tv_sec, tv_nsec + tm.tv_nsec);
+}
+
+UMLRTTimespec UMLRTTimespec::operator-( const UMLRTTimespec & tm ) const
+{
+ return UMLRTTimespec(tv_sec - tm.tv_sec, tv_nsec - tm.tv_nsec);
+}
+
+UMLRTTimespec & UMLRTTimespec::operator+=( const UMLRTTimespec & tm )
+{
+ tv_sec += tm.tv_sec;
+ tv_nsec += tm.tv_nsec;
+
+ timespecAdjust(this);
+
+ return *this;
+}
+
+UMLRTTimespec & UMLRTTimespec::operator-=( const UMLRTTimespec & tm )
+{
+ tv_sec -= tm.tv_sec;
+ tv_nsec -= tm.tv_nsec;
+
+ timespecAdjust(this);
+
+ return *this;
+}
+
+/*static*/ void UMLRTTimespec::getClock( UMLRTTimespec * tm )
+{
+ // Linux-specific for now.
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) < 0)
+ {
+ FATAL_ERRNO("gettimeofday");
+ }
+ tm->tv_sec = tv.tv_sec;
+ tm->tv_nsec = tv.tv_usec * 1000;
+}
+
+bool UMLRTTimespec::isZeroOrNegative() const
+{
+ return (tv_sec < 0) || ((tv_sec == 0) && (tv_nsec == 0));
+}
+
+char * UMLRTTimespec::toString( char * buffer, int size ) const
+{
+ struct tm from_localtime;
+ const time_t seconds = tv_sec;
+
+ localtime_r( &seconds, &from_localtime );
+
+ size_t length = strftime(buffer, size, "%Y-%m-%d:%H:%M:%S", &from_localtime);
+ long msec = tv_nsec / NANOSECONDS_PER_MILLISECOND;
+ snprintf( &buffer[length], size-length, ".%03ld", msec );
+
+ return buffer;
+}
+
+char * UMLRTTimespec::toStringRelative( char * buffer, int size ) const
+{
+ long int seconds = tv_sec;
+ long int days = seconds / SECONDS_PER_DAY;
+ seconds -= (days * SECONDS_PER_DAY);
+ long int hours = seconds / SECONDS_PER_HOUR;
+ seconds -= (hours * SECONDS_PER_HOUR);
+ long int minutes = seconds / SECONDS_PER_MINUTE;
+ seconds -= (minutes * SECONDS_PER_MINUTE);
+
+ long int msec = tv_nsec / NANOSECONDS_PER_MILLISECOND;
+
+ snprintf( buffer, size, "%ld:%02ld:%02ld:%02ld.%03ld", days, hours, minutes, seconds, msec );
+
+ return buffer;
+}
+
+/*static*/ void UMLRTTimespec::timespecAbsAddMsec( struct timespec * timeout, long msec )
+{
+ clock_gettime(CLOCK_REALTIME, timeout);
+ timeout->tv_sec += (msec / 1000);
+ timeout->tv_nsec += (msec % 1000) * ONE_BILLION;
+ if (timeout->tv_nsec >= ONE_BILLION)
+ {
+ timeout->tv_sec += 1;
+ timeout->tv_nsec -= ONE_BILLION;
+ }
+}