Table of Contents Previous Chapter 36 The Master Library
The symbol table is used for storing information mainly about the static properties of the SDL system, such as the block structure, connections of channels and the valid input signal set for processes. Some dynamic properties are also placed in the symbol table; for example the list of all active process instances of a process instance set. This is part of the node representing the process instance set.
The nodes in the symbol table are structs with components initialized in the declaration. During the initialization of the application, in the yInit function in generated code, a tree is built up from these nodes.
The symbol table is created in two steps:
- First, symbol table nodes are declared as structs with components initialized in the declaration (in generated code).
- Then, the yInit function (in generated code) updates some components in the nodes and builds a tree from the nodes. This operation is not needed in an application!
The following names can be used to refer to some of the nodes that are always present. These names are defined in scttypes.h.
xSymbolTableRoot;
xSystemId;
xEnvId;
xSrtN_SDL_Boolean;
xSrtN_SDL_Character;
xSrtN_SDL_Charstring;
xSrtN_SDL_Duration;
xSrtN_SDL_Integer;
xSrtN_SDL_Natural;
xSrtN_SDL_PId;
xSrtN_SDL_Real;
xSrtN_SDL_Time;
xSymbolTableRoot is the root node in the symbol table tree. Below this node the system node is inserted (referred to by xSystemId). After the system node, there is a node representing the environment of the system (xEnvId). Then there is one node for each package referenced from the SDL system. This is true also for the package predefined containing the predefined data types. The nodes for the predefined data types, that are sons to the node for the package predefined, can be directly referenced by the names xSrtN_SDL_xxx, according to the list above.
Nodes in the symbol table are placed in the tree exactly according its place of declaration in SDL. A node that represent an item declared in a block is placed as a child to that block node, and so on. The hierarchy in the symbol table tree will directly reflect the block structure and declarations within the blocks and processes.
A small example can be found in Figure 560 on page 2194. The following node types will be present in the tree:
---------------------------------------------------------------------
Node Type Description
---------------------------------------------------------------------
xSystemEC Represent the system or the system instance.
xSystemTypeEC Represents a system type.
xPackageEC Represents a package.
xBlockEC Represent blocks and block instances.
xBlockTypeEC Represents a block type.
xBlockSubstEC Represents a block substructure and can be
found as a child of a block node.
xProcessEC Represent processes and process instances.
The environment process node is placed after
the system node and is used to represent the
environment of the system.
xProcessTypeEC Represents a process type.
xServiceEC Represents a service or service instance.
xServiceTypeEC Represents a service type.
xProcedureEC Represents a procedure.
xOperatorEC Represents an operator diagram, i.e. an ADT
operator with an implementation in SDL.
xSignalEC Represents a signal or timer type.
xTimerEC
xRPCSignalEC Represents the implicit signals (pCALL,
pREPLY) used to implement RPCs.
xSignalParEC There will be one signal parameter node, as a
child to a signal, timer, and RPC signal node,
for each signal or timer parameter.
xStartUpSignalEC Represents a start-up signal, that is, the sig
nal sent to a newly created process contain
ing the actual FPAR parameters. An
xStartUpSignalEC node is always placed
directly after the node for its process.
xSortEC
Represents a newtype or a syntype.
xSyntypeEC
Struct A sort node representing a struct has one
Component Node
struct component node as child for each
(xVariableEC) struct component in the sort definition.
xLiteralEC A sort node similar to an enum type has one
literal node as child for each literal in literal
list.
xStateEC Represents a state and can be found as a child
to process and procedure nodes.
xVariableEC
Represents a variable (DCL) or a formal
xFormalParEC parameter (FPAR) and can be found as chil
dren of process and procedure nodes
xChannelEC
Represents a channel, a signal route, or a
xSignalRouteEC gate.
xGate
xRemoteVarEC Represents a remote variable definition.
xRemotePrdEC Represents a remote procedure definition.
---------------------------------------------------------------------
The nodes (the struct variables) will in generated code be given names according to the following table:
ySysR_SystemName
(system, system type, system instance)
yPacR_PackageName
yBloR_BlockName
(block, block type, block instance)
yBSuR_SubstructureName
yPrsR_ProcessName
(process, process type, process instance)
yPrdR_ProcedureName (procedure, operator)
ySigR_SignalName
(signal, timer, startup signal, RPC signal)
yChaR_ChannelName (channel, signal route, gate)
yStaR_StateName
ySrtR_NewtypeName (newtype, syntype)
yLitR_LiteralName
yVarR_VariableName
(variable, formal parameter, signal
parameter, struct component)
yReVR_RemoteVariable
yRePR_RemoteProcedure
In most cases it is of interest to refer to a symbol table node via a pointer. By taking the address of a variable according to the table above, i.e.
& yPrsR_Process1
such a reference is obtained. For backward compatibility, macros according to the following example is also generated for several of the entity classes:
#define yPrsN_ProcessName (&yPrsR_ProcessName)
The following type definitions, from the file scttypes.h, are used in connection with the symbol table:
typedef enum {
xExportEC,
xRemoteVarEC,
xRemotePrdEC,
xSignalrouteEC,
xStateEC,
xTimerEC,
xFormalParEC,
xLiteralEC,
xVariableEC,
xViewEC,
xBlocksubstEC,
xChannelsubstEC,
xPackageEC,
xProcedureEC,
xOperatorEC,
xProcessEC,
xProcessTypeEC,
xGateEC,
xSignalEC,
xSignalParEC,
xStartUpSignalEC,
xRPCSignalEC,
xSortEC,
xSyntypeEC,
xSystemEC,
xSystemTypeEC,
xBlockEC,
xBlockTypeEC,
xChannelEC,
xServiceEC,
xServiceTypeEC,
xMonitorCommandEC
} xEntityClassType;
typedef enum {
xPredef, xUserdef, xEnum,
xStruct, xArray, xGArray, xString,
xPowerSet, xGPowerSet, xInherits, xSyntype,
xUnion
} xTypeOfSort;
typedef char *xNameType;
typedef struct xIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
} xIdRec;
/*BLOCKSUBSTRUCTURE*/
typedef struct xBlockSubstIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
} xBlockSubstIdRec;
/*LITERAL*/
typedef struct xLiteralIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
} xLiteralIdRec;
/*PACKAGE*/
typedef struct xPackageIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
} xPackageIdRec;
/*SYSTEM*/
typedef struct xSystemIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xIdNode * Contents;
xPrdIdNode * VirtPrdList;
xSystemIdNode Super;
#ifdef XTRACE
int Trace_Default;
#endif
#ifdef XGRTRACE
int GRTrace;
#endif
#ifdef XMSCE
int MSCETrace;
#endif
} xSystemIdRec;
/*CHANNEL,SIGNALROUTE*/
#ifndef XOPTCHAN
typedef struct xChannelIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xSignalIdNode *SignalSet; /*Array*/
xIdNode *ToId; /*Array*/
xChannelIdNode Reverse;
} xChannelIdRec; /* And xSignalRouteEC.*/
#endif
/*BLOCK*/
typedef struct xBlockIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xBlockIdNode Super;
xIdNode * Contents;
xPrdIdNode * VirtPrdList;
xViewListRec * ViewList;
int NumberOfInst;
#ifdef XTRACE
int Trace_Default;
#endif
#ifdef XGRTRACE
int GRTrace;
#endif
#ifdef XMSCE
int MSCETrace;
int GlobalInstanceId;
#endif
} xBlockIdRec;
/*PROCESS*/
typedef struct xPrsIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xStateIdNode *StateList;
xSignalIdNode *SignalSet;
#ifndef XNOUSEOFSERVICE
xIdNode *Contents;
#endif
#ifndef XOPTCHAN
xIdNode *ToId; /*Array*/
#endif
int MaxNoOfInst;
#ifdef XNRINST
int NextNr;
int NoOfStaticInst;
#endif
xPrsNode *ActivePrsList;
xptrint VarSize;
#if defined(XPRSPRIO) || defined(XSIGPRSPRIO) || \
defined(XPRSSIGPRIO)
int Prio;
#endif
xPrsNode *AvailPrsList;
#ifdef XTRACE
int Trace_Default;
#endif
#ifdef XGRTRACE
int GRTrace;
#endif
#ifdef XBREAKBEFORE
#ifndef ULTRIXCC
char *(*GRrefFunc)
XPP((int, xSymbolType *));
#else
char *(*GRrefFunc) ();
#endif
int MaxSymbolNumber;
int SignalSetLength;
#endif
#ifdef XMSCE
int MSCETrace;
#endif
#ifdef XCOVERAGE
long int *CoverageArray;
long int NoOfStartTransitions;
long int MaxQueueLength;
#endif
void (*PAD_Function) XPP((xPrsNode));
xPrsIdNode Super;
xPrdIdNode * VirtPrdList;
xBlockIdNode InBlockInst;
#ifdef XBREAKBEFORE
char * RefToDefinition;
#endif
} xPrsIdRec;
#ifndef XNOUSEOFSERVICE
/*SERVICE*/
typedef struct xSrvIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xStateIdNode *StateList;
xSignalIdNode *SignalSet;
#ifndef XOPTCHAN
xIdNode *ToId;
#endif
xptrint VarSize;
#ifdef XBREAKBEFORE
#ifndef ULTRIXCC
char *(*GRrefFunc) XPP((int, xSymbolType *));
#else
char *(*GRrefFunc) ();
#endif
int MaxSymbolNumber;
int SignalSetLength;
#endif
#ifdef XCOVERAGE
long int *CoverageArray;
long int NoOfStartTransitions;
#endif
xSrvNode *AvailSrvList;
void (*PAD_Function) XPP((xPrsNode));
xSrvIdNode Super;
xPrdIdNode * VirtPrdList;
} xSrvIdRec;
#endif
/*PROCEDURE*/
typedef struct xPrdIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xStateIdNode *StateList;
xSignalIdNode *SignalSet;
xbool (*Assoc_Function)
XPP((xPrsNode));
xptrint VarSize;
xPrdNode *AvailPrdList;
#ifdef XBREAKBEFORE
#ifndef ULTRIXCC
char *(*GRrefFunc)
XPP((int, xSymbolType *));
#else
char *(*GRrefFunc) ();
#endif
int MaxSymbolNumber;
int SignalSetLength;
#endif
#ifdef XCOVERAGE
long int *CoverageArray;
#endif
xPrdIdNode Super;
xPrdIdNode * VirtPrdList;
} xPrdIdRec;
typedef struct xRemotePrdIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xRemotePrdListNode RemoteList;
} xRemotePrdIdRec;
/* SIGNAL, TIMER */
typedef struct xSignalIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xptrint VarSize;
xSignalNode *AvailSignalList;
#ifndef ULTRIXCC
xbool (*Equal_Timer)
XPP((void *, void *));
#else
xbool (*Equal_Timer) ();
#endif
#ifdef XFREESIGNALFUNCS
#ifndef ULTRIXCC
void (*Free_Signal) XPP((void *));
#else
void (*Free_Signal) ();
#endif
#endif
#ifdef XBREAKBEFORE
char *RefToDefinition;
#endif
} xSignalIdRec; /* And xTimerEC, xStartUpSignalEC,
and xRPCSignalEC.*/
/*STATE*/
typedef struct xStateIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
int StateNumber;
xInputAction *SignalHandlArray;
int *InputRef;
#ifndef ULTRIXCC
xInputAction (*EnablCond_Function)
XPP((XSIGTYPE, void *));
void (*ContSig_Function)
XPP((void *, int *, xIdNode *, int *));
#else
xInputAction (*EnablCond_Function) ();
void (*ContSig_Function) ();
#endif
int StateProperties;
#ifdef XCOVERAGE
long int *CoverageArray;
#endif
xStateIdNode Super;
#ifdef XBREAKBEFORE
char *RefToDefinition;
#endif
} xStateIdRec;
/*SORT*/
typedef struct xSortIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
#ifdef XFREEFUNCS
void (*Free_Function) XPP((void **));
#endif
#ifdef XREADANDWRITEF
int (*Read_Function) XPP((void *));
char *(*Write_Function) XPP((void *));
#endif
#ifdef XTESTF
xbool (*Test_Function) XPP((void *));
#endif
xptrint SortSize;
xTypeOfSort SortType;
xSortIdNode CompOrFatherSort;
xSortIdNode IndexSort;
long int LowestValue;
long int HighestValue;
} xSortIdRec;
/*VARIABLE,...*/
typedef struct xVarIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xSortIdNode SortNode;
xptrint Offset;
xptrint Offset2;
xbool IsAddress;
} xVarIdRec; /* And xFormalParEC and
xSignalParEC.*/
typedef struct xRemoteVarIdStruct {
xEntityClassType EC;
#ifdef XSYMBTLINK
xIdNode First;
xIdNode Suc;
#endif
xIdNode Parent;
#ifdef XIDNAMES
xNameType Name;
#endif
xptrint SortSize;
xRemoteVarListNode RemoteList;
} xRemoteVarIdRec;
There are also pointer types defined for each of the xECIdStruct according to the following example:
typedef XCONST struct xIdStruct *xIdNode;
The type definitions above define the contents in the symbol table nodes. Each xECIdStruct, where EC should be replaced by an appropriate string, have the first five components in common. These components are used to build the symbol table tree. To access these components, a pointer to a symbol table node can be type cast to any of the xIdECNode types. The type xIdNode is used as such general type, for example when traversing the tree.
The five components present in all xIdNode are:
- EC of type xEntityClassType. This component is used to determine what sort of SDL object the node represents. xEntityClassType is an enum type containing elements for all entity classes in SDL.
- First, Suc, and Parent of type xIdNode. These components are used to build the symbol table tree. First refers to the first child of the current node. Suc refers to the next brother, while Parent refers to the father node. Only Parent are needed in an application.
- Name of type xNameType, which is defined as char *. This component is used to represent the name of the current SDL object as a character string. Not needed in an application.
Next there are components depending on what entity class that is to be represented. Below we discuss the non-common elements in the other xECIdStruct.
- Content of type xIdNode *. This component contains a list of all channels in the system.
- VirtPrdList of type xPrdIdNode *. This is a list of all virtual procedures in this system instance.
- Super of type xSystemIdNode. This is a reference to inherited system type. In a system this component in null. In a system instance it is a reference to the instantiated system type.
- Trace_Default of type int. This component contains the current trace value defined for the system.
- GRTrace of type int. This component contains the current GR (graphical) trace value defined for the system.
- MSCETrace of type int. This component contains the current MSCE (Message Sequence Chart Editor) trace value defined for the system.
For channels, signal routes, and gates there are always two consecutive xChannelIdNodes in the symbol table, representing the two possible directions for a channel, signal route, or gate. The components are:
- SignalSet of type xIdNode *. This component represents the signal set of the channel in the current direction (a unidirectional channel has an empty signal set in the opposite direction).
SignalSet is an array with components refering to the xSignalIdNodes that represent the signals which are members of the signal set. The last component in the array is always a NULL pointer (the value (xSignalIdNode)0).
- ToId of type xIdNode *. This is an array of xIdNodes, where each array component is a pointer to a symbol table node representing an SDL object, which this Channel/Signal route/Gate is connected to (connected to in the sense: to the SDL objects that signals are sent forward to).
The SDL objects that may be referenced in ToId are channels, signal routes, gates, processes, and services. The last component in the array is always a NULL pointer (the value (xIdNode)0). See also "Channels and Signal Routes" on page 2193.
- Reverse of type xChannelIdNode. This is a reference to the symbol table node that represents the other direction of the same channel, signal route, or gate.
- Super of type xBlockIdNode. In a block, this component is NULL. In a block type this component is a reference to the block that this block inherits from (NULL if no inheritance). In a block instance, this is a reference to the block type that is instantiated.
- Contents of type xIdNode *. In a block instance, these components contains list of:
- The process instantiations in the block
- The signal routes in the block
- The outgoing gates from the block
- The processes in the block
- The gates into process instantiations in the block.
- VirtPrdList of type xPrdIdNode *. This is a list of all virtual procedures in this block instance.
- ViewList of type xViewListRec *. This is a list of all revealed variables in the block or block instance.
- NumberOfInst of type int. This is the number of block instances in a block instance set. The component is thus only relevant for a block instance.
- Trace_Default of type int. This component contains the current value of the trace defined for the block.
- GRTrace of type int. This component contains the current value of the GR trace defined for the block.
- MSCETrace of type int. This component contains the current MSCE (Message Sequence Chart Editor) trace value defined for the block.
- GlobalInstanceId of type int. This component is used to store a unique id needed when performing MSCE trace.
- StateList of type xStateIdNode *. This a list of references to the xStateIdNode for this process or process type. Using the state value of an executing process, this list can be used to find the corresponding xStateIdNode.
- SignalSet of type xIdNode *. This represents the valid input signal set of the process or process type.
SignalSet is an array with components that refer to xSignalIdNodes that represent the signals and timers which are part of the signal set. The last component in the array is always a NULL pointer (the value (xSignalIdNode)0).
- Contents of type xIdNode *. This is an array containing references to the xSrvIdNodes of the services and service instances in this process.
- ToId of type xIdNode *. This is an array of xIdNode, where each array component is a pointer to an IdNode representing an SDL object that this process or process instance is connected to (connected to in the sense: to the SDL objects that signals are sent forward to).
The SDL objects that may be referenced in ToId are channels, signal routes, gates, processes, and services. The last component in the array is always a NULL pointer (the value (xIdNode)0). See also section "Channels and Signal Routes" on page 2193.
- MaxNoOfInst of type int. This represents the maximum number of concurrent processes that may exist according to the specification for the current process or process instance. An infinite number of concurrent processes is represented by -1.
- NextNo of type int. This is the instance number that will be assigned to the next instance that is created of this process instance set.
- NoOfStaticInst of type int.This component contains the number of static instance of this process instance set that should be present at start up. Used for process and process instance.
- ActivePrsList of type xPrsNode *. This is the address of a pointer to the "first" in the (single linked) list of active process instances of the current process or process instantiation.
The list is continued using the NextPrs component in the xPrsRec struct that is used to represent a process instance. The order in the list is such that the first created of the active process instances is last, and the latest created is first.
- VarSize of type xptrint. The size, in bytes, of the data area used to represent the process (the struct: yVDef_ProcessName).
- Prio of type int. This represents the process priority.
- AvailPrsList of type xPrsNode. This is the address to the avail list pointer for process instances that have stopped. The data area can later be reused in subsequent Create actions on this process or process instantiation.
- Trace_Default of type int. This component contains the current value of the trace defined for the process.
- GRTrace of type int. This component contains the current value of the GR trace defined for the process.
- GRrefFunc, which is a pointer to a function that, given a symbol number (number assigned to a process symbol), will return a string containing the SDT reference to that symbol.
- MaxSymbolNumber of type int. This component is the number of symbols contained in the current process or process type.
- SignalSetLength of type int. This component is the number of signals contained in the signal set of the current process or process type.
- MSCETrace of type int. This component contains the current MSCE (Message Sequence Chart Editor) trace value defined for the process.
- CoverageArray of type long int. This component is used as an array over all symbols in the process. Each time a symbol is executed the corresponding array component is increased by 1.
- NoOfStartTransitions of type long int. This component is used to count the number of times the start transition of the current process is executed. This information is presented in the coverage tables.
- MaxQueueLength of type long int. This component is used to register the maximum input port length for any instance of the current process. The information is presented in the coverage tables.
- PAD_Function, which is a pointer to a function. This pointer refers to the yPAD_ProcessName function for the current process. This function is called when a process instance of this type is to execute a transition. The PAD_Functions will of course be part of generated code, as they contain the action defined in the process graphs.
- Super of type xPrsIdNode. In a process this component is NULL. In a process type this component is a reference to the process type that this process type inherits from (NULL if no inheritance). In a process instance set, this is a reference to the process type that is instantiated.
- VirtPrdList of type xPrdIdNode *. This is a list of all virtual procedures in this process instantiation.
- InBlockInst of type xBlockIdNode. This component is a reference to the block instance set (if any) that this process or process instantiation is part of.
- RefToDefinition of type char *. This is the SDT reference to this process.
- StateList of type xStateIdNode *. This a list of the references to the xStateIdNode for this service or service type. Using the state value of an executing service, this list can be used to find the corresponding xStateIdNode.
- SignalSet of type xIdNode *. This represents the valid input signal set of the service or service type.
SignalSet is an array with components that refer to xSignalIdNodes that represent the signals and timers which are part of the signal set. The last component in the array is always a NULL pointer (the value (xSignalIdNode)0).
- ToId of type xIdNode *. This is an array of xIdNode, where each array component is a pointer to an IdNode representing an SDL object that this service or service instance is connected to (connected to in the sense: to the SDL objects that signals are sent forward to).
The SDL objects that may be referenced in ToId are channels, signal routes, gates, processes, and service. The last component in the array is always a NULL pointer (the value (xIdNode)0). See also section "Channels and Signal Routes" on page 2193.
- VarSize of type xptrint. The size, in bytes, of the data area used to represent the service (the struct: yVDef_ServiceName).
- GRrefFunc, which is a pointer to a function that, given a symbol number (number assigned to a service symbol), will return a string containing the SDT reference to that symbol.
- MaxSymbolNumber of type int. This component is the number of symbols contained in the current service or service type.
- SignalSetLength of type int. This component is the number of signals contained in the signal set of the current service or service type.
- CoverageArray of type long int. This component is used as an array over all symbols in the service. Each time a symbol is executed the corresponding array component is increased by 1.
- NoOfStartTransitions of type long int. This component is used to count the number of times the start transition of the current service is executed. This information is presented in the coverage tables.
- AvailSrvList of type xSrvNode. This is the address to the avail list pointer for service instances that have stopped. The data area can later be reused.
- PAD_Function, which is a pointer to a function. This pointer refers to the yPAD_ServiceName function for the current service. This function is called when a service instance of this type is to execute a transition. The PAD_Functions will of course be part of generated code, as they contain the action defined in the service graphs.
- Super of type xSrvIdNode. In a service this component is NULL. In a service type this component is a reference to the service type that this service type inherits from (NULL if no inheritance). In a service instantiation this is a reference to the service type that is instantiated.
- VirtPrdList of type xPrdIdNode *. This is a list of all virtual procedures in this service instantiation.
- StateList of type xStateIdNode *. This a list of references to the xStateIdNode for this process or process type. Using the state value of an executing process, this list can be used to find the corresponding xStateIdNode.
- SignalSet of type xIdNode *. This represents the valid input signal set of the process or process type.
SignalSet is an array with components that refer to xSignalIdNodes that represent the signals and timers which are part of the signal set. The last component in the array is always a NULL pointer (the value (xSignalIdNode)0).
- Assoc_Function, which is a pointer to a function. This pointer refers to the yProcedureName function for the current procedure. This function is called when the SDL procedure is called and will execute the appropriate actions. The yProcedureName functions will, of course, be part of generated code as they contain the action defined in the procedure graphs.
- VarSize of type xptrint. The size, in bytes, of the data area used to represent the procedure (struct yVDef_ProcedureName).
- AvailPrdList of type xPrdNode *. This is the address of the avail list pointer for the data areas used to represent procedure instances. At a return action the data area is placed in the avail list and can later be reused in subsequent Calls of this procedure type.
- GRrefFunc, which is a pointer to a function that given a symbol number (number assigned to a procedure symbol) will return a string containing the SDT reference to that symbol.
- MaxSymbolNumber of type int. This component is the number of symbols contained in the current procedure.
- SignalSetLength of type int. This component is the number of signals contained in the signal set of the current procedure.
- CoverageArray of type long int. This component is used as an array over all symbols in the procedure. Each time a symbol is executed the corresponding array component is increased by 1.
- Super of type xPrdIdNode. This component is a reference to the procedure that this procedure inherits from (NULL if no inheritance).
- VirtPrdList of type xPrdIdNode *. This is a list of all virtual procedures in this procedure.
- RemoteList of type xRemotePrdListNode. This component is the start of a list of all processes that exports this procedure. This list is a linked list of xRemotePrdListStructs, where each node contains a reference to the exporting process.
- VarSize of type xptrint. The size, in bytes, of the data area used to represent the signal (the struct: yPDef_SignalName).
- AvailSignalList of type xSignalNode *. This is the address to the avail list pointer for signal instances of this signal type.
- Equal_Timer, which is a pointer to a function. This pointer only refers to a function when this node is used to represent a timer with parameters.
In this case the referenced function can be used to investigate if the parameters of two timers are equal or not, which is necessary at reset actions. The Equal_Timer functions will be part of generated code. These functions are called from the functions xRemoveTimer and xRemoveTimerSignal, both defined in sctsdl.c
- Free_Signal, which is a function. This function takes a signal reference and returns any dynamic data referenced from the signal parameters to the pool of available memory.
- RefToDefinition of type char *. The SDT reference to the definition of the signal or timer.
- StateNumber of type int. The int value used to represent this state.
- SignalHandlArray of type xInputAction *. This component refers to an array of xInputAction, where xInputAction is an enum type with the possible values xNotInSignalSet, xInput, xSave, xDiscard, xEnablCond, xPrioInput.
The array will have the same number of components as the SignalSet array in the node representing the process in which this state is contained. Each position in the SignalHandlArray represents the way the signal in the corresponding position in the SignalSet array in the process should be treated in this state.
The last component in the SignalHandlArray is equal to xNotInSignalSet, which corresponds to the 0 value last in the SignalSet.
If the SignalHandlArray contains the value xInput, xSave, or xDiscard at a given index, the way to handle the signal is obvious. If the SignalHandlArray contains the value xEnablCond, it is, however, necessary to calculate the enabling condition expression to know if the signal should cause an input or should be saved. This calculation is exactly the purpose of the EnablCond_Function described below.
- InputRef of type int *. This component is an array. If the SignalHandlArray contains xInput, xPrioInput, or xEnablCond at a certain index, this InputRef contains the symbol number for the corresponding input symbol in the graph.
- EnablCond_Function, which is a function that returns xInputAction. If the state contains any enabling conditions, this pointer will refer to a function. Otherwise it refers to 0. An EnablCond_Function takes a reference to an xSignalIdNode (referring to a signal) and a reference to a process instance and calculates the enabling condition for the input of the current signal in the current state of the given process instance.
The function returns either of the values xInput or xSave. The EnablCond_Functions will of course be part of generated code, as they contain enabling condition expressions. These functions are called from the function xFindInputAction in the file sctsdl.c xFindInputAction is used by the SDL_Output and SDL_Nextstate functions.
- ContSig_Function, which is a function returning int. If the state contains any continuous signals, this pointer will refer to a function. Otherwise it refers to 0.
- StateProperties of type int. In this component the three least significant bits are used to indicate:
- If any enabling condition or continuous signal expression in the state contains a reference to an object that might change its value even though the process does not execute any actions.
- If there are any priority inputs in the state.
- If there are any virtual priority inputs in the state.
Objects according to the first item in the list are: Now, Active (timer is active), Import, View, and Sender. StateProperties is used in the function SDL_Nextstate to take appropriate actions when a process enters a state.
- CoverageArray of type long int. This component is used as an array over the signalset (+1) of the process. Each time an input operation is performed, the corresponding array component is increased by 1. The last component, at index equal to the length of the signalset, is used to record the number of continuous signals "received" in the state. The information stored in this component is presented in the coverage table.
- Super of type xPrdIdNode. This component is a reference to the procedure that this procedure inherits from (NULL if no inheritance).
- RefToDefinition of type char *. The SDT reference to the definition of the state (one of the symbols where this state is defined).
- Free_Function, which is a function. This function pointer is non-0 only for Charstring and for types containing Charstrings, as well as for types where the user has provided a free function. The Free_Function are used to return dynamic memory to the pool of dynamic memory.
- Read_Function, which is a function. These functions are used to read values of the current sort (mainly in the monitor). The pointer is non-0 for the predefined SDL data types and for the types where the user has provided a Read_Function.
- Write_Function, which is a function. These functions are used to write values of the current sort (mainly in the monitor). The pointer is non-0 for the predefined SDL data types and for the types where the user has provided a Write_Function.
- Test_Function, which is a function returning xbool. This function is non-0 for all types containing range conditions. The function pointers are used by the monitor system to check the validity of a value when assigning it to a variable.
- SortSize of type xptrint. This component represents the size, in bytes, of a variable of the current sort.
- SortType of type xTypeOfSort. This component indicates the type of sort. Possible values are: xPredef, xUserdef, xEnum, xStruct, xArray, xGArray, xString, xPowerSet, xGPowerSet, xInherits, xSyntype, and xUnion.
- CompOrFatherSort of type xSortIdNode. This is a pointer to the SortIdNode that represents the component sort.
- IndexSort of type xSortIdNode. This is a pointer to the SortIdNode that represents the index sort.
- In xGArray, LowestValue is used as the offset of Data in the xxx_ystruct.
- In xGArray, HighestValue is used as the size of the xxx_ystruct.
- CompOrFatherSort of type xSortIdNode. This is a pointer to the SortIdNode that represents the component sort.
SortType is xPowerSet, xGPowerSet
- CompOrFatherSort of type xSortIdNode. This is a pointer to the SortIdNode that represents the component sort.
- CompOrFatherSort, of type xSortIdNode. This is a pointer to the SortIdNode that represents the inherited sort.
- CompOrFatherSort, of type xSortIdNode. This is a pointer to the SortIdNode that represents the father sort (the newtype from which the syntype originates).
- LowestValue, of type int. If the syntype can be used as an index in an array (translated to a C array) then this value is the lowest value in the syntype range, otherwise it is 0.
- HighestValue, of type int. If the syntype can be used as an index in an array (translated to a C array) then this value is the highest value in the syntype range, otherwise it is 0. The LowestValue and HighestValue are used by the monitor when it handles arrays with this type as index type.
- SortNode of type xSortIdNode. This component is a pointer to the SortIdNode that represents the sort of this variable or parameter.
- Offset of type xptrint. This component represents the offset, in bytes, within the struct that represents the process or procedure variables, the signal parameter, or the SDL struct. In other words, this is the relative place of this component within the struct.
- Offset2 of type xptrint. For a formal parameter in a process this component represents the offset, in bytes, of a formal parameter in the StartUpSignal. For an exported variable in a process this component represents the offset, in bytes, of the exported value for this variable.
- IsAddress of type xbool. This component is only used for procedure formal parameters and is then used to indicate if the parameter in IN (IsAddress is (xbool)0) or IN/OUT (IsAddress is (xbool)1).
- SortSize of type xptrint. This component is the size of the type of the exported variables.
- RemoteList of type xRemoteVarListNode. This component is the start of a list of all processes that exports this variable. This list is a linked list of xRemoteVarListStructs, where each node contains a reference to the exporting process and the Offset where to find the exported value.
A signal is represented by a struct type. The xSignalRec struct, defined in scttypes.h, is a struct containing general information about a signal except from the signal parameters. In scttypes.h the following information about signals can be found:
#ifdef XMSCE
#define GLOBALINSTID int GlobalInstanceId;
#else
#define GLOBALINSTID
#endif
#ifdef XENV_CONFORM_2_3
#define XSIGNAL_VARP void * VarP;
#else
#define XSIGNAL_VARP
#endif
#define SIGNAL_VARS \
xSignalNode Pre; \
xSignalNode Suc; \
int Prio; \
SDL_PId Receiver; \
SDL_PId Sender; \
xSignalIdNode NameNode; \
XSIGNAL_VARP \
GLOBALINSTID
typedef struct xSignalStruct *xSignalNode;
typedef struct xSignalStruct {
SIGNAL_VARS
} xSignalRec;
The xSignalNode type is thus a pointer type which is used to refer to allocated data areas of type xSignalRec. The components in the xSignalRec struct are used as follows:
- Pre and Suc. These pointers are used to link a signal into the input port of the receiving process instance.
The input port is a doubly linked list of signals. Suc is also used to link a signal into the avail lists for the current signal type. This list can be found in the SignalIdNode that represents this signal type. If the signal is in the avail list Pre is 0.
- Prio is used to represent the priority of the signal instance. Signal priorities are used by continuous signals and by ordinary signals if signal priorities are defined (signal priority is a possible extension provided in the product).
- Receiver is used to reference the receiver of the signal. It is either set in the output statement (OUTPUT TO), or calculated (OUTPUT without TO).
- Sender is the PId value of the sending process instance. This value is necessary to provide the SDL function SENDER.
- NameNode is a reference to the xSignalIdNode representing the signal type and thus defines the signal type of this signal instance.
- VarP is a pointer introduced via the macro XSIGNAL_VARP to make signal compatible with SDT2.3. Normally this components is not present.
- GlobalInstanceId is used in the MSCE trace as a unique identification of the signal instance.
A signal without parameters are represented by a xSignalStruct, while for signals with parameters a struct type named yPDef_SignalName and a pointer type referencing this struct type (yPDP_SignalName) are defined in generated code. The struct type will start with the SIGNAL_VARS macro and then have one component for each signal parameter, in the same order as the signal parameters are defined. The components will be named Param1, Param2, and so on.
Example 136
typedef struct {
SIGNAL_VARS
SDL_Integer Param1;
SDL_Boolean Param2;
} yPDef_sig;
typedef yPDef_sig *yPDP_sig;
These types would represent a signal sig(Integer, Boolean).
As all signals starts with the components defined in SIGNAL_VARS it is possible to type cast a pointer to a signal, to the xSignalNode type, if only the components in SIGNAL_VARS is to be accessed.
In sctos.c there are two functions, xGetSignal and xReleaseSignal, where data areas for signal are handled:
xSignalNode xGetSignal(
xSignalIdNode SType,
SDL_PId Receiver,
SDL_PId Sender )
void xReleaseSignal( xSignalNode *S )
xGetSignal takes a reference to the SignalIdNode identifying the signal type and two PId values (sending and receiving process instance) and returns a signal instance. xGetSignal first looks in the avail list for the signal type (the component AvailSignalList in the SignalIdNode for the signal type) and reuses any available signal there. Only if the avail list is empty new memory is allocated. The component VarSize in the SignalIdNode for the signal type provides the size information needed to correctly allocate the yPDef_SignalName even though the type is unknown for the xGetSignal function.
The function xReleaseSignal takes the address of an xSignalNode pointer and returns the referenced signal to the avail list for the signal type. The xSignalNode pointer is then set to 0.
The function xGetSignal is used:
- In generated code (output, set, reset)
- In a number of places in the library:
SDL_Create
SDL_SimpleReset
SDL_Nextstate (to handle continuous signals)
- In the postmaster communication section and in the monitor to obtain signal instances.
The function xReleaseSignal is used by:
- SDL_Nextstate
- SDL_Stop, in both cases to release the signal that initiated the transition.
In this subsection the signal handling operation is only outlined. More details will be given in the section treating processes. See "Output and Input of Signals" on page 2175.
Signal instances are sent using the function SDL_Output. That function takes a signal instance and inserts it into the input port of the receiving process instance.
If the receiver is not already in the ready queue (the queue containing the processes that can perform a transition, but which have not yet been scheduled to do so) and the current signal may cause an immediate transition, the process instance is inserted into the ready queue.
If the receiver is already in the ready queue or in a state where the current signal should be saved, the signal instance is just inserted into the input port.
If the signal instance can neither cause a transition nor should be saved, it is immediately discarded (the data area for the signal instance is returned to the avail list).
The input port is scanned during nextstate operations, according the rules of SDL, to find the next signal in the input port that can cause a transition. Signal instances may then be saved or discarded.
There is no specific input function, instead this behavior is distributed both in the runtime library and in the generated code. The signal instance that should cause the next transition to be executed is removed from the input port in the main loop (the scheduler), immediately before the PAD function for the current process is called. The PAD function is the function where the behavior of the process is implemented and is part of the generated code. The assignment of the signal parameters to local SDL variables is one of the first actions performed by the PAD function.
The signal instance that caused a transition is released and returned to the avail list in the nextstate or stop action that ends the current transition.
A timer with parameters is represented by a type definition, where the timer parameters are defined, in exactly the same way as for a signal definition, see "Data Structure Representing Signals and Timers" on page 2157. At runtime, all timers that are set and where the timer time has not expired, are represented by a xTimerRec struct and a signal instance:
#define TIMER_VARS \
xSignalNode Pre; \
xSignalNode Suc; \
int Prio; \
SDL_PId Receiver; \
SDL_PId Sender; \
xSignalIdNode NameNode; \
GLOBALINSTID \
SDL_Time TimerTime;
typedef xTimerRec *xTimerNode;
typedef struct xTimerStruct {
TIMER_VARS
} xTimerRec;
The TIMER_VARS is and must be identical to the SIGNAL_VARS macro, except for the TimerTime component last in the macro. A timer with parameters have yPDef_timername and yPDP_timername types in generated code exactly as a signal (see previous section), except that SIGNAL_VARS is replaced by TIMER_VARS.
During its life-time a timer have two different appearances. First it is a timer waiting for the timer time to expire. In that phase the timer is inserted in the xTimerQueue. When the timer time expires the timer becomes a signal and is inserted in the input port of the receiver just like any other signal. Due to the identical typedefs for xSignalRec and xTimerRec, there are no problems with type casting between xTimerNode and xSignalNode types.
When a timer is treated as a signal the components in the xTimerRec are used in the same ways as for a xSignalRec. While the timer is in the timer queue, the components are used as follows:
- Pre and Suc are pointers used to link the xTimerRec into the timer queue (the queue of active timers, see below).
- TimerTime is the time given in the Set operation.
The queue mentioned above, the timer queue for active timers is represented by the component xTimerQueue in the variable xSysD:
xTimerNode xTimerQueue;
The variable is initialized in the function xInitKernel in sctsdl.c. xTimerQueue is initialized it refers to the queue head of the timer queue.
The queue head is an extra element in the timer queue that does not represent a timer, but is introduced as it simplifies the algorithms for queue handling. The TimerTime component in the queue head is set to a very large time value (xMaxTime).
The timer queue is thus a doubly linked list with a list head and it is sorted according to the timer times, so that the timer with lowest time is at the first position.
The xTimerRec structs are allocated and reused in the same way as signal.
From the SDL point of view, timers are handled in:
- Timer definitions
- Set and reset operations
- Timer outputs.
The timer output is the event when the timer time has expired and the timer signal is sent. After that, a timer signal is treated as an ordinary signal. These operations are implemented as follows:
void SDL_Set(
SDL_Time T,
xSignalNode S )
This function, which represents the Set operation, takes the timer time and a signal instance as parameters. It first uses the signal instance to make an implicit reset (see reset operation below) It then updates the TimerTime component in S and inserts S into the timer queue at the correct position.
The SDL_Set operation is used in generated code, together with xGetSignal, in much the same way as SDL_Output. First a signal instance is created (by xGetSignal), then timer parameters are assigned their values, and finally the Set operation is performed (by SDL_Set).
void SDL_Reset( xSignalNode *TimerS )
void SDL_SimpleReset(
xPrsNode P,
xSignalIdNode TimerId )
Two functions are used to represent the SDL action reset. SDL_SimpleReset is used for timers without parameters and SDL_Reset for timers with parameters.
SDL_Reset uses the two functions xRemoveTimer and xRemoveTimerSignal to remove a timer in the timer queue and to remove a signal instance in the input port of the process. It then releases the signal instance given as parameter. This signal is only used to carry the parameter values given in the reset action.
The function SDL_SimpleReset is implemented in the same way as SDL_Reset, except that it creates its own signal instance (without parameters).
At a reset action the possibly found timer is removed from the timer queue and returned to the avail list. A found signal instance (in the input port) is removed from the input port and returned to the avail list for the current signal type.
static void SDL_OutputTimerSignal( xTimerNode T )
The SDL_OutputTimerSignal is called from the main loop (the scheduler) when the timer time has expired for the timer first in the timer queue. The corresponding signal instance is then sent.
SDL_OutputTimerSignal takes a pointer to an xTimerRec as parameter, removes it from the timer queue and sends as an ordinary output using the function SDL_Output.
It can be checked if timer is active by using a call to the function SDL_Active. This function is used in generated code to represent the SDL operator active.
SDL_Boolean SDL_Active (
xSignalIdNode TimerId,
xPrsNode P )
-----------------------------------------------------------------------
Note:
Only timers without parameters can be tested. This is a restriction in
the C Code Generator.
-----------------------------------------------------------------------
There is one more place where timers are handled. When a process instance performs a stop action all timers in the timer queue connected to this process instance are removed. This is performed by calling the function xRemoveTimer with the first parameter equal to 0.
A process instance is represented by two structs, an xLocalPIdRec and a struct containing both the general process data and the local variables and formal parameters of the process (yVDef_ProcessName), see also Figure 554 on page 2165. The reason for having both the xLocalPIdRec and the yVDef_ProcessName will be discussed under "Create and Stop Operations" on page 2171.
Figure 554 : Representation of a Process Instance.
-----
(fig)
-----
The corresponding type definitions, which can be found in scttypes.h, are:
#ifdef XPRSSENDER
#define XPRSSENDERCOMP SDL_PId Sender;
#else
#define XPRSSENDERCOMP
#endif
#ifdef XTRACE
#define XTRACEDEFAULTCOMP int Trace_Default;
#else
#define XTRACEDEFAULTCOMP
#endif
#ifdef XGRTRACE
#define XGRTRACECOMP int GRTrace;
#else
#define XGRTRACECOMP
#endif
#ifdef XMSCE
#define XMSCETRACECOMP int MSCETrace;
#else
#define XMSCETRACECOMP
#endif
#ifdef XMONITOR
#define XINTRANSCOMP xbool InTransition;
#else
#define XINTRANSCOMP
#endif
#ifdef XMONITOR
#define XCALL_ADDR int CallAddress;
#else
#define XCALL_ADDR
#endif
#ifndef ULTRIXCC
#define XPAD_STRUCT_COMP \
void (*RestartPAD) XPP((xPrsNode VarP));
#else
#define XPAD_STRUCT_COMP void (*RestartPAD) ();
#endif
#ifndef XNOUSEOFSERVICE
#define XSERVICE_COMP xSrvNode ActiveSrv; xSrvNode
SrvList;
#else
#define XSERVICE_COMP
#endif
#define PROCESS_VARS \
xPrsNode Pre; \
xPrsNode Suc; \
int RestartAddress; \
xPrdNode ActivePrd; \
XPAD_STRUCT_COMP \
XCALL_ADDR \
XSERVICE_COMP \
xPrsNode NextPrs; \
SDL_PId Self; \
xPrsIdNode NameNode; \
int State; \
xSignalNode Signal; \
xInputPortRec InputPort; \
SDL_PId Parent; \
SDL_PId Offspring; \
int BlockInstNumber; \
XSIGTYPE pREPLY_Waited_For; \
xSignalNode pREPLY_Signal; \
XPRSSENDERCOMP \
XTRACEDEFAULTCOMP \
XGRTRACECOMP \
XMSCETRACECOMP \
XINTRANSCOMP
typedef struct {
xPrsNode PrsP;
int InstNr;
int GlobalInstanceId;
} xLocalPIdRec;
typedef xLocalPIdRec *xLocalPIdNode;
typedef struct {
int GlobalNodeNr;
xLocalPIdNode LocalPId;
} SDL_PId;
typedef struct xPrsStruct *xPrsNode;
typedef struct xPrsStruct {
PROCESS_VARS
} xPrsRec;
A PId value is thus a struct containing two components:
- The global node number
- A pointer to a xLocalPIdRec struct.
The use of the global node number is discussed in the chapter "The Application Library".
A xLocalPIdRec contains the following two components:
- PrsP of type xPrsNode. This component is a pointer to the xPrsRec struct that is part of the representation of the process instance.
- InstNr of type int. This is the instance number of the current process instance, which is used in the communication with the user in the monitor and in dynamic error messages.
- GlobalInstanceId is used in MSCE traces to have a unique identification of the process instance.
A xPrsRec struct contains the following components described below. As each yVDef_ProcessName struct contains the PROCESS_VARS macro as first item, it is possible to cast pointer values between a pointer to xPreRec and a pointer to a yVDef_ProcessName struct.
- Pre and Suc of type xPrsNode. These components are used to link the process instance in the ready queue (see below).
- RestartAddress of type int. This component is used to find the appropriate SDL symbol to continue execute from.
- ActivePrd of type xPrdNode. This is a pointer to the xPrdRec that represents the currently executing procedure called from this process instance. The pointer is 0 if no procedure is currently called.
- RestartPAD, which is a pointer to a PAD function. This component refers to the PAD function where to execute the sequence of SDL symbols. RestartPAD is used to handle inheritance between process types.
- CallAddress of type int. This component contains the symbol number of the procedure call currently executed by this process.
- ActiveSrv of type xSrvNode. This component contains a reference to the currently active service (or latest active service) in this process.
- SrvList of type xSrvNode. This component contains a reference to the first service contained in this process The component NextSrv in the struct representing a service can be used to find next active services in the process.
- NextPrs of type xPrsNode. This component is used to link the process instance either in the active list or in the avail list for this process type. The start of these two lists are the components ActivePrsList and AvailPrsList in the IdNode representing the current process type.
- Self of type SDL_PId. This is the PId value of the current process instance.
- NameNode of type xPrsIdNode. This is a pointer to the PrsIdNode representing the current process or process instantiation.
- State of type int. This component contains the int value used to representing the current state of the process instance.
- Signal of type xSignalNode. This is a pointer to a signal instance. The referenced signal is the signal that will cause the next transition by the current process instance, or that caused the transition that is currently executed by the process instance.
- InputPort of type xInputPortRec. This is the queue head in the doubly linked list that represents the input port of the process instance. The signals are linked in this list using the Pre and Suc components in the xSignalRec struct.
- Parent of type SDL_PId. This is the PId value of the parent process (according to the rules of SDL). A static process instance has parent equal to NULL.
- Offspring of type SDL_PId. This is the PId value of the latest created process instance (according to the rules of SDL). A process instance that has not created any processes has offspring equal to NULL.
- BlockInstNumber of type int. If the process is part of a block instance set, this component indicates which of the blocks that the process belongs to.
- pREPLY_Waited_For of type xSignalIdNode. When a process is waiting in the implicit state for the pREPLY signal in a RPC call, this components is used to store the IdNode for the expected pREPLY signal.
- pREPLY_Signal of type xSignalNode. When a process receives a pCALL signal, i.e. accepts a RPC, it immediately creates the return signal, the pREPLY signal. This component is used to refer to this pREPLY signal until it is sent.
- Sender of type SDL_PId. This component represents the SDL concept Sender.
- Trace_Default of type int. This component contains the current value of the trace defined for the process instance.
- GRTrace of type int. This component contains the current value of the GR trace defined for the process instance.
- MSCETrace of type int. This component contains the current MSCE trace value for the process instance.
- InTransition of type xbool. This component is true while the process is executing a transition and it is false while the process is waiting in a state. The monitor system needs this information to be able to print out relevant information.
The ready queue is a doubly linked list with a head. It contains the process instances that can execute an immediate transition, but which has not been allowed to complete that transition. Process instances are inserted into the ready queue during output operations and nextstate operations and are removed from the ready queue when they execute the nextstate or stop operation that ends the current transition. The head in the ready queue, which is an object in the queue that does not represent any process but is inserted only to simplify the queue operations, is referenced by the xSysD component:
xPrsNode xReadyQueue;
This component is initiated in the function xInitKernel and used throughout the runtime library to reference the ready queue.
Scheduling of events is performed by the function xMainLoop, which is called from the main function in generated code after the initialization is performed.
void xMainLoop()
The strategy to have all interesting queues (the ready queue, the timer queue, and the input ports) sorted in the correct order is used in the library. Sorting is thus performed when an object is inserted into a queue, which means that scheduling is a simple task: select the first object in the timer queue or in the ready queue and submit it for execution.
There are several versions of the body of the endless loop in the function xMainLoop, which are used for different combinations of compilation switches. When it comes to scheduling of transitions and timer outputs they all have the following outline:
while (1) {
if ( xTimerQueue->Suc->TimerTime <= SDL_Now() )
SDL_OutputTimerSignal( xTimerQueue->Suc );
else if ( xReadyQueue->Suc != xReadyQueue) {
xRemoveFromInputPort(xReadyQueue->Suc->Signal);
xReadyQueue->Suc->Sender =
xReadyQueue->Suc->Signal->Sender;
(*xReadyQueue->Suc->RestartPAD)(xReadyQueue->Suc);
}
}
or, in descriptive terms:
while (1) {
if ( there is a timer that has expired )
send the corresponding timer signal;
else if ( there is a process that can execute
a transition ) {
remove the signal causing the transition
from input port;
set up Sender in the process to Sender of
the signal;
execute the PAD function for the process;
}
}
The different versions of the main loop handle different combinations of compilation switches. Other actions necessary in the main loop are dependent of the compilation switches. Example of such actions are:
- Handling of the monitor
- Calling the xInEnv function
- Handling real time or simulated time
- Delay execution up to the next scheduled event
- Handling enabling conditions and continuous signals that need to be recalculated.
A process instance is, while it is active, represented by the two structs:
- xLocalPIdRec
- The yVDef_ProcessName struct.
These two structs are dynamically allocated. A PId value is also a struct (not allocated) containing two components, GlobalNodeNr and LocalPId, where LocalPId is a pointer to the xLocalPIdRec. Figure 555 on page 2172 shows how the xLocalPIdRec and the yVDef_ProcesName structs representing a process instance are connected.
Figure 555 : A xLocalPIdRec and a xVDef_ProcessName
representing a Process
instance.
-----
(fig)
-----
When a process instance performs a stop action, the memory used for the process instance should be reclaimed and it should be possible to reuse in subsequent create actions. After the stop action, old (invalid) PId values might however be stored in variables in other process instances.
If a signal is sent to such an old PId value; that is, to a stopped process instance, it should be possible to find and perform appropriate actions. If the complete representation of a process instance is reused then this will not be possible. There must therefore remain some little piece of information and thus some memory for each process instance that has ever existed. This is the purpose of the xLocalPIdRec. These structs will never be reused. Instead the following (see Figure 556) will happen when the process instance in Figure 555 on page 2172 performs a stop action.
Figure 556 : The memory structure after the process in Figure 555 has
performeda stop action.
-----
(fig)
-----
A new xLocalPIdRec is allocated and its PrsP references the yVDef_ProcessName (InstNr is 0). The Self component in the yVDef_ProcessName is changed to reference this new xLocalPIdRec. The old xLocalPIdRec still references the yVDef_ProcessName. The yVDef_ProcessName is entered into the avail list for this process type.
To reuse the data area for a process instance at a create operation it is only necessary to remove the yVDef_ProcessName from the avail list and update the InstNr component in the xLocalPIdRec referenced by Self.
Using this somewhat complicated structure to represent process instances allows a simple test to see if a PId value refers to an active or a stopped instance:
If P is a PId variable then the following expression:
P.LocalPId == P.LocalPId->PrsP->Self.LocalPId
is true if the process instance is active and false if it is stopped.
The basic behavior of the create and stop operations is performed by the functions SDL_Create and SDL_Stop.
void SDL_Create(
xSignalNode StartUpSig,
xPrsIdNode PrsId )
void SDL_Stop( xPrsNode PrsP )
To create a process instance takes three steps performed in generated code:
- Call xGetSignal to obtain the start-up signal.
- Assign the actual process parameters to the start up signal parameters.
- Call SDL_Create with the start-up signal as parameter, together with the PrsIdNode representing the process to be created.
In xGetProcess the process instance is removed from the avail list of the process instance set (the component AvailPrsList in the PrsIdNode representing the process instance set), or if the avail list is empty new memory is allocated.
The process instance is linked into the list of active process instances (the component ActivePrsList in the PrsIdNode representing the process instance set). Both the avail list and the active list are single linked lists (without a head) using the component NextPrs in the yVDef_ProcessName struct as link.
To have an equal treatment of the initial transition and other transitions, the start state is implemented as an ordinary state with the name "start state" It is represented by 0. To execute the initial transition a "startup" signal is sent to the process. The start state can thus be seen as a state with one input of the startup signal and with save for all other signals. This implementation is completely transparent in the monitor, where startup signals are never shown in any way.
-------------------------------------------------------------
Note:
The actual values for FPARs are passed in the startup signal.
-------------------------------------------------------------
Two IdNodes that are not part of the symbol table tree are created to represent a start state and a startup signal.
xStateIdNode xStartStateId;
xSignalIdNode xStartUpSignalId;
These xSysD components are initialized in the function xInitSymbolTable, which is part of sctsdl.c.
At a stop operation the function SDL_Stop is called. This function will release the signal that caused the current transition and all other signals in the input port. It will also remove all timers in the timer queue that are connected to this process instance by calling xRemoveTimer with the first parameter equal to 0. It then removes the process executing the stop operation from the ready queue and from the active list of the process type and returns the memory to the avail list of the current process instance set.
There are three actions performed in generated code to send a signal. First xGetSignal is called to obtain a data area that represents the signal instance, then the signal parameters are assigned their values and finally the function SDL_Output is called to actually send the signal. First in the SDL_Output function there are a number of dynamic tests (check if receiver in TO-clause is not NULL and not stopped, check if there is a path to the receiver). If the output does not contain any TO-clause and the C Code Generator has not been able to calculate the receiver, the xFindReceiver function is called to calculate the receiver according to the rules of SDL.
Next, in SDL_Output signals to the environment are handled. Three cases can be identified here:
- The environment function xOutEnv is called.
- The corresponding function that sends signals via the SDT communication mechanism (xOutPM) is called.
- The signal is inserted into the input port of the function representing the environment (xEnv).
Finally, internal signals in the SDL system are treated. Here also three cases can be identified (how this is evaluated is described last in this subsection):
- The signal can cause an immediate transition by the receiver.
- The signal should be saved.
- The signal should be immediately discarded.
If the signal can cause an immediate transition, the signal is inserted into the input port of the receiver, and the receiving process instance is inserted into the ready queue.
If the signal should be saved, the signal is just inserted into the input port of the receiver.
If the signal should be discarded, the function xReleaseSignal is called to reused the data area for the signal.
When a signal is identified to be the signal that should cause the next transition by the current process instance (at an Output or Nextstate operation), the component Signal in the yVDef_ProcessName for the process is set to refer to the signal. The signal is still part of the input port list.
When the transition is to be executed, the signal is removed from the input port in the main loop (see "The Ready Queue, Scheduling" on page 2170) immediately before the PAD function for the process is called.
First in the PAD function, the parameters of the signal are copied to the local variables according to the input statement. In the ending Nextstate or Stop operation of the transition the signal instance is returned to the avail list.
There are two places in the run-tim kernel where it is necessary to evaluate how to handle signals (input, save, discard,...).
- At an Output operation to a currently idle process.
- At a Nextstate operation, when the process have signals in the input port.
This calculation is implemented in the run-time kernel function xFindInputAction.
typedef unsigned char xInputAction;
#define xNotInSignalSet (xInputAction)0
#define xInput (xInputAction)1
#define xSave (xInputAction)2
#define xDiscard (xInputAction)3
#define xEnablCond (xInputAction)4
#define xPrioInput (xInputAction)5
- static xInputAction xFindInputAction(
xSignalNode SignalId,
xPrsNode VarP,
xbool CheckPrioInput )
The parameters of this function is:
- SignalId, which is a pointer to a signal.
- VarP, which is a pointer to a process instance.
- CheckPrioInput, which is a boolean value indicating is the function should check only for priority inputs or for ordinary inputs.
As a result the function should return:
- The action that should be performed for this signal (input, save,...), taking all information about this process into account, like inheritance between processes, virtual - redefined transitions and so on.
- If the function result is xInput or xPrioInput, then the RestartPAD and RestartAddr components in the VarP struct should be updated with information about where this input can be found.
After this last update the correct transition can be started by the scheduler by just calling the function referenced by RestartPAD, which the as first action performs switch RestartAddr and starts execute the input symbol.
Figure 557 : Data structure used to evaluate the xFindInputAction.
-----
(fig)
-----
The algorithm to find the InputAction, the RestartAddr, and the RestartPAD is as follows:
- Let ProcessId become yVarP->NameNode and let StateId become ProcessId->StateList[yVarP->State].
- In ProcessId->SignalSet find the index (Index) where SignalId->NameNode is found. If the signal is not found, this signal is not in the signal set of the process, and the algorithm terminates returning the result xNotInSignalSet.
- StateId->SignalHandlArray[Index] now gives the action to be performed. If this value is xEnablCond, then the function StateId->EnablCond_Function is called. This function returns either xInput or xSave.
- If the result from step 3. is xInput, the algorithm terminates returning this value. yVarP->RestartAddr is also updated to
StateId->InputRef[Index], while yVarP->RestartPAD is updated to ProcessId->PAD_Function.
If the result from step 3. is xSave, the algorithm terminates returning this value.
If the result from step 3. is xDiscard and ProcessId->Super equal to NULL, then the algorithm terminates returning this value.
If the result from step 3. is xDiscard and ProcessId->Super not equal to NULL, then we are in a process type that inherits from another process type. We then have to perform step 2. - 4. again, with ProcessId assigned the value ProcessId->Super and StateIs assigned the value StateId->Super.
The nextstate operation is implemented by the SDL_Nextstate function, where the following actions are performed:
- The signal that caused the current transition (component Signal in the yVDef_ProcessName) is released and the state variable (component State in the yVDef_ProcessName) is updated to the new state.
- Then the input port of the process is scanned for a signal that can cause a transition. During the scan signals might be saved or discarded until a signal specified in an input is found. Priority inputs are treated according to the rules of SDL.
- If no signal that can cause a transition is found, a check is made if any continuous signal can cause a transition (see "Enabling Conditions and Continuous Signals" on page 2180). The process is thereafter removed from the ready queue.
- If any signal (or continuous signal) can cause a transition then the process is re-inserted into the ready queue again at a position determined by its priority, else if the new state contains any continuous signal or enabling condition with an expression that might change its value during the time the process is in the state (view, import...), the process is inserted into the check list (see also "Enabling Conditions and Continuous Signals" on page 2180).
Decision and Task operations are implemented in generated code, except for the Trace-functions implemented in the sctutil.c file and for informal and any decisions that uses some support functions in sctutil.c. A Decision is implemented as a C if-statement, while the assignments in a Task are implemented as assignments or function calls in C.
The expressions involved in continuous signals and enabling conditions are implemented in generated code in functions called yCont_StateName and yEnab_StateName. These functions are generated for each state containing continuous signals respectively enabling conditions. The functions are referenced through the components ContSig_Function and EnablCond_Function in the StateIdNode for the state. These components are 0 if no corresponding functions are generated.
The EnablCond_Functions are called from the function xFindInputAction, which is called from SDL_Output and SDL_Nextstate. If the enabling condition expression for the current signal is true then xInput is returned else xSave is returned. This information is then used to determine how to handle the signal in this state.
The ContSig_Functions are called from SDL_Nextstate, if the component ContSig_Function is not 0 and no signal that can cause an immediate transition is found during the input port scan. A ContSig_Function has the following prototype:
void ContSig_Function_Name (
void *, int *, xIdNode *, int *);
where the first parameter is the pointer to the yVDef_ProcessName. The remaining parameters are all out parameters; the second contains the priority of the continuous signal with highest priority (=lowest value) that has an expression with the value true. Otherwise <0 is returned here. The third and fourth is only defined the second parameter >=0; the third is the IdNode for the process/procedure where the actual continuous signal can be found and the fourth is the RestartAddress connected to this continuous signal.
If a continuous signal expression with value true is found, a signal instance representing the continuous signal is created and inserted in the input port, and is thereafter treated as an ordinary signal. The signal type is continuous signal and is represented by an SignalIdNode (referenced by the variable xContSigId).
The check list is a list that contains the processes that wait in a state where enabling conditions or continuous signals need to be repeatedly recalculated.
A process is inserted into the check list if:
- It enters a state containing enabling conditions and/or continuous signals and
- No signal or continuous signal can cause an immediate transition and
- One or several of the expressions in the enabling conditions or continuous signals can change its value while the process is in the state (view, import...)
The component StateProperties in the StateIdNode reflects if any such expression is present in the state.
The check list is represented by the xSysD component:
xPrsNode xCheckList;
The behavior of enabling conditions and continuous signals is in SDL modeled by letting the process repeatedly send signals to itself, thereby to repeatedly entering the current state. In the implementation chosen here, nextstate operations are performed "behind the scene" for all processes in the check list directly after a call to a PAD function is completed, that is directly after a transition is ended and directly after a timer output. This is performed by calling the function xCheckCheckList in the main loop of the program.
A view expression is part of an expression in generated code and implemented by calling the function SDL_View.
void * SDL_View (
xViewListRec *VList,
SDL_PId P,
xbool IsDefP,
xPrsNode ViewingPrs,
char * Reveal_Var
int SortSize);
- VList is a list of all revealed variables in this block.
- P is the PId expression given in the view statement.
- IsDefP is 1 is the view expression contained a PId value, 0 otherwise.
- ViewingPrs is the process instance performing the view operation.
- Reveal_Var is the name of the revealed variable as a string. The Reveal_Var parameter is only used in error messages and is remove under certain conditions.
- SortSize is the size of the data type of the viewed variable.
The SDL_View function performs a test that the view expression is not NULL, refers to a process in the environment, or to a stopped process instance. If no errors are found the address of the revealed variable is returned as result from the SDL_View function. Otherwise the address of a variable containing only zeros is returned.
For an exported variable there are two components in the yVDef_ProcessName struct. One for the current value of the variable and one for the currently exported value of the variable. For each exported variable there will also be a struct that can be linked into a list in the corresponding RemoteVarIdNode. This list is then used to find a suitable exporter of a variable in an import action.
An export action is a simple operation. The current value of the variable is copied to the component representing the exported value. This is performed in generated code.
An import action is more complicated. It involves mainly a call of the function xGetExportAddr:
void * xGetExportAddr (
xRemoteVarIdNode RemoteVarNode,
SDL_PId P,
xbool IsDefP,
xPrsNode Importer )
RemoteVarNode is a reference to the RemoteVarIdNode representing the remote variable (implicit or explicit), P is the PId expression given in the import action and IsDef is 0 or 1 depending on if any PId expression was given in the import action or not, Importer is the importing process instance. The xGetExportAddr will check the legality of the import action and will, if no PId expression is given, calculate which process it should be imported from.
If no errors are found the function will return the address where the exported value can be found. This address is then casted to the correct type (in generated code) and the value is obtained. If no process possible to import from is found, the address of a variable containing only zeros is returned by the xGetExportAddr function.
------------------------------------------------------------------------
Note:
The strategy for import actions is in one sense not equal to the model
for import given in the SDL recommendation. An import action is
in the recommendation modelled as a signal sent from the importing
process to the exporting process asking for the exported value, and
a signal with this value sent back again. The synchronization effects
by this signal communication is lost in the implementation model
we have chosen. Instead our model is much easier and faster and the
primary part of the import action, to obtain the exported value, is the
same.
------------------------------------------------------------------------
A service is represented by a struct type. The xSrvRec struct defined in scttypes.h, is, just like xPrsRec for processes, a struct containing general information about a service, while the parameters and variables of the service are defined in generated code in the same way as for processes.
In scttypes.h the following types concerning procedures can be found:
#ifdef XMONITOR
#define XCALL_ADDR int CallAddress;
#else
#define XCALL_ADDR
#endif
#ifndef ULTRIXCC
#define XPAD_STRUCT_COMP \
void (*RestartPAD) XPP((xPrsNode VarP));
#else
#define XPAD_STRUCT_COMP void (*RestartPAD) ();
#endif
#define SERVICE_VARS \
xSrvNode NextSrv; \
xPrsNode ContainerPrs; \
int RestartAddress; \
xPrdNode ActivePrd; \
XPAD_STRUCT_COMP \
XCALL_ADDR \
xSrvIdNode NameNode; \
int State; \
XSIGTYPE pREPLY_Waited_For; \
xSignalNode pREPLY_Signal; \
XINTRANSCOMP
#ifndef XNOUSEOFSERVICE
typedef struct xSrvStruct *xSrvNode;
#endif
#ifndef XNOUSEOFSERVICE
typedef struct xSrvStruct {
SERVICE_VARS
} xSrvRec;
#endif
In generated code yVDef_ProcedureName structs are defined accord
ing to the following:
typedef struct {
SERVICE_VARS
components for FPAR and DCL
} yVDef_ServiceName;
The components in the xSrvRec are used as follows:
- NextSrv of type xSrvNode. Reference to next service contained in this process.
- ContainerPrs of type xPrsNode. Reference to the process instance containing this service.
- RestartAddress of type int. This component is used to find the appropriate SDL symbol to continue execution from.
- ActivePrd of type xPrdNode. This is a pointer to the xPrdRec that represents the currently executing procedure called from this service instance. The pointer is 0 if no procedure is currently called.
- RestartPAD, which is a pointer to a PAD function. This component refers to the PAD function where to execute the sequence of SDL symbols. RestartPAD is used to handle inheritance between service types.
- CallAddress of type int. This component contains the symbol number of the procedure call performed from this procedure (if any).
- NameNode of type xSrvIdNode. This is a pointer to the IdNode representing the service or service instantiation.
- State of type int. This component contains the int value used to represent the current state of the service instance.
- pREPLY_Waited_For of type xSignalIdNode. When a service is waiting in the implicit state for the pREPLY signal in a RPC call, this components is used to store the IdNode for the expected pREPLY signal.
- pREPLY_Signal of type xSignalNode. When a service receives a pCALL signal, i.e. accepts a RPC, it immediately creates the return signal, the pREPLY signal. This component is used to refer to this pREPLY signal until it is sent.
- InTransition of type xbool. This component is true while the service is executing a transition and it is false while the service is waiting in a state. The monitor system needs this information to be able to print out relevant information.
From the scheduler's point view, it is not of interest if a process contains services or not. It is still the process instance that is scheduled in the ready queue and the PAD function of the process that is to be called to execute a transition. The PAD function for a process containing services performs three different actions:
- Assign default value to variables declared at the process level
- Create one service instance for each service or service instantiation in the process.
- Calls the proper PAD function for a service to execute transitions.
The structure for a PAD function for a process with services are as follows:
YPAD_FUNCTION(yPAD_z00_P1)
{
YPAD_YSVARP
YPAD_YVARP(yVDef_z00_P1)
YPRSNAME_VAR("P1")
LOOP_LABEL_SERVICEDECOMP
CALL_SERVICE
/*-----
* Initialization (no START symbol)
------*/
BEGIN_START_TRANSITION(yPDef_z00_P1)
yAssF_SDL_Integer(yVarP->z002_Global,
SDL_INTEGER_LIT(10), XASS);
START_SERVICES
}
where LOOP_LABEL_SERVICEDECOMP and BEGIN_START_TARNSITION are empty macros, i.e. expanded to no code. The yAss_SDL_Integer statement in an assignment of a default value to a process variable.
The macro CALL_SERVICE is expanded to:
if (yVarP->ActiveSrv != (xSrvNode)0) {
(*yVarP->ActiveSrv->RestartPAD)(VarP);
return; \
}
that is to a call of the PAD function of service reference by ActiveSrv.
The macro START_SERVICE is expanded to a call to the function xStart_Services, which can be found in sctsdl.c.The function creates the service instances, sets up the ActiveSrv pointer for the process to the first service, and then schedules the process for a new transition. This means that the next action performed by the system will be the start transition by the first service instance. When the first service executes a nextstate or stop action in the end of its start transition, the process will be scheduled again to execute the start transition of the second service, and so on until all services in the process has executed its start transitions.
For ordinary transitions, i.e. reception of a signal, it is obvious from the code above that the ActiveSrv pointer is essential. It should refer to the service instance that is to be executed. When a signal is to be received by a process, it is the function xFindInputAction (in sctsdl.c) that determines how to handle the signal and if it is to be received, where is the code for that transition. This function now also determines and sets up the ActiveSrv pointer.
A procedure is represented by a struct type. The xPrdRec struct defined in scttypes.h, is, just like xPrsRec for processes, a struct containing general information about a procedure, while the parameters and variables of the procedure are defined in generated code in the same way as for processes.
In scttypes.h the following types concerning procedures can be found:
#ifndef ULTRIXCC
#define XPRD_STRUCT_COMP \
xbool (*RestartPRD) XPP((xPrsNode VarP));
#else
#define XPRD_STRUCT_COMP xbool (*RestartPRD) ();
#endif
#define PROCEDURE_VARS \
xPrdIdNode NameNode; \
xPrdNode StaticFather; \
xPrdNode DynamicFather; \
int RestartAddress; \
XCALL_ADDR \
XPRD_STRUCT_COMP \
xSignalNode pREPLY_Signal; \
int State;
typedef struct xPrdStruct *xPrdNode;
typedef struct xPrdStruct {
PROCEDURE_VARS
} xPrdRec;
In generated code yVDef_ProcedureName structs are defined according to the following:
typedef struct {
PROCEDURE_VARS
components for FPAR and DCL
} yVDef_ProcedureName;
The components in the xPrdRec are used as follows:
- NameNode of type xPrdIdNode. This is a pointer to the IdNode representing the procedure type.
- StaticFather of type xPrdNode. This is a pointer that represents the scope hierarchy of procedures (and the process at the top), which is used when a procedure instance refers to non-local variables. An example is shown in Figure 558 on page 2190. StaticFather == 0 means that the static father is the process.
- DynamicFather of type xPrdNode. This is a pointer that represents that this procedure is called by the referenced procedure. DynamicFather == 0 means that this procedure was called from the process. This component is also used to link the xPrdRec in the avail list for the procedure type.
- RestartAddress of type int. This component is used to find the appropriate SDL symbol to continue execution from.
- CallAddress of type int. This component contains the symbol number of the procedure call performed from this procedure (if any).
- RestartPRD is a pointer to a procedure function. This component refers to the PRD function where to execute the next sequence of SDL symbols. RestartPRD is used to handle inheritance between procedures.
- pREPLY_Signal of type xSignalNode. When a process receives a pCALL signal, i.e. accepts a RPC, it immediately creates the return signal, the pREPLY signal. This component is used to refer to this pREPLY signal until it is sent.
- State of type int. This is the value representing the current state of the procedure instance.
In Figure 558 on page 2190 an example of the structure of yVDef_Procedurename after four nested procedure calls are presented. Note that procedure Q is declared in the process, procedure R and S in Q and T in S.
Figure 558 : Structure of yVDef_ProcedureName After Four Nested Procedure
Calls.
-----
(fig)
-----
The SDL procedures are partly implemented using C functions and partly using the structure shown above. Each SDL procedure is represented by a C function, which is called to execute actions defined in the procedure. This function corresponds to the PAD function for processes. The formal parameters and the variables are however implemented using a struct defined in generated code. The procedure stack for nested procedure calls is implemented using the components StaticFather and DynamicFather, and does not use the C function stack.
Procedure calls and procedure returns are handled by three functions, one handling allocation of the data areas for procedures:
xPrdNode xGetPrd( xPrdIdNode PrdId )
and two functions called from generated code at a procedure call and a procedure return:
void xAddPrdCall(
xPrdNode R,
xPrsNode VarP,
int StaticFatherLevel,
int RestartAddress )
void xReleasePrd (xPrsNode VarP)
A procedure call in SDL is in C represented by the following steps:
- Calling xGetPrd to obtain a data area for the procedure.
- Assigning procedure parameters to the data area.
- Calling xAddPrdCall to link the procedure into the static and dynamic chains.
- Calling the C function modelling the SDL procedure, i.e. the yProcedureName function.
The parameters to xAddPrdCall are as follows:
- R. A reference to the xPrdNode obtained from the call of xGetPrd.
- VarP. A reference to the yVDef_ProcessName, i.e. the data area for variables and parameters of the process (even if it is a procedure that performed the procedure call).
- StaticFatherLevel. This is the difference in declaration levels between the caller and the called procedure. This information is used to set up the StaticFather component correctly.
- RestartAddress. This is the symbol number of the SDL symbol directly after the procedure call. The symbol number is the switch case label generated for all symbols.
The xGetPrd returns a pointer to an xPrdRec, which can then be used to assign the parameter values directly to the components in the data area representing the formal parameters and variables of the procedure. Note that IN/OUT parameters are represented as addresses in this struct.
A procedure return is in generated code represented by calling the xReleasePrd followed by return 0, whereby the function representing the behavior of the SDL procedure is left.
The function representing the behavior of the SDL procedure is returned in two main situations:
- When an SDL Return is reached (the function returns 0)
- When a Nextstate is reached (the function returns 1).
If 0 is returned then the execution should continue with the next SDL symbol after the procedure call, while if 1 is returned the execution of the process instance should be terminated and the scheduler (main loop) should take control. This could mean that a number of nested SDL procedure calls should be terminated.
To continue to execute at the correct symbol when a procedure should be resumed after a nextstate operation, the following code is introduced in the PAD function for processes containing procedure calls:
while ( yVarP->ActivePrd != (xPrdNode)0 )
if ((*yVarP->ActivePrd->RestartPRD)(VarP))
return;
This means that uncompleted procedures are resumed one after one from the bottom of the procedure stack, until all procedures are completed or until one of them returns 1, i.e. executes a nextstate operation, at which the process is left for the scheduler again.
The ChannelIdNodes for channels, signal routes, and gates are used in the functions xFindReceiver and xIsPath, which are both called from SDL_Output, to find the receiving process when there is no TO clause in the Output statement, respectively to check that there is a path to the receiver in the case of a TO clause in the Output statement. In both cases the paths built up using the ToId components in the IdNodes for processes, channels and signal routes are followed. To show the structure of these paths we use the small SDL system given in Figure 559
Figure 559 : A Small SDL System
-----
(fig)
-----
During the initialization of the system, the symbol table is built up. The part of the symbol table starting with the system will then have the structure outlined in Figure 560 As we can see in this example the declarations in the SDL system are directly reflected by IdNodes.
-----------------------------------------------------------------------
Note:
Each channel and signal route is represented by two IdNodes, one
for each direction. This is also true for an unidirectional channel or
signal route. In this case the signal set will be empty for the unused
direction.
-----------------------------------------------------------------------
Figure 560 : The symbol table tree for the system in Figure 559.
------
.(fig)
------
Each IdNode representing a process, a signal route, or a channel will have a component ToId. A ToId component is an address to an array of references to IdNodes. The size of this array is dependent on the number of items this object is connected to. A process that has three outgoing signal routes will have a ToId array which can represent three pointers plus an ending 0 pointer.
In the example in Figure 559 on page 2193 and Figure 560 on page 2194 there is no branching, so all ToId arrays will be of the size necessary for two pointers. Figure 561 shows how the IdNodes for the processes, signal routes and channels are connected to form paths, using the components ToId. In this case only simple paths are found (one from P1, via SR1, C, SR2, to P2, and one in the reverse direction). The generalization of this structure to handle branches is straightforward and discussed in the previous paragraph.
Figure 561 : The Connection of ToId for the System in
Figure 559 and Figure
560.
-----
(fig)
-----
The probably most important new feature in SDL-92 is the introduction of the object oriented features, such as TYPE, INHERITS, VIRTUAL, and REDEFINED. Here we start by discussing process types.
For each process type the C Code Generator will generate:
- a PrsIdNode
- a PAD function
- a yVDef_ProcessName struct.
In the PrsIdNode there is one component (Super) that will refer to the PrsIdNode for the process type inherited by this process type. As sons to a PrsIdNode, IdNodes for declaration that are common for all instantiation of the process type can be found. Examples of such IdNodes are: nodes for variables, formal parameters, signals, timers, procedures, states, newtypes, and syntypes. Any typedefs or help functions for such units are also treated in the process type.
The PAD function will be independent of the PAD function for a inherited type, each PAD function just implementing the action described in its process type.
A yVDef_ProcessName struct will on the other hand include all variables and formal parameters from the top of the inheritance chain and downwards. Example:
process type P1;
fpar f1 integer;
dcl d1 integer;
...
endprocess;
process type P2 inherits P1;
fpar f2 integer;
dcl d2 integer;
...
endprocess;
This will generate the following principle yVDef_... structs:
typedef struct {
PROCESS_VARS
SDL_Integer f1;
SDL_Integer d1;
} yVDef_P1;
typedef struct {
PROCESS_VARS
SDL_Integer f1;
SDL_Integer d1;
SDL_Integer f2;
SDL_Integer d2;
} yVDef_P2;
A pointer to yVDef_P2 can thus be casted to a pointer to yVDef_P1, if only the common component (in PROCESS_VARS) or the variables in P1 is to be accessed. This possibility is used every time the PAD function for an inherited process type is called.
Each process instantiation will all be implemented as a xPrsIdNode. The Super component in such an object refers to the process type that is instantiated. No PAD function or yVDef_... struct will be generated. As sons to the PrsIdNode for a process instantiation, only such object are inserted that are different in different instantiations. For a process instantiation this is the gates. For other types of information the process instantiation uses the information given for its process type.
A very similar structure when it comes to IdNodes generated for block types and block instantiations are used by the code generator. There will be a BlockIdNode for both a block type and for a block instantiation. As sons to a block type, nodes that are the same in each block instantiation can be found (example: signal, newtype, syntype, block type, process type, procedure). As sons to a block instantiation, nodes that are needs to be represented in each block instantiation can be found (example: block instantiation, process instantiation, channel, signal route, gate, remote definitions).
---------------------------------------------------------------------
Note:
A block or process (according to SDL-88), that is contained in a
block type or a system type, is translated as if it was a type and in
stantiation at the same place.
---------------------------------------------------------------------
A way to look at the structure of IdNodes in a particular system is to use the command Symboltable in the monitor system. This command prints the IdNode structure as an indented list of objects.
This page intentionally left blank
Table of Contents Next Chapter