This chapter has been split into several major sections, including
Much of the configurability of the C TargetRTS is done with compilation dependencies by having target-specific source files override common source files. This is illustrated in "Platform-specific implementation" on page 61. However, configurability is also available within source files using C preprocessor definitions. The configuration is set in two C header files:
$RTS_HOME/target/<target>/RTTarget.h for specifying the operating system specific definitions
$RTS_HOME/libset/<libset>/RTLibSet.h for specifying compiler specific definitions; this does not exist by default
These files override macros whose defaults appear in $RTS_HOME/include/RTConfig.h. The macros and their default values are listed in Table 7.
Some of the TargetRTS configuration is done at the makefile level. When a developer compiles an update, the generated compilation makefile requires two files:
$(RTS_HOME)/target/common.mk1
$(OVERRIDESFILE)
The OVERRIDESFILE can be specified by the developer within the update's active configuration and is typically intended for temporary overrides of make macros. The definition for OVERRIDESFILE is provided in RTUpdate.mk and defaults to $(RTS_HOME)/target/empty.mk, which is itself an empty file.
The makefile fragment common.mk is really intended as a common entry point independent of the value of $(RTS_HOME). It typically includes four other makefile fragments:
$(RTS_HOME)/libset/default.mk
$(RTS_HOME)/libset/$(LIBRARY_SET) /libset.mk
$(RTS_HOME)/target/$(TARGET)/target.mk
$(RTS_HOME)/config/$(CONFIG)/config.mk
The make macro TARGET is defined by:
TARGET=$(PLATFORM)$(THREADED)
The make macro CONFIG is defined by:
CONFIG=$(LIBRARY_SET).$(TARGET)
LIBRARY_SET, PLATFORM and THREADED are all defined from within the update's active configuration.
This makefile fragment defines some common make macros. These macros are interpreted first and may be overridden in any of the three RTS-specific makefile fragments or even the update-specific Make Overrides file that will be included later. Consequently, these macros may serve one of several purposes:
In many cases, default definitions are provided that may only be appropriate for a subset of compiler/library-sets, targets or configurations.
For more information, see Table 4, "Make macro definitions," on page 40.
This file is intended to list items specific to correctly linking with a particular library, and/or typically configuring the compiler. These configuration items include
Some targets will require further configuration without respect to which compiler is being used. For example Wind River System's Tornado targets require their linkers to be invoked with the -r option (to facilitate run-time linking).
In cases where a configuration item should only affect a particular library-set/target pairing, configurations can be put in the config.mk makefile fragment.
If the compilation host does not have Perl (version 5.002 or greater), it is highly recommended to obtain and compile the source from http://www.perl.com.
Since obtaining Perl may take some time, some limited functionality will still be available on the compilation host:
MAKEDEPEND_CMD) must be disabled.
OTCOMPILE_CMD and OTLINK_CMD) must be disabled.
The implementation of the C TargetRTS is contained in the $RTS_HOME/src directory. In this directory, there is a subdirectory for each major run-time area. For example, the sub-directory C
TargetRTS contains all of the standard run-time library functions. Other sub-directories, for example, TCP, MONITOR, INITSTOP, and TRANSPRT contain target observability functionality.
To port the C TargetRTS to a new platform, it may be necessary to update or replace some of these files. The target specific source is placed in a subdirectory of $RTS_HOME/src/target/<target_base>, where <target_base> is the target name without the `S' or `T'. For the remainder of this section, the target directory is referred to as $TARGET_SRC. For example, the target source directory for an example <target> PSOS2T is $RTS_HOME/src/target/PSOS2. This directory provides an overlay to the $RTS_HOME/src directory. When the C TargetRTS loadbuild tools search for a source file, it searches first in the $TARGET_SRC directory then in RTS_HOME/src.
Note:
There is only a single source directory for all configurations of the C TargetRTS for a given platform. C preprocessor macros, such as RSLMULTITHREADED, may be used to differentiate code for specific configurations.
For a single-threaded port without target observability, it is entirely conceivable that no source code modifications would be required, as the intent was to make the C TargetRTS completely portable to any ANSI C compiler, which should compile single-threaded C code without modification.
However, if the target is multi-threaded, modifications will be required to specify how certain RTOS services (for example, semaphore creation) are performed in that target environment. The remainder of this section discusses the most common required implementation code required for a new multi-threaded target.
In order for the execution of the C TargetRTS to begin, code must be provided to call cRSL_entryPoint( int argc, const char * const * argv ) passing in the arguments to the program. This code is placed in the file $TARGET_SRC/MAIN/main.c.
The default code is likely to be suitable for a new target platform.
If any special initialization and/or shut-down code is required for target observability, the files TGTinit.c, TGTstop.c, TOinit.c, and TOstop.c which are located in the directory $TARGET_SRC/INITSTOP contain code to perform these functions, in conjunction with the C TargetRTS initialization and shutdown.
The default code is likely to be suitable for a new target platform.
All memory allocation performed by the C TargetRTS (this does not apply to any dynamic memory allocation performed by the application) is performed through a single routine:
void *RSLAllocateMemory(RSLMemorySize size)
The C TargetRTS only allocates dynamic memory during its initialization phase. Thus, once the system has initialized, the C TargetRTS will not perform any dynamic memory allocations. Since the C
TargetRTS does not support any implicit recovery capabilities, the C TargetRTS does not support the freeing of any dynamically allocated memory, which was allocated by it. Thus, users must exercise caution when using this memory management routine at an application level.
The default implementation of this routine is to invoke the standard C "malloc" run-time routine, to allocate a large chunk of memory, whereupon requests are serviced efficiently from within this block (this works particularly efficiently in certain RTOS environments where there are minimal sizes, for example, 64 bytes for any malloc request). If standard C run-time malloc support is not availableor alternatively, you want to have the C TargetRTS allocate memory from a specific memory regionyou should modify this routine (in file memory.c) as appropriate for your target environment.
All of the RTOS interface mapping routines used by the C TargetRTS should be contained in the RTThread.c file located in the directory $TARGET_SRC/target/<target>/THREAD.
Figure 3 shows the relationships between an application, the C TargetRTS, and underlying RTOS primitives required to support a multi-threaded C TargetRTS.
Required relationshipsThis file is split into three general sections:
This section should specify the list of header files that are required for compilation for the TargetRTS (specifically, those header files that are required for the multi-threaded RTOS interface). An example follows:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
This section specifies RTOS-equivalent types for three C TargetRTS base types:
RSL_mutex
RSL_semaphore
RSL_thread_id
If the RTOS does not support mutexes, a mutex can be constructed via a semaphore.
typedef SEM_ID RSL_mutex;
typedef SEM_ID RSL_semaphore;
typedef int RSL_thread_id;
The final section in the RTThread.c file is used to define a set of C TargetRTS functions providing a direct interface from the C TargetRTS to the underlying RTOS capability.
These functions are organized as:
RSLmutex_init(lock). Given a pointer to the address "X" of a lock, create a lock, and set the address "X" to be the address of the newly created lock.
RSLmutex_lock(lock). Given the address of a lock, lock the lock.
RSLmutex_unlock(lock). Given the address of a lock, unlock the lock.
RSLsemaphore_init(semaphore). Given a pointer to the address "X" of a semaphore, create a semaphore as initially signalled (or with a count of 1), and set the address "X" to be the address of the newly created semaphore.
RSLsemaphore_wait(semaphore). Given the address of a semaphore, wait for the semaphore to be available (signalled, or count >= 1).
RSLsemaphore_post(semaphore). Given the address of a semaphore, post the semaphore as signalled, or increment the semaphore count.
RSLthr_create (index,priority,stack,stackSize,entryPoint,args). Given the task/thread instance integer number (0 through n-1 threads), task/thread priority, task/thread stack address, task/thread stack size, task/thread entry point, and task/thread arguments, create a thread and assign the returned thread id to the C TargetRTS thread description array RSLRTThreadInstances[index].threadId.
A suitable definition of a complete RTThread.c file for a Tornado 1.0.1 target might appear as shown in the extract below:
#include <RTThread.h>
#include <crsl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
typedef SEM_ID RSL_mutex;
typedef SEM_ID RSL_semaphore;
typedef int RSL_thread_id;
extern RSLRTThreadInstanceDescription *RSLRTThreadInstances;
int
RSLmutex_init( void **lockptr_ptr ) {
*lockptr_ptr = semCCreate( SEM_Q_FIFO , 1 );
return 1;
}
int
RSLmutex_lock( void *lockptr ) {
return semTake( lockptr , WAIT_FOREVER );
}
int
RSLmutex_unlock( void *lockptr ) {
return semGive( lockptr );
}
int
RSLsemaphore_init( void **semaphoreptr_ptr ) {
*semaphoreptr_ptr = semBCreate( SEM_Q_FIFO , SEM_FULL );
return 1;
}
int
RSLsemaphore_wait( void *semaphoreptr ) {
return semTake( semaphoreptr , WAIT_FOREVER );
}
int
RSLsemaphore_post( void *semaphoreptr ) {
return semGive( semaphoreptr );
}
int
RSLthr_create( int index , int priority , void *stack , int stackSize ,
void *entryPoint , void *args ) {
RSLRTThreadInstances[index].threadId =
taskSpawn( NULL , priority , 0 , (stackSize) , entryPoint , args ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 );
}
If you create a new file for the C TargetRTS, then you must add the new file name to a manifest file for the C TargetRTS. This must be done in order for the dependency calculations to include the new file and thus include it into the C TargetRTS.
This file lists all the elements of the run-time system. There is one file entry per line. Each entry has three or more fields separated by whitespace. The first field names a make variable that specifies which library to associate the file with. The second field is a directory name. The third field is the base name of a file. The fourth and subsequent fields, if present, give an expression that evaluates to zero when the element should be excluded. Note that the expression is evaluated by Perl and so should be of a form that it can handle.
If a file has been overridden in $(RTS_HOME)/target/src/<target_name> directory or a new file has been added to the MANIFEST.c you must regenerate the make dependencies in order for the modification to be included in the new TargetRTS. This is done by removing the depend.mk file in the build directory ($(RTS_HOME)/build-<platform_name>). This causes the dependencies to be recalculated and a new depend.mk file to be created. Note that include files must not be enclosed in #ifdef/#endif or similar macros because the preprocessor may not have the needed definitions to satisfy these macros. Consequently, the include file may be ignored during dependency checking.
The C TargetRTS does not distinguish between a task, process, or thread. In fact, some RTOSswhich support multiple tasksrepresent a thread as a task. Instead, the C TargetRTS requires the simple notion of a "thread of execution", which (depending upon the customer RTOS capability) might be a thread, a task, or a process. However, the C TargetRTS does require that if multiple threads of execution are required, they share a common "shared" memory space.
If multiple threads are supported in the target environment, the C TargetRTS, in conjunction with the ObjecTime toolset, provides the customer with the ability to split up an application into multiple threads. The decision to split the application into multiple threads of execution is an important architectural decision. The C TargetRTS provides simple capabilities to define which top-most ObjecTime actors are to be assigned to which threads. Thus it is possible to "code-generate" different thread topologies easily, without necessarily modifying any user code in the ObjecTime model.
Currently, most systems are comprised of one or more separate threads of execution. Each of these threads typically has a main routine that is responsible for servicing the IPC (Inter-Process Communication) messages or signals that are received by that system. These IPC routines are usually blocking RTOS IPC calls; that is, they do not return execution to the main routine until a message/signal has been received. When a message/signal is received, an appropriate subroutine is invoked to handle the actual processing associated with the arrival of that particular message/signal. This message processing occurs for the duration of the execution of the system.
Similarly, each thread of execution in the C TargetRTS also has a "mainloop". This is in recognition of the fact that the main C TargetRTS routine processes many IPC messages, in a serial fashion, via a looping mechanism. After receiving each external message, it processes it, that is, it "walks' the state machines of one or more actors.
The following pseudo-code describes this notion of a main-loop:
<Initialize IPC>
<Do Forever>
{
<Process Actor Messages>
<Block for IPC Message>
}
To support user-defined messaging interfaces, each of these threads of execution requires a application-specified routine, which in conjunction with the C TargetRTS mainloop, implements how that particular task is to interface to the user's IPC. It is possible to have many different interface routinesa different one for every thread. It is also possible for different threads to use different IPC mechanisms. Note that a thread blocked waiting for external messages from the IPC will not unblock to process actor messages.
In an ObjecTime design, the top-most actor is assigned its own thread; in fact, by default, every program that is run has at least one thread of execution. Unless directed otherwise by that top-most actor, all contained actors will also "run" on that same thread.
However, if the user application is multi-threaded (that is, at least two or more threads of execution), then these additional threads are contained by this top-level actor. These contained actors are assigned a different thread at run-time through the top-level actor's function RSLThreadMap, which is a "special" function, with a non-C specification syntax; that is, it cannot be compiled by a C compiler. It is called a "special" function, since there is special code-generation handling the particular function name of "RSLThreadMap".
This RSLThreadMap function specifies the run-time characteristics of each of the threads, except the top-thread. The top-thread's characteristics are not defined by the C TargetRTS, as the user's RTOS specifies how the first thread of the C TargetRTS is to be started (and with which characteristics, for example, priority, stack size, and so forth).
The RSLThreadMap, in conjunction with the logical/physical thread configuration stipulated in the ObjecTime toolset (see the C Language Guide for additional information), specify which logical threads are mapped to which physical threads. In those instances where multiple logical threads are mapped to a single physical thread, the resource requirements of the physical thread are determined by summing all of the resource requirements of each of the logical threads together, that is, the number of internal messages and the number of timer control blocks. Each logical thread name specified in the RSLThreadMap must have a corresponding logical thread definition in the thread configuration browser in the ObjecTime toolset.
The characteristics that can be specified, on a per thread basis (for more information see "C TargetRTS constants/macros and their default values" on page 55), include
The maximum requirements for inter-actor messages used within the thread. If not specified, it defaults to 0, which implies that the thread intends to do no internal messaging between actors in the same thread (probably unlikely).
The maximum number of active timers that can be active at one time within the thread. If not specified, it defaults to 0, which implies that no actors contained in that thread intend to start any timers.
Finally, the RSLThreadMap serves to provide an association between an actor reference in the top actor (that is, the name of an actor in the structure diagram of the top actor in the system), and the logical thread name, as specified in the ObjecTime toolset in the Threads Browser.
For example, in the example RSLThreadMap below, two threads are defined. `sender' and `replyer' are the names of two actor references contained in the top-most actor. Each of these actor references are associated with their respective logical threads (which, in turn, are mapped to physical threads within the toolset). In addition, each thread has been defined to be created with five internal messages and no TCBs (Timer Control Blocks).
/*
thread SenderLogicalThread {
NumberOfInternalMessages 5;
NumberOfTCBs 0;
} sender;
thread ReplyerLogicalThread {
NumberOfInternalMessages 5;
NumberOfTCBs 0;
} replyer;
*/
The C TargetRTS message priority, defines six RSL levels of priority (from highest to lowest):
These message priorities are used by the internal C TargetRTS scheduler to dispatch the highest priority messages (for example, Panic) before lower priority messages (for example, General) are processed. Without the use of message priority, all messages are treated equally, in that all messages are processed FIFO (First-In, First-Out).
Often, indiscriminate use of message priority unnecessarily increases the complexity of actor behavior, since the sequence of events now also depends on message priority.
Each thread of execution has six incoming internal message queues (five to be used by customers), one for each message priority, as defined in the previous section.
At run-time, based on the specified SAP/Endport in a message send request, the C TargetRTS determines whether the recipient of a message is within the same thread as the sender, or alternatively, is in a different thread. Once this is known, the C TargetRTS automatically selects an appropriate message enqueueing technique for the message send.
In the single-threaded C TargetRTS, there is no distinguishing run-time characteristic made between "internal" and "external" C TargetRTS messages, since there is only one thread of execution.
Thus, in a single-threaded C TargetRTS, there is
The C TargetRTS mainloop will process the messages that are on the internal message queues (more about this in a subsequent section).
When an actor transition is fired that contains one or more requests to send messages to another actor, as indicated in Figure 4, the following steps are performed for each such message send:
If no message is available, a false result (a value of 0) is returned by the send function. Under the assumption that the port has been bound, this is the only error that the C TargetRTS can detect on a send.
Intra-thread inter-actor sendIn the multi-threaded C TargetRTS, the C TargetRTS distinguishes between "internal" and "external" C TargetRTS messages. Internal messages are inter-actor intra-thread messages, where each actor runs in the same thread of execution. External messages are inter-actor inter-thread messages, where the sending and receiving actors run in a different thread of execution.
In the multi-threaded C TargetRTS, each thread has
In addition, in the multi-threaded C TargetRTS, there is a global free message queue.
As discussed in "Message processing" on page 72, the multi-threaded C TargetRTS:
The previous section described the C TargetRTS implementation of an intra-thread inter-actor send. In the multi-threaded C TargetRTS, all intra-thread sends are performed in the same manner.
Inter-thread sends, however, are substantially different, in that they require processing as indicated in Figure 5, with the following numbered steps:
If none are available, a free message is popped from the sender's external free queue (1B).
Once again, if not available, a free message is popped from the global free queue (1C).
If that also fails, the C TargetRTS will then move all free external messages from every thread and put them on the global free queue (then it tries to pop from the global queue again).
Finally, if no message is available, a false result (a value of 0) is returned by the send function. Under the assumption that the port has been bound, this is the only error that the C TargetRTS can detect on an inter-thread send.
Inter-thread sendsThe C TargetRTS ensures system integrity by locking a global mutex prior to accessing any global shared data (shared amongst multiple threads), and releasing the lock once the shared data updates have been completed.
The `InPriority' field is used by a thread to determine at what point an external incoming message is of higher priority than that which is currently being processed internally. Thus, this allows the C
TargetRTS to "interrupt" normal actor message processing (on a per-message basis) and service a higher priority actor message. Note, however, that this does not mean that the processing of one or more user code transitions is interrupted, as new messages are selected for processing only after the processing of the behavior of the current message has been completed.
As indicated below, in Figure 6, single-threaded C TargetRTS actor message processing is implemented by:
Single-threaded C TargetRTS message processing
If the invocation of actor behavior causes one or more messages to be sent to other actors, the C
TargetRTS mainloop may continue to invoke actor behaviors until there are no further messages to be processed. By default, all of these messages are processed prior to the C TargetRTS directing the invocation of the user-registered external interface (IPC) routine.
As indicated in Figure 7, multi-threaded C TargetRTS actor message processing is implemented by:
message processingIf the initial invocation of actor behavior causes one or more messages to be sent to other intra-thread actors, the C TargetRTS mainloop may continue to invoke actor behaviors until there are no further messages to be processed. By default, if both intra-thread and inter-thread messages are enqueued, all of these messages are processed prior to the C TargetRTS invoking the user-registered external interface/IPC routine.
This default "run-to-completion" C TargetRTS message scheduling policy is implemented in the C
TargetRTS mainloop as
while(RSLDispatch(RSLBACKGROUND));
where "RSLBACKGROUND" represents the lowest priority message to be processed. The RSLDispatch routine is responsible for invoking the behavior associated with the single highest-priority message on the input queue. It returns true if a message was processed, or false if no internal messages were available for processing at the specified, or higher priority.
Thus, the "while" clause causes the C TargetRTS to continue processing these "internally" queued messages, until no further messages are enqueued, at which point, the C TargetRTS will call the user-defined IPC interface routine to block for external stimuli (for example, IPC messages, timeouts, and so forth). If the C TargetRTS is multi-threaded, the C TargetRTS will also check the incoming external queue for higher priority messages than those in the internal message queue. If there are higher priority external messages, these messages are moved to the internal queue, and C TargetRTS actor message processing continues.
If no user-defined IPC routine was specified, the multi-threaded C TargetRTS will wait until its `ready-to-run' semaphore has been signalled by another thread (for example, an inter-thread inter-actor message send). In the single-threaded C TargetRTS, if there is no other source of potential inputs, the thread will exit.
Also, in the multi-threaded C TargetRTS, an external C TargetRTS message is moved from the thread's external incoming message queue to its internal message queue (based on priority). At that point, message processing within the thread proceeds in a similar fashion in both the single and multi-threaded C TargetRTS.
The C TargetRTS was designed so that it requires no inherent notion of how customers' IPC mechanisms work. Instead, it provides the "internal" C TargetRTS actor message processing portion of the mainloop, and allows customers to register their own IPC external interface routines, which are invoked by the C TargetRTS once no further internal message processing is possible. Note that a blocked thread waiting for external messages from the IPC will not unblock to process any new actor messages.
It is possible to have many different interface routinesa different one for every threadand it is possible for different threads to use different IPC mechanisms.
The C TargetRTS provides support for each thread to register its own interface function by using the following routine to dynamically perform this registration, an example of which might appear as
RSLRegisterExternalInterface(_actor,MyActorIPCInterface);
The first parameter `_actor' of type RSLActorIndex should be set to the internal C TargetRTS variable automatically defined for all actor transitions. `MyActorIPCInterface' would be the name of the user-defined actor function, which implements how the thread interfaces to IPC and/or timers.
Alternative implementations can be contemplated whereby an actor implements the IPC interface directly in the actor behavior, but this technique would likely require that this IPC actor run with the lowest priority messaging to ensure that all internal actor messages have been processed before the IPC actor blocks on an IPC call.
RSLRegisterExternalInterface is used for all IPC and timer implementations. For example, the code fragment shown below was taken from an actor's initialization transition. Once this initialization transition is executed, the thread that the actor is executing in will have a registered external interface routine called 'ExternalInterfaceRoutine'.
RSLRegisterExternalInterface(_actor,ExternalInterfaceRoutine);
printf("Example thread: Alive!\n");
The C TargetRTS was designed to support different types of IPC and timing mechanisms, suitable for a particular target environment. In some instances, it may be necessary to have the C TargetRTS signal another C TargetRTS thread that it is in the process of sending that thread a message (for example, to interrupt a blocking RTOS function called from within a registered external interface function).
The C TargetRTS provides support for each thread to register a signalling function via the following example:
RSLRegisterMessageSignallingInterface(_actor,
&MyCV,MySignallingInterface);
The first parameter `_actor' of type RSLActorIndex should be set to the internal C TargetRTS variable automatically defined for all actor transitions. The second parameter is the address of the RTOS object to be signalled. "MySignallingInterface" is the name of a user-defined actor function, which would be responsible for signalling the passed RTOS object MyCV.
At run-time, when an inter-thread message is delivered to a thread that had previously registered a signalling interface routine, the C TargetRTS calls the specified procedure, along with the address of the specified RTOS object, via the pre-registered procedure variable.
To allow customer-registered external interface/IPC routines to enqueue messages that represent the arrival of an external IPC message or a timeout event (discussed later), the C TargetRTS provides the following routine:
RSLMessage *RSLPortEnqueue(RSLActorIndex _actor,
RSLPortIndex portOffset,
RSLSignalIndex signal,
RSLMessagePriority priority,
void *data);
Once a message is enqueued in this manner, control should be returned from the registered external interface routine to the C TargetRTS, which is responsible for dispatching the message. In fact, if there is no message enqueued after the registered external interface function returns, it is likely that the thread will suspend indefinitely (or if single-threaded, possibly exit).
RSLPortEnqueue is used for all IPC and timer implementations.
This C TargetRTS function is used to get the RTTimerId (same as RSLTimerReference) of the first timer control block on the sorted list of timers for the thread associated with the specified actor. An example follows:
RSLTimerReference RSLGetFirstTimeout(_actor)
RSLGetFirstTimeout is used for all timer implementations (RSLTIMERS set to RSLTRUE).
This C TargetRTS function is used to cancel a pending timer request by marking the timer control block associated with that timer reference as cancelled. Thus, at a later time, the timer control block could be released and reused.
RSLBool RSLCancelTimer(RSLActorIndex,RSLTimerReference);
RSLCancelTimer is used for all timer implementations (RSLTIMERS set to RSLTRUE).
Since the TCBs are managed by one or more actors or registered actor functions (as opposed to automatic control via the C TargetRTS), the C TargetRTS also provides a routine to dequeue a timer, and possibly free the timer control block:
RSLDequeueTimer(_actor,&TimerControlBlock,Free)
The first parameter `_actor' indicates which context we are executing. The second parameter supplies the address of the timer control block. The final parameter is a Boolean value that instructs the C
TargetRTS to return the timer control block to the original user thread.
The C TargetRTS supports actor-based implementations of timing services via the following routine:
RSLRegisterTimerServices(_actor,sap,
&MyCV,MySignallingInterface);
The first parameter `_actor' of type RSLActorIndex should be set to the internal C TargetRTS variable automatically defined for all actor transitions. The second parameter is the SAP upon which service requests are to be received. The third parameter is the address of the RTOS object to be signalled. "MySignallingInterface" is the name of a user-defined actor function, which would be responsible for signalling the passed RTOS object MyCV.
RSLRegisterTimerServices is only used for actor timer implementations (RSLTIMERS set to RSLTRUE; RSLACTOR_TIMERS set to RSLTRUE).
This C TargetRTS function is used to get the run-time actor Id of the timer actor that has registered as the provider of all timing services for the C TargetRTS. An example invocation follows:
RSLActorIndex RSLGetTimerServiceActor(_actor)
RSLGetTimerServiceActor is used for timer actor implementations (RSLTIMERS set to RSLTRUE; RSLACTOR_TIMERS set to RSLTRUE).
Often, timing events are required by system tasks to determine that some function was not completed in a timely manner, or to schedule activities. Depending upon the customer's application requirements and RTOS timing capabilities, two different timer implementation approaches can be considered:
These two approaches are independent implementations, and the techniques used in one approach are not compatible with the other. Hybrid solutions should be avoided. Once again, primarily for efficiency and size considerations, it is recommended that all C TargetRTS users use integrated timers in an RTOS environment.
Local timers implement timing services on a per-thread basis by using an RTOS capability to block for some sort of a signal/message for a specified period of time. Thus, once the RTOS primitive returns execution, a return code can be checked to see if either a signal/message was received or the RTOS call timed out.
Typically, the blocking RTOS call that forms the basis for implementation for local timers is either a message read request (for example, on a message queue), or a signal wait request (for example, on a semaphore). By specifying an appropriate timeout parameter each time this RTOS function is invoked, a simple and efficient timing mechanism can be implemented. Since this same approach can be used in multiple threads, each thread is responsible for managing its external interface (message queue, semaphore, and so forth) and timer requests.
By using the RSLRegisterExternalInterface C TargetRTS API function, it is possible to define an RTOS-specific actor interface function, which is registered by an actor executing in each thread. These registered functions would be responsible for implementing timer services for all actors in that thread, and possibly interfacing to a proprietary RTOS IPC (Inter-Process Communication) mechanism.
Since the C TargetRTS provides an efficient inter-thread inter-actor messaging mechanism via port bindings, if an actor provides a proprietary RTOS IPC interface, it is likely that it will interfere with normal C TargetRTS inter-thread communication. This is because it is not possible to service multiple communication mechanisms simultaneously (proprietary C TargetRTS communication and RTOS IPC), unless you consider the use of a signalling function. For more information, see "RSLRegisterMessageSignallingInterface" on page 75.
With the local timer approach, there are two inter-thread messaging alternatives:
Since there is a tremendous advantage in having the C TargetRTS manage all inter-thread inter-actor messagingthat is, you can change thread topologies easily and the software is likely to be more portablethe first alternative "Integrated Timers" is recommended. However, if there is a requirement to service different types of IPC (for example, in a legacy system), it is still possible to provide actor interface functions to these threads, which are responsible for providing the proprietary RTOS IPC interface and forwarding requests (via port bindings) on to other C TargetRTS threads. Thus, in some system environments, it is possible that a mix of approaches may be needed to satisfy all requirements.
An example of both of these types of implementations can be found under $(OBJECTIME_HOME)/ModelExamples/C, in an update called "C_TornadoQueuesWithTimers". A detailed description of this update can be found in "Compliance Suite & Examples" on page 219 of the C Language Guide. This update should be used as a basis for implementing either Integrated Timers or Integrated IPC and Timers.
RTTarget.h should specify that RSLTIMERS is set to RSLTRUE; RSLACTOR_TIMERS is set to RSLFALSE.
No special timing actor/packages are required.
Two actor functions must be implemented.
At initialization time, an actor in each thread that requires the use of integrated timers must:
An example of this type of timer implementation can be found under $(OBJECTIME_HOME)/ModelExamples/C, in an update called "C_TornadoQueuesWithTimers". Specifically, the actors "Sender" and "Replyer" in this update implement this form of timer. A detailed description of this update can be found in "Compliance Suite & Examples" on page 219 of the C Language Guide.
The relevant transitions and functions which adhere to the above guidelines are shown below for the sender actor:
/* Sender -- Initialize transition */
this->MySemaphore=semCCreate(0,0);
RSLRegisterExternalInterface(_actor,SenderMainloop);
RSLRegisterMessageSignallingInterface(_actor,
(void*)this->MySemaphore,SenderSignalRoutine);
printf("Sender thread: Alive!\n");
ROOM_InformIn(MyTimer,RSLTARGET_TIME(100));
ROOM_PortSend(inOut,InOutSignal);
/* Sender Functions */
/* Signalling Function */
void SenderSignalRoutine(SEM_ID semaphore) {
semGive(semaphore);
}
/* External Interface Routine */
void SenderMainloop(Sender_InstanceData *this,
RSLActorIndex _actor) {
unsigned long current;
int MsgQReceiveResult;
RSLTimerControlBlock *tcb;
RSLTimerReference tr;
/* Get the current time and check to see if the lowest timer
value has already expired */
current=tickGet();
tr=RSLGetFirstTimeout(_actor);
tcb=tr.tcb;
if((tcb)&&(current>=tcb->timeoutNSec)) {
tcb->timeoutMessage=RSLPortEnqueue(_actor,
MyTimer,RSLTIMEOUT,RSLGENERAL,(void *)0);
RSLCancelTimer(_actor,tr);
return;
}
/* Block for any message on the queue with a timeout
(# clock ticks from now) */
if(tcb)
MsgQReceiveResult=semTake(this->MySemaphore,
tcb->timeoutNSec-current);
else
MsgQReceiveResult=msgQReceive(
this->MySemaphore,WAIT_FOREVER);
if((MsgQReceiveResult==ERROR)&&
(errno=S_objLib_OBJ_TIMEOUT)) { /* Timeout */
RSLPortEnqueue(_actor,MyTimer,
RSLTIMEOUT,RSLGENERAL,(void *)0);
RSLCancelTimer(_actor,tr);
}
}
The relevant transitions and functions that adhere to the above guidelines are shown below for the replyer actor:
/* Replyer -- Initialize Transition */
this->MySemaphore=semCCreate(0,0);
RSLRegisterExternalInterface(_actor,ReplyerMainloop);
RSLRegisterMessageSignallingInterface(_actor,
(void *)this->MySemaphore,ReplyerSignalRoutine);
printf("Replyer thread: Alive!\n");
ROOM_InformIn(MyTimer,RSLTARGET_TIME(100));
/* Replyer -- Functions */
/* Signalling Function */
void ReplyerSignalRoutine(SEM_ID semaphore) {
semGive(semaphore);
}
/* External Interface Function */
void ReplyerMainloop(Replyer_InstanceData *this,
RSLActorIndex _actor) {
unsigned long current;
int MsgQReceiveResult;
RSLTimerControlBlock *tcb;
RSLTimerReference tr;
/* Get the current time and check to see if the lowest timer
value has already expired */
current=tickGet();
tr=RSLGetFirstTimeout(_actor);
tcb=tr.tcb;
if((tcb)&&(current>=tcb->timeoutNSec)) {
tcb->timeoutMessage=RSLPortEnqueue(_actor,
MyTimer,RSLTIMEOUT,RSLGENERAL,(void *)0);
RSLCancelTimer(_actor,tr);
return;
}
/* Block for any message on the queue with a timeout
(# clock ticks from now) */
if(tcb)
MsgQReceiveResult=semTake(this->MySemaphore,
tcb->timeoutNSec-current);
else
MsgQReceiveResult=msgQReceive(this->MySemaphore,
WAIT_FOREVER);
if((MsgQReceiveResult==ERROR)&&
(errno=S_objLib_OBJ_TIMEOUT)) { /* Timeout */
RSLPortEnqueue(_actor,MyTimer,
RSLTIMEOUT,RSLGENERAL,(void *)0);
RSLCancelTimer(_actor,tr);
}
}
The external interface function pseudo-code for the two actors' code fragments (sender and replyer) would be defined as:
<GetFirstTimeout>
<GetCurrentTime>
<If (ValidTimer && TimerHasTimedOut)> {
<EnqueueTimeoutEvent>
<RemoveTimerRequest>
return;
}
<If ValidTimer>
<BlockForSignalWithTimeout>
else
<BlockForSignalWithInfiniteTimeout>
<IfTimedOut> {
<EnqueueTimeoutEvent>
<RemoveTimerRequest>
}
The first block of pseudo code determines if (during the intervening period of time while the system was processing) any timeouts would have occurred. If timeouts have occurred, timeout events are generated, and the time requests are removed from the timer list.
If no timeout occurred, the next block of code will await for an external RTOS signal while specifying a timeout value (which is our next shortest time to a timeout). Once control is returned to the user's IPC routine, a check is then done to determine if the blocking RTOS request timed-out (at which point, a timeout event is enqueued, and the timer request is removed from the timer list).
These timers require the use of relative time (for the informIn request), and absolute time to determine when a timer is to expire (for example, clock ticks). Thus, the use of a macro (for example, RSLTarget_Time) might be used to ensure compatibility between SimulationRTS and the C TargetRTS running on a target. Specifically, the target version of RSLTarget_Time should be defined to add the current number of clock ticks to the relative time to create a future absolute time, which is used by the external interface routine.
e.g. #define RSLTarget_Time(x) (clockTicks()+x)
Timers are started when an actor in that thread starts a timer via ROOM_InformIn. In the C
TargetRTS, the timer start routine will allocate a local thread TCB, populate it with appropriate data, and then insert it into the list of active timers for that thread, in sorted order. Once enqueued, the registered external interface for that routine will process the timeout requests, as described in the above pseudo-code.
If a thread is currently blocked, waiting for a timeout to occur, and if another thread sends a message to that thread, the thread will be interrupted, since the sending thread will invoke the previously registered signalling function in the receiving thread. Once that message is processed, the external interface function will once again be invoked by the C TargetRTS.
Timers are cancelled via ROOM_CancelTimer. In the C TargetRTS, the timer cancel routine will mark the RTTimerID (which points to a timer control block) as cancelled, and thus will be ignored by external interface function, when it actually times out.
The number of timers that can be started is configured in the RSLThreadMap. For the first/top thread, the number of timers to be created at initialization time should be configured in RTTarget.h, overriding the defaults in RTConfig.h.
Each thread has a set of internal TCBs available for its exclusive use. TCBs are not shared amongst threads, and are to be modified by the C TargetRTS only.
Also, each thread has a linked-list of active TCBs, which are purposely stored in sorted order. That is to say, when you start a timer, the timeout is checked and inserted in order into the linked-list, where the smallest timeout values appear at the front of the queue, and the largest timeout values appear at the end of the queue. By inserting these items into the queue in sorted order, it is possible to quickly determine the next timeout value by simply reviewing the first entry on the timer queue.
RTTarget.h should specify that RSLTIMERS is set to RSLTRUE; RSLACTOR_TIMERS is set to RSLFALSE.
No special timing actor/packages are required.
One actor function must be implemented.
At initialization time, an actor in each thread that requires the use of integrated IPC and timers must
An example of this type of timer implementation can be found under $(OBJECTIME_HOME)/ModelExamples/C, in an update called "C_TornadoQueuesWithTimers". Specifically, the actors "Behavior" and "Responder" in this update implement this form of timer. A detailed description of this update can be found in "Compliance Suite & Examples" on page 219 of the C Language Guide.
The relevant transitions and functions that adhere to the above guidelines are shown below for the Behavior actor. Note that the behavior external interface function is not registered until it has received a corresponding Sync signal (with identifying Queue ID information).
/* Behavior -- initialize transition */
this->MyQ=msgQCreate(2,256,MSG_Q_FIFO);
ROOM_PortSendData(ids,Request,(void *)this->MyQ);
printf("Behavior: Sending QID Info (0x%x) of my Q\n",this->MyQ);
/* Behavior -- Sync transition */
this->otherQ=(MSG_Q_ID)msg->data;
printf("Behavior: Received other Q ID (0x%x)\n",this->otherQ);
RSLRegisterExternalInterface(_actor,BehaviorMainloop);
printf("Behavior: Starting Timer for 1 Second\n");
ROOM_InformIn(timer,RSLTARGET_TIME(100));
/* Behavior -- timeout transition */
printf("Behavior: Got expected timeout!\n");
msgQSend(this->otherQ,"Hello!",7,NO_WAIT,0);
printf("Behavior: Starting Timer for 1 Second\n");
ROOM_InformIn(timer,RSLTARGET_TIME(100));
/* Behavior -- Functions */
void BehaviorMainloop(Behavior_InstanceData *this,
RSLActorIndex _actor) {
static char copyData[256],*newCopy;
unsigned long current;
int MsgQReceiveResult;
RSLTimerControlBlock *tcb;
RSLTimerReference tr;
/* Get the current time and check to see if the lowest timer
value has already expired */
current=tickGet();
tr=RSLGetFirstTimeout(_actor);
tcb=tr.tcb;
if((tcb)&&(current>=tcb->timeoutNSec)) {
tcb->timeoutMessage=RSLPortEnqueue(_actor,timer,
RSLTIMEOUT,RSLGENERAL,(void *)0);
RSLCancelTimer(_actor,tr);
return;
}
/* Block for any message on the queue with a timeout
(# clock ticks from now) */
if(tcb)
MsgQReceiveResult=msgQReceive(this->MyQ,copyData,
256,tcb->timeoutNSec-current);
else
MsgQReceiveResult=msgQReceive(this->MyQ,copyData,
256,WAIT_FOREVER);
if((MsgQReceiveResult==ERROR)&&
(errno=S_objLib_OBJ_TIMEOUT)) { /* Timeout */
RSLPortEnqueue(_actor,timer,RSLTIMEOUT,RSLGENERAL,
(void *)0);
RSLCancelTimer(_actor,tr);
}
else if(MsgQReceiveResult>0) { /* Received a Message! */
newCopy=(char *)malloc(256);
memcpy(newCopy,copyData,256);
RSLPortEnqueue(_actor,ids,SomeEvent,RSLGENERAL,newCopy);
}
else {
/* ??? Error */
}
}
Since this actor supports both timers and proprietary IPC (Tornado message queues), the external interface function pseudo-code would be defined as:
<GetFirstTimeout>
<GetCurrentTime>
<If (ValidTimer && TimerHasTimedOut)> {
<EnqueueTimeoutEvent>
<RemoveTimerRequest>
return;
}
<If ValidTimer>
<BlockForIPCWithTimeout>
else
<BlockForIPCWithInfiniteTimeout>
<IfTimedOut> {
<EnqueueTimeoutEvent>
<RemoveTimerRequest>
}
else
<EnqueueIPCMessage>
The first block of pseudo code determines if (during the intervening period of time while the system was processing) any timeouts would have occurred. If timeouts have occurred, timeout events are generated, and the time requests are removed from the timer list.
If no timeout occurred, the next block of code will await for an external IPC message/stimulus while specifying a timeout value (which is our next shortest time to a timeout). Once control is returned to the user's IPC routine, a check is then done to determine if the IPC request timed-out (at which point, a timeout event is enqueued, and the timer request is removed from the timer list), or a real IPC message or external stimulus was received. In that instance, an appropriate message event is enqueued for subsequent execution by the actor.
These timers require the use of relative time (for the informIn request), and absolute time to determine when a timer is to expire (for example, clock ticks). Thus, the use of a macro (for example, RSLTarget_Time) might be used to ensure compatibility between the SimulationRTS and the C
TargetRTS running on a target. Specifically, the target version of RSLTarget_Time should be defined to add the current number of clock ticks to the relative time to create a future absolute time, which is used by the external interface routine.
e.g. #define RSLTarget_Time(x) (clockTicks()+x)
Timers are started when an actor in that thread starts a timer via ROOM_InformIn. In the C
TargetRTS, the timer start routine will allocate a local thread TCB, populate it with appropriate data, and then insert it into the list of active timers for that thread, in sorted order. Once enqueued, the registered external interface for that routine will process the timeout requests, as described in the above pseudo-code.
If a thread is currently blocked, awaiting for a timeout to occur, and if another thread sends a message to that thread, the thread will not be interrupted (assuming that the RTOS IPC is incompatible with the proprietary C TargetRTS inter-thread inter-actor messaging implementation). Thus the ObjecTime message will be deferred until after that thread's interface routine returns.
Timers are cancelled via ROOM_CancelTimer. In the C TargetRTS, the timer cancel routine will mark the RTTimerID (which points to a timer control block) as cancelled, and thus will be ignored by the external interface function, when it actually times out.
The number of timers that can be started is configured in the RSLThreadMap. For the first/top thread, the number of timers to be created at initialization time should be configured in RTTarget.h, overriding the defaults in RTConfig.h.
Each thread has a set of internal TCBs available for its exclusive use. TCBs are not shared amongst threads, and are to be modified by the C TargetRTS only.
Also, each thread has a linked-list of active TCBs, which are purposely stored in sorted order. That is to say, that when you start a timer, the timeout is checked, and inserted in order into the linked-list, where the smallest timeout values appear at the front of the queue, and the largest timeout values appear at the end of the queue. By inserting these items into the queue in sorted order, it is possible to quickly determine the next timeout value by simply reviewing the first entry on the timer queue.
The relevant transitions and functions that adhere to the above guidelines are shown below for the Responder actor. Note that the behavior external interface function is not registered until it has received a corresponding Sync signal (with identifying Queue ID information).
/* Responder -- initial transition */
this->stimulatorQ=msgQCreate(2,256,MSG_Q_FIFO);
printf("Responder: Sending QID Info (0x%x) of my Q\n",
this->stimulatorQ);
ROOM_PortSendData(ids,Request,(void *)this->stimulatorQ);
/* Responder -- Sync transition */
this->otherQ=(MSG_Q_ID)msg->data;
printf("Responder: Received other Q ID (0x%x)\n",this->otherQ);
RSLRegisterExternalInterface(_actor,ResponderMainloop);
/* Responder -- ExtMsg transition */
printf("Responder: Received Q msg '%s'\n",(char *)msg->data);
msgQSend(this->otherQ,"There!",7,NO_WAIT,0);
/* Responder -- Functions */
void ResponderMainloop(Responder_InstanceData *this,
RSLActorIndex _actor) {
static char copyData[256];
/* Get a Tornado message for the queue and enqueue it
for the cRSL */
msgQReceive(this->stimulatorQ,copyData,256,WAIT_FOREVER);
RSLPortEnqueue(_actor,ids,SomeEvent,RSLGENERAL,©Data);
}
Since this actor supports only proprietary IPC (Tornado message queues), the external interface function pseudo-code would be defined as:
<BlockForExternalMessage>
<EnqueueMessage>
RTTarget.h should specify that RSLTIMERS is set to RSLTRUE; RSLACTOR_TIMERS is set to RSLTRUE.
A special timers package should be used as a basis for implementation. An example of this type of timer implementation can be found under $(OBJECTIME_HOME)/ModelExamples/C, in an update called "C_Timers". A detailed description of this update can be found in "Compliance Suite & Examples" on page 219 of the C Language Guide.
In this approach, a timer actor registers itself as the provider of timing services for all timing requests in the C TargetRTS. If multi-threaded, this actor should be configured to run on its own thread. The C
TargetRTS will handle all inter-actor inter-thread and intra-thread messaging to and from the timing actor.
At least two timing actor functions must be implemented. They are as follows:
At initialization time, an actor in each thread that requires the use of integrated timers must
The external interface function pseudo-code would be defined as
<GetFirstTimeout>
<GetCurrentTime>
<If (ValidTimer && TimerHasTimedOut)> {
<EnqueueTimeoutEvent>
<RemoveTimerRequest>
return;
}
<If ValidTimer>
<BlockForSignalWithTimeout>
else
<BlockForSignalWithInfiniteTimeout>
<IfTimedOut> {
<EnqueueTimeoutEvent>
<RemoveTimerRequest>
}
The first block of pseudo code determines if (during the intervening period of time while the system was processing) any timeouts would have occurred. If timeouts occurred, timeout events are generated, and the time requests are removed from the timer list.
If no timeouts occurred, the next block of code waits for an external signal while specifying a timeout value (which is our next shortest time to a timeout). Once control is returned to the timer actor's external interface routine, a check is then done to determine if the blocking RTOS request timed-out (at which point, a timeout event is enqueued, and the timer request is removed from the timer list).
These timers require the use of relative time (for the informIn request), and absolute time to determine when a timer is to expire (for example, clock ticks). Thus, the use of a macro (for example, RSLTarget_Time) might be used to ensure compatibility between SimulationRTS and the
C TargetRTS running on a target. Specifically, the target version of RSLTarget_Time should be defined to add the current number of clock ticks to the relative time to create a future absolute time, which is used by the external interface routine.
e.g. #define RSLTarget_Time(x) (clockTicks()+x)
Timers are started when an actor in a thread starts a timer via ROOM_InformIn. In the C TargetRTS, the timer start routine will allocate a local thread TCB, populate it with appropriate data, and then enqueue it via an inter-thread message to be delivered to the registered timing actor. When the timing actor receives the new request, it will insert it into the list of active timers for the timing thread, in sorted order. Once enqueued, the registered external interface for the timing thread will process the timeout requests, as described in the above pseudo-code.
If the timing thread is currently blocked, waiting for a timeout to occur or for new timing requests, and if another thread sends a timing request message to the timing thread, the thread will be interrupted. Thus, once the ObjecTime timeout request message is processed, the timing threads interface routine will be re-invoked.
Timers are cancelled via ROOM_CancelTimer. In the C TargetRTS, the timer cancel routine will mark the RTTimerID (which points to a timer control block) as cancelled, and thus will be ignored by external interface function when it actually times out.
The number of timers that can be started is configured in the RSLThreadMap. For the first/top thread, the number of timers to be created at initialization time should be configured in RTTarget.h, overriding the defaults in RTConfig.h.
Each thread has a set of internal TCBs available for its exclusive use. TCBs are not shared amongst threads, and are to be modified only by the C TargetRTS or the timers. In the multi-threaded C TargetRTS, timer control blocks are moved back and forth between the requesting and timing threads.
Also, each thread has a linked-list of active TCBs, which are purposely stored in sorted order. That is to say, that when you start a timer, the timeout is checked, and inserted in order into the linked-list, where the smallest timeout values appear at the front of the queue, and the largest timeout values appear at the end of the queue. By inserting these items into the queue in sorted order, it is then possible to quickly determine the next timeout value by simply reviewing the first entry on the timer queue.