04-Implementation.tex 128 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499
  1. \chapter{Implementation: The SCCD Runtime}
  2. \label{chapt:Implementation}
  3. This chapter discusses the main artifact of our work: our implementation of a statechart execution runtime with configurable semantics.
  4. We first explain how our implementation differs from the original SCCD project, from which it was forked. We then introduce the basic design of our implementation, which consists, among other things, of a simple action language, the statechart language with semantic variability, and, on top of it, the SCCD language and its event-queueing execution, implemented in what we call ``the Controller''. We then explain the detailed design and implementation of each of those languages, giving a deep insight into our solution. Finally, we go back to discuss and motivate changes made from the original SCCD project, at a more detailed level.
  5. \section{Current State of SCCD}
  6. % \subsection{Original SCCD project}
  7. The original implementation of SCCD \cite{VanMierlo2016} was a statechart \& class diagram compiler + execution runtime library, which supported a (smaller) subset of BSML semantic configurations as well.
  8. At the highest level, an SCCD model is a \emph{class diagram} where the behavior of the classes is defined as a statechart. The class diagram has a single \emph{default class}, which is instantiated when the model is instantiated. This instance may \emph{dynamically create} new instances of other classes (statecharts). The relationships between instances (e.g. multiplicities) are modeled in the class diagram and enforced during execution.
  9. It generated executable code in a number of target languages. The supported target languages were Python, JavaScript and C\# \cite{glenn2014statecharts}.
  10. Semantic configurations were limited to Big-Step Maximality (\textsc{Take One}, \textsc{Take Many}), Internal+Input Event Lifeline (\textsc{Next Small Step}, \textsc{Next Combo Step}, \textsc{Present in Remainder}, \textsc{Queue}) and Priority (\textsc{Source-Child}, \textsc{Source-Parent}).
  11. \begin{figure}
  12. \centering
  13. \includegraphics{original_sccd_features}
  14. \caption{Feature diagram of original SCCD}
  15. Figure taken from \cite{VanMierlo2016}
  16. \label{fig:original_sccd_features}
  17. \end{figure}
  18. % \todo{illustration of original SCCD project, many-to-many compiler, feature tree?}
  19. \subsection{SCCD in this thesis}
  20. The SCCD discussed in this thesis is a fork from the original SCCD. The most important ``functional additions'' to original SCCD are the following:
  21. \begin{description}
  22. \item[More semantic options]
  23. The main goal of this thesis. On top of the semantic options supported in original SCCD, the following options were added: Big-Step Maximality: \textsc{Syntactic}, Combo-Step Maximality: \textsc{Combo Take One}, \textsc{Combo Take Many}, \textsc{Combo Syntactic}, Memory Protocol: \textsc{Big Step}, \textsc{Combo Step}, \textsc{Small Step}, Priority: \textsc{Hierarchical} (\textsc{Arena-Parent}, \textsc{Arena-Child}), \textsc{Explicit Priority}, \textsc{Negation of Triggers}, Order of Small Steps: \textsc{None} and \textsc{Explicit Ordering}. See \ref{tab:sccd_as_bsml} for a full overview.
  24. \item[Action language]
  25. In the original SCCD, action code had to be written in the same language as the target language of compilation, making models non-portable. This was only a temporary solution, the plan was to add an action language eventually. Another reason for integrating an action language is to have precise control over Memory Protocol semantics. Therefore, SCCD now has a built-in (textual) statically type-checked action language.
  26. \end{description}
  27. \begin{table}
  28. \footnotesize
  29. \centering
  30. \begin{minipage}{.4\linewidth}
  31. \centering
  32. \begin{tabular}{ | c | c |}
  33. \hline
  34. \textbf{Big-Step Maximality} & \\
  35. \hline
  36. \textsc{Take One} & \checkmark \\
  37. \textsc{Take Many} & \checkmark \\
  38. \textsc{Syntactic} & \checkmark \\
  39. \hline
  40. \hline
  41. \textbf{Combo-Step Maximality} & \\
  42. \hline
  43. \textsc{Combo Take One} & \checkmark \\
  44. \textsc{Combo Take Many} & \checkmark \\
  45. \textsc{Combo Syntactic} & \checkmark \\
  46. \hline
  47. \hline
  48. \textbf{Input/Internal Event Lifeline} & \\
  49. \hline
  50. \textsc{Present in Whole} & \\
  51. \textsc{Present in Remainder} & \checkmark \\
  52. \textsc{Present in Next Combo-Step} & \checkmark \\
  53. \textsc{Present in Next Small-Step} & \checkmark \\
  54. \textsc{Present in Same} & \\
  55. \hline
  56. \end{tabular}
  57. \end{minipage}%
  58. \begin{minipage}{.6\linewidth}
  59. \centering
  60. \begin{tabular}{ | c | c | }
  61. \hline
  62. \textbf{Enabledness/Assignment Memory Protocol} & \\
  63. \hline
  64. \textsc{GC/RHS Big Step} & \checkmark \\
  65. \textsc{GC/RHS Combo Step} & \checkmark \\
  66. \textsc{GC/RHS Small Step} & \checkmark \\
  67. \hline
  68. \hline
  69. \textbf{Priority} & \\
  70. \hline
  71. \textsc{Hierarchical} & \checkmark \\
  72. \textsc{Explicit Priority} & \checkmark \\
  73. \textsc{Negation of Triggers} & \checkmark \\
  74. \hline
  75. \hline
  76. \textbf{Order Of Small Steps} & \\
  77. \hline
  78. \textsc{None} & \checkmark \\
  79. \textsc{Explicit Ordering} & \checkmark \\
  80. \textsc{Dataflow} & \\
  81. \hline
  82. \hline
  83. \textbf{Concurrency} & \\
  84. \hline
  85. \textsc{Single} & \checkmark \\
  86. \textsc{Many} & \\
  87. \hline
  88. \end{tabular}
  89. \end{minipage}
  90. \caption{Semantic options supported in SCCD}
  91. \label{tab:sccd_as_bsml}
  92. \end{table}
  93. Under the hood, SCCD has changed so much, that little resemblance with the original remains. Most importantly, SCCD is no longer a compiler (code generator), but an execution runtime, which loads models in an XML format and executes them. A detailed discussion and motivation for the changes made, follows at the end of this chapter, in Section \ref{sec:changes}.
  94. \section{Overview of Implementation}
  95. The SCCD runtime consists of 3 languages: the action language, the statechart language, and the SCCD language. The action language is part of the statechart language, and the statechart language is part of the SCCD language. Apart from these 3 languages, a few libraries are included. The dependency graph between SCCD's packages reflects this (Figure \ref{fig:package_dependencies}).
  96. \begin{figure}
  97. \centering
  98. \includegraphics[]{package_dependencies_nolabel}
  99. \caption{Dependencies between packages (directories) of SCCD.}
  100. The meaning of an arrow is: ``depends on''.
  101. \label{fig:package_dependencies}
  102. \end{figure}
  103. An overview of the directory structure (packages) at the highest level:
  104. \begin{description}
  105. \item[\texttt{action\_lang}] Implementation of the action language parser, syntax and semantics.
  106. \item[\texttt{statechart}] Implementation of the statechart parser, syntax and variable semantics. Naturally depends on \texttt{action\_lang} since the action language is part of the statechart language as well.
  107. \item[\texttt{cd}] Placeholder implementation of the ``class diagram'' (SCCD language) parser and syntax. A model in SCCD is always a class diagram, with the behavior of each class defined as a statechart. Each class diagram also has a \emph{default class} defined, which is initialized at startup, just like e.g. the ``main class'' in a Java program. In this thesis, all SCCD models consist of only a single default class, whose behavior is defined by a statechart.
  108. \item[\texttt{controller}] Implementation of the Controller, the primitive for executing SCCD models.
  109. \item[\texttt{realtime}] An optional library, wrapping around the Controller, for soft real-time (wall-clock sync'ed) execution of models. Most ``real'' applications would want to use this. Includes support for event loop integration.
  110. \item[\texttt{test}] Parser and executor of SCCD tests. An SCCD test consists of an SCCD model, a set of timed input events, and a set of timed output events.
  111. \item[\texttt{util}] Utility library, used by most of the project. Contains classes like \texttt{Bitmap}, \texttt{Duration}, etc.
  112. \end{description}
  113. Every one of the 3 languages has a parser, a set of language constructs (the abstract syntax), and an implementation of its execution. These elements are implemented in the sub-packages \texttt{parser}, \texttt{static} and \texttt{dynamic} of each language. The parser and dynamic packages always depend on static: The parser package \emph{produces} instances declared in static; the dynamic package \emph{reads/interprets} instances from static (it does not modify them) in order to carry out execution.
  114. Also note how the the sub-packages of a language on the right depend on their respective sub-packages of a language on the left: E.g. the statechart language's parser-package depends on the action language's parser-package, as the statechart language may include action language fragments, and therefore in order to parse statechart models, it also has to parse action language code, but it does not depend on the action language's dynamic-package, as in order to parse statechart models, it does not have to execute action language code.
  115. Figure \ref{fig:package_dependencies} shows the dependencies between the packages (directories) of SCCD. Note how the \texttt{action\_lang}, \texttt{statechart} and \texttt{cd} packages (directories) have the same sub-package structure of \texttt{static}, \texttt{dynamic} and \texttt{parser}. The sub-packages on the right depend on their equivalent on the left. The recurring sub-packages have the following meaning:
  116. \begin{description}
  117. \item[\texttt{static}] Roughly, the \emph{syntactical constructs} of the language. A loaded model is a connected graph of instances of the classes (types) defined in this package. They are constructed by the parser, and some semantic processing steps / checks (e.g. static analysis) are possibly done. Those processing steps are also implemented in this package. Once the model is loaded, the model itself no longer changes.
  118. \item[\texttt{dynamic}] The types defined in this package are responsible for creating and executing instances of a loaded model. This package only \emph{reads} loaded models (i.e. instances from \texttt{static}), it does not modify them.
  119. \item[\texttt{parser}] The parser logic: a composition of rule-based algorithms for parsing textual (in the case of the action language) or XML (in the case of the statechart and class diagram languages) data into loaded models, producing instances of \texttt{static}.
  120. \end{description}
  121. The dependency graph among packages in Figure \ref{fig:package_dependencies} hints that the Controller is the ``\texttt{dynamic}'' part of the ``\texttt{cd}'' package. This is a correct observation, as the Controller \emph{reads} class diagram models and creates and executes instances of it. Initially, the Controller was given its own package to make it prominent. At some point, it may be put in the \texttt{cd.dynamic} package.
  122. In the following sections, we will introduce the detailed design of the action language (Sec \ref{sec:action_lang_impl}), the statechart language (Sec \ref{sec:statechart_impl}), and finally, the Controller (or better, the class diagram language top-level execution semantics) (Sec \ref{sec:controller_impl}).
  123. \section{Action Language Implementation}\label{sec:action_lang_impl}
  124. The action language is an important new feature in SCCD. In statecharts, action language code is mostly used in a transition's guard condition (an expression) and a transition's action (a block of statements). The integration of an action language makes the statechart language much more powerful, while remaining portable. By using a customly developed language, we have complete control over its features.
  125. The action language is a simple \textbf{procedural}, \textbf{statically typed} language with a syntax somewhat resembling Python (for expressions) and JavaScript (for function declarations and blocks). It is a language on its own, and can be used independently of other parts of the SCCD project. (An interactive prompt app is included in the project to demonstrate this.) Currently, the language is interpreted, but it would be trivial to add a code generator, since much of the work a compiler does has already been implemented in the static analysis step, such as assigning a memory layout to declared variables.
  126. In this section, the action language itself is the focus, but we may mention statecharts now and then, as a motivation for why e.g. some design decision was made. Figure \ref{fig:parser_analysis_execution} shows how the action language is used. First, the parser constructs an AST of syntactical constructs. Next, a static analysis step ``initializes'' the AST tree (note the prime symbol). Then, using the initialized AST tree, execution happens as a function transforming memory.
  127. We will now briefly discuss the parser and syntactical constructs, followed by static analysis and execution.
  128. \begin{figure}
  129. \centering
  130. \includegraphics[width=1.0\textwidth]{parser_analysis_execution}
  131. \caption{Usage of the action language}
  132. \label{fig:parser_analysis_execution}
  133. \end{figure}
  134. \subsection{Parser and syntactical constructs}\label{sec:action_lang_constructs}
  135. The Python library Lark\cite{Erez2020Lark} is used to parse action language fragments. Lark constructs a parser from a grammar file. Lark can switch between 2 parsing algorithms: LALR(1) and Early. The former offers better performance, while the latter is easier to write grammars for. We use LALR(1) since it was found to work perfectly well.
  136. The grammar file used by the action language is listed in Appendix \ref{chapt:grammar}. Apart from the grammar file, the parsing step also uses a ``transformer'' class, written in Python, which translates encountered textual constructs to our own syntactical constructs. The transformer class also \emph{desugars} some syntax, e.g. \texttt{i += 1} becomes \texttt{i = i + 1}.
  137. Figures \ref{fig:cd_expression} and \ref{fig:cd_statement} show a class diagram of the syntactical constructs that make up the language. Some constructs are composed of other constructs, and can form a tree structures, called ASTs (abstract syntax trees). The result of parsing a piece of action code is always an AST.
  138. \textbf{Example:} Figure \ref{fig:cd_y=2x} shows the AST for the statement \texttt{y = 2 * x}. At the root of the AST is the assignment statement. The left-hand side of the assignment is the identifier \texttt{y}, which is treated as an LValue. At the right-hand side is the expression \texttt{2 * x}, where $x$ is treated as an RValue.
  139. \subsubsection{Expressions, statements and LValues}
  140. Every construct either implements the \texttt{Expression} or \texttt{Statement} interface. \texttt{Expression} is not a subtype of \texttt{Statement}, or vice versa. Expressions can be \emph{evaluated}, statements can be \emph{executed}, and both can have side-effects, such a new value to a variable being written. Expression evaluation always yields a result, statement execution does not.
  141. There is also the abstract class \texttt{LValue}, which is a bit special. \texttt{LValue} inherits \texttt{Expression} because all \texttt{LValue} instances can also be treated as expressions (i.e. RValues), but only if they occur in an expression context. Vice versa, an \texttt{LValue} instance occuring in an LValue context is never treated as an expression. The only LValue-context currently existing in the action language is the left-hand side of an \texttt{Assignment} statement. When an \texttt{LValue} type is the root of an AST, it is always treated as an \texttt{Expression}.
  142. All constructs have at least 2 methods (See Table \ref{tab:methods}):
  143. \begin{enumerate}
  144. \item A \emph{static analysis} method, implementing the static analysis step, which must be executed on every AST before it can be executed.
  145. \item An \emph{execution} (or evaluation) method, implementing the \emph{actionable} part of the language.
  146. \end{enumerate}
  147. We will first discuss static analysis, execution follows later.
  148. \begin{table}[h!]
  149. \centering
  150. \begin{footnotesize}
  151. \begin{tabular}{ | r | c | c | }
  152. \hline
  153. & \textbf{Static Analysis} & \textbf{Execution} \\
  154. \hline
  155. Expression & init\_expr(:Scope): SCCDType & eval(:MemoryInterface): Any \\
  156. LValue & init\_lvalue(:Scope, rhs\_type: SCDDType) & eval\_lvalue(): int \\
  157. Statement & init\_stmt(:Scope): ReturnBehavior & exec(:MemoryInterface): ReturnValue \\
  158. \hline
  159. \end{tabular}
  160. \end{footnotesize}
  161. \caption[Methods for static analysis and execution]{Methods for static analysis and execution. The meaning of these methods is discussed in the subsections on Static Analysis (\ref{sec:action_static_analysis}) and Execution (\ref{sec:action_execution}).}
  162. \label{tab:methods}
  163. \end{table}
  164. \begin{figure}
  165. \centering
  166. \includegraphics[width=1.0\textwidth]{CD_Expression2}
  167. \caption{Syntactical constructs, Expression type.}
  168. \label{fig:cd_expression}
  169. \end{figure}
  170. \begin{figure}
  171. \centering
  172. \includegraphics{CD_Statement2}
  173. \caption{Syntactical constructs, Statement type.}
  174. \label{fig:cd_statement}
  175. \end{figure}
  176. \begin{figure}
  177. \centering
  178. \includegraphics{CD_SCCDType}
  179. \caption{Type constructs}
  180. \label{fig:cd_sccdtype}
  181. \end{figure}
  182. \begin{figure}
  183. \centering
  184. \includegraphics{CD_y=2x}
  185. \caption{AST for statement: \texttt{y = 2 * x}}
  186. \label{fig:cd_y=2x}
  187. \end{figure}
  188. \subsection{Static analysis}\label{sec:action_static_analysis}
  189. Static analysis is an important feature of the action language. It is responsible for 2 related things:
  190. \begin{enumerate}
  191. \item Static typing with type inference: Determining the types of all expressions, while checking whether types are compatible in all expressions and statements.
  192. \item Assigning a memory layout to declared variables. Currently, only stack memory is supported, there is no heap. (The execution runtime allocates stack frames on Python's heap, though.)
  193. \end{enumerate}
  194. Static analysis must be done once, after the structure of the AST has been created, and before the AST can be ``executed'' (interpreted). Static analysis may \emph{fail} (yield an error) if the AST is found to be semantically invalid, e.g. when a type error is found. Static analysis is an (idempotent) operation on the AST, that only adds information to the tree, such as types, memory offsets, and scope objects (see later).
  195. Every construct in the action language has a method implementing the analysis step. To run static analysis on an AST, one calls the static analysis method on the root node of the AST. Nodes that have children implement static analysis by also invoking it on their children. As such, the analysis step is performed on the entire AST.
  196. Depending on whether a node is an \texttt{Expression}, \texttt{LValue} or \texttt{Statement}, the method for performing static analysis is as follows:
  197. \begin{description}
  198. \item[Expression:] \texttt{init\_expr(scope: Scope): SCCDType}
  199. Initializes the expression as part of the given scope (more on scopes later), determines and returns the type of the expression.
  200. Typically, expressions use the scope to lookup types and memory offsets of encountered variable names.
  201. \item[LValue:] \texttt{init\_lvalue(scope: Scope, rhs\_type: SCCDType)}
  202. Initializes the LValue as part of the given scope, with the given type \texttt{rhs\_type}.
  203. An LValue may introduce a new variable to the scope, or if the variable already exists, it merely asserts that the types match (assignment-wise), and looks up the memory offset of the existing variable.
  204. \item[Statement:] \texttt{init\_stmt(scope: Scope): ReturnBehavior}
  205. Initializes the statement as part of the given scope, determines and returns the ``return behavior'' (see later section) of the statement.
  206. \end{description}
  207. \subsubsection{Determining expression types}
  208. The static analyzer determines the type of every expression. Different strategies are used for different expression types:
  209. \begin{description}
  210. \item[Literals] (e.g. \texttt{IntLiteral}) Trivial, the type is always the same.
  211. For \texttt{ArrayLiteral}, the type is Array-of-element-type, and mixed element types are not allowed.
  212. \item[BinaryExpression] (e.g. a sum) The type depends on the operator, the left-hand side and right-hand side expressions, e.g. the sum of two integers is an integer.
  213. \item[UnaryExpression] (e.g. unary minus) The type depends on the operator and the expression.
  214. \item[Identifier] If the identifier occurs in a RValue context, its type is discovered by looking up the variable name in the scope object received during static analysis.% If the identifier occurs in an LValue context, either a new variable is created (if the name does not exist in the scope) or a new value is assigned to an existing variable. If a new variable is created, the type of the rhs-expression is used
  215. \item[FunctionDeclaration] The type is ``function type'' (Figure \ref{fig:cd_sccdtype}). The formal parameters are type-annotated in the syntax. The return type is inferred (see later).
  216. \item[FunctionCall] First, it is asserted that the expression being called is of function type. Then the type is the return type of the function being called.
  217. \end{description}
  218. \subsubsection{Variable declaration type inference}
  219. A useful feature of the action language is its type inference for declared variables (this section), and return types of functions (next section). Type inference results in less verbose code, saving development and maintenance time.
  220. In many statically typed languages such as C or Java, when declaring a variable, a type must be given. This is not the case for the action language, because of the following principles:
  221. \begin{enumerate}
  222. \item The only way to declare a new variable is by \emph{assigning} a value to a name that does not yet exist in the current scope (or parent scopes).
  223. As a consequence, variables are always initialized with a value, which can prevent certain errors.
  224. \item The type of a variable on the left-hand side of an assignment is inferred from right-hand side of the assignment. The right-hand side is just an expression, and the type of expressions is statically known.
  225. \end{enumerate}
  226. A type annotation is also not optional (like in TypeScript or Haskell): it is simply not part of the language. The only place where type annotations occur, is in the formal parameters of a function declaration. Unlike e.g. Haskell, the action language is not powerful enough to infer the possible types of a function's formal parameters.
  227. \subsubsection{Function return type inference}
  228. As mentioned before, the return type of a declared function is inferred:
  229. \begin{python}
  230. inc = func(i: int) {
  231. return i + 1;
  232. };
  233. \end{python}
  234. In the above code fragment, the value assigned to \texttt{inc} will be of type \texttt{func(int) -> int}, meaning, a function taking an integer as parameter, and returning an integer.
  235. This feature is more complex than one might think, as a piece of code may contain multiple branches. Consider the following fragment, which the static analyzer will reject:
  236. \begin{python}
  237. inc = func(i: int) {
  238. if (i < 10)
  239. return i + 1;
  240. else
  241. return "too large" # error: not all branches return the same type
  242. };
  243. \end{python}
  244. Different branches return different types, making the return type of the function vary depending on the input. This is not allowed in the action language. Detection of this is implemented by having static analysis come up with a static description of the \emph{return behavior} of every statement (just like static analysis determines the type of every expression). The return behavior is recorded in an object of type \texttt{ReturnBehavior} (Figure \ref{fig:cd_static_analysis}), returned from the \texttt{Statement.init\_stmt} method, which was already mentioned.
  245. The return behavior can be one of:
  246. \begin{description}
  247. \item[NEVER] The statement never returns, i.e. there are no return-statements in any of the branches.
  248. \item[SOME\_BRANCHES] The statement contains a conditional branch. One or more branches return a value of the same known type, other branches do not have return-statements.
  249. \item[ALWAYS] All of the statements branches contain a return statement, and they all return the same known type.
  250. \end{description}
  251. For SOME\_BRANCHES and ALWAYS, the ``return type'' (e.g. ``int'') is included in the \texttt{ReturnBehavior} object.
  252. For simple statements, such as \texttt{Assignment} or \texttt{ExpressionStatement} (return behavior: NEVER) and \texttt{ReturnStatement} (return behavior: ALWAYS), the return behavior is always the same. For complex types of statements, the return behavior depends on their sub-statements:
  253. \begin{description}
  254. \item[\texttt{Block}] A sequence of statements. The return behavior of a block is calculated with an algorithm that walks over the sequence statements: Initially, the return behavior is NEVER. For each next statement, the ``so-far''-calculated return behavior is \emph{sequenced} with the return behavior of that statement. Sequencing means: the return behavior can only go from NEVER to SOME\_BRANCHES or ALWAYS, and only from SOME\_BRANCHES to ALWAYS. While this happens, as soon as a return type has been established, later statements must return the same type, if they have non-NEVER return behavior, or we throw an error.
  255. This algorithm uses the static method \texttt{ReturnBehavior.sequence} (Figure \ref{fig:cd_static_analysis}), which takes to ``so-far''-calculated behavior, and the behavior of the ``next''-statement to produce a new ``so-far''-return behavior.
  256. \item[\texttt{IfStatement}] A conditionally executed statement, with an optional else-branch. First of all, if there is no else-branch, the return behavior of the else-branch is considered NEVER. Next, the branches are \emph{combined} according to an algorithm: If both branches have the exact same return behavior, that will be the return behavior of the \texttt{IfStatement} as well. In all other cases, the return behavior will be SOME\_BRANCHES, as at least one of the branches does not return ALWAYS and at least one does not return NEVER. We also check if the returned types match. If not, we throw an error.
  257. This algorithm is implemented in the static method \texttt{ReturnBehavior.combine\_branches} (Figure \ref{fig:cd_static_analysis}).
  258. \end{description}
  259. It is clear that for a function body, only NEVER and ALWAYS are allowed, because whether a function returns something, and the type of what is returned, must always be the same.
  260. The SOME\_BRANCHES option thus is illegal for function bodies, but it is allowed in other parts of the AST. For instance, look at the code fragment and its AST in Figure \ref{fig:cd_branches}. The \texttt{IfStatement} in the AST has return behavior SOME\_BRANCHES, meaning it may or may not return (an integer). However, it occurs in a \texttt{Block} (which is a sequence of statements), where it is followed by a \texttt{ReturnStatement}, which has return behavior ALWAYS, meaning it always returns (an integer). As a result, the \texttt{Block} itself also has return behavior ALWAYS, and as such, is a valid function body.
  261. \begin{figure}
  262. \centering
  263. Code fragment:
  264. \begin{python}
  265. inc = func(i: int) {
  266. if (i < 10)
  267. return i + 1;
  268. return 0;
  269. };
  270. \end{python}
  271. AST:
  272. \includegraphics{CD_Branches}
  273. \caption{Code fragment and its AST, with return behavior annotated for all statements}
  274. \label{fig:cd_branches}
  275. \end{figure}
  276. \subsubsection{Scope object}
  277. As we have seen before, the static analysis method of every construct expects a reference to a \texttt{Scope} object as parameter. A scope object primarily serves to lookup and declare variables, containing their types, const-ness and names. It is also a static description of the memory layout of a stack frame during execution, containing the memory offsets of variables in the frame. See Figure \ref{fig:cd_static_analysis} for a UML diagram of the Scope class.
  278. During static analysis, most types of constructs (expressions, statements) simply pass the \texttt{Scope} object on to their children. This means the scope did not change.
  279. \begin{figure}
  280. \centering
  281. \includegraphics{CD_static_analysis}
  282. \caption{Classes involved in static analysis}
  283. \label{fig:cd_static_analysis}
  284. \end{figure}
  285. \textbf{Example:} Figure \ref{fig:seq_y=2x} shows static analysis performend on the AST of Figure \ref{fig:cd_y=2x}. The identifier ``\texttt{x}'' is analyzed as an RValue (\texttt{init\_expr}), and uses the \texttt{Scope} object to lookup the memory offset of its variable, and finds that it is $0$. The identifier ``\texttt{y}'' is analyzed as an LValue (\texttt{init\_lvalue}) and uses the \texttt{Scope} object to attempt to ``put'' a variable \texttt{y} of type \texttt{SCCDInt}. The operation succeeds, and \texttt{y} is given memory offset 1.
  286. \begin{figure}
  287. \centering
  288. \includegraphics[width=1.0\textwidth]{SEQ_y=2x_2}
  289. \caption{Sequence diagram for static analysis of statement \texttt{y = 2 * x}}
  290. \label{fig:seq_y=2x}
  291. \end{figure}
  292. In some cases, the scope does change: A function declaration introduces a new scope for the function's body. This means that variables declared in functions are not visible outside of that function declaration. Function bodies can access (read/write) variables of surrounding scopes, however. (A function declaration is just an expression, so function declarations are allowed almost everywhere, and can be nested arbitrarily.) In order to access surrounding scopes, when a new scope is introduced, that scope always has a \emph{parent}. The only scope without a parent is the \emph{global scope}, which is typically created by the invoker of the static analysis step on the root node of the AST.
  293. Action language constructs can create new scopes during static analysis. Currently, the only construct doing this, is the \texttt{FunctionDeclaration} expression. Created scope objects are stored in the AST, because they contain important information for execuction (such as the size by which to grow the stack upon calling a declared function).
  294. \textbf{Example:} Figure \ref{fig:cd_funcdecl_scope} shows a fragment of code and its AST after static analysis. Upon static analysis, the \texttt{FunctionDeclaration} object creates a new, nested scope for its function body, setting the parent to the scope object it received from the \texttt{Block} object at the top. As a result, the variable \texttt{x} can be successfully looked up from within the function body, while the variable \texttt{y} remains private to the function body. The nested scope is stored in the \texttt{FunctionDeclaration} object itself, because it will be required in order to evaluate the declaration, as seen in the later section on Execution.
  295. \begin{figure}
  296. \centering
  297. Code fragment:
  298. \begin{python}
  299. x = 1;
  300. func() {
  301. y = 2;
  302. x = y;
  303. };
  304. \end{python}
  305. AST:
  306. \includegraphics[width=1.0\textwidth]{cd_funcdecl_scope}
  307. \caption{Code fragment with function declaration and its AST after static analysis, showing the hierarchy of \texttt{Scope} objects on the right}
  308. \label{fig:cd_funcdecl_scope}
  309. \end{figure}
  310. There is one more use case for nested scopes, but not in the action language itself: In the statechart language, we also use many nested scopes, as will be discussed in Section \ref{sec:statechart_scopes}. For instance, every transition has its own scope, containing the names of the transition's event parameters as variables, so they can be accessed from the guard condition and transition's action code, but not from elsewhere.
  311. \subsection{Execution}\label{sec:action_execution}
  312. After static analysis has been performed on an AST, and has succeeded, one can execute the AST. The execution of an AST does not modify the AST, it only modifies \emph{memory}, as was shown in Figure \ref{fig:parser_analysis_execution}.
  313. Similar to the static analysis step, to execute an AST, one invokes the execution method of the root node, as it will invoke execution on its children, etc. Depending on the type of the node, the execution method is as follows:
  314. \begin{description}
  315. \item[Expression] \texttt{eval(memory: MemoryInterface): Any}
  316. Evaluates the expression, reading/writing from/to the memory object given, and yielding a result. (An expression may write to memory, because a function call is an expression.)
  317. \item[LValue] \texttt{eval\_lvalue(): int}
  318. Returns the offset of the LValue (variable) relative to the start of the current stack frame. This offset has already been computed in the static analysis step, and has been stored in the LValue object. It can be a positive or negative value, depending on whether the variable exists in the current stack frame, or one of its ancestors.
  319. \item[Statement] \texttt{exec(memory: MemoryInterface): Return}
  320. Executes the statement, reading/writing from/to the memory object given, and yields a \texttt{Return} object, indicating whether the statement execution caused a return-statement to be executed, and if so, what the returned value was.
  321. \end{description}
  322. The \texttt{MemoryInterface}-implementing object received by these methods as a parameter, is conceptually the \emph{stack memory} of the instance being executed. It defines operations for \emph{loading and storing} data from/to memory, as well as \emph{pushing/popping stack frames} (UML diagram in Figure \ref{fig:cd_evaluation_execution}).
  323. \begin{figure}
  324. \centering
  325. \includegraphics{CD_evaluation_execution}
  326. \caption{Classes involved in execution.}
  327. \label{fig:cd_evaluation_execution}
  328. \end{figure}
  329. For most constructs, the implementation of the execution method is trivial. The not-so-trivial cases are function declarations and function calls. A function call should push a stack frame, and this is done by calling \texttt{MemoryInterface.push\_frame}. This method expects a \texttt{Scope} object. The scope object's primary function here is to describe the size by which the stack should grow. For every function declaration, a scope object has been statically computed during static analysis, and stored in the \texttt{FunctionDeclaration}-expression. Every time the \texttt{FunctionDeclaration} is evaluated, a function-value is returned with the scope object embedded in it. A \texttt{FunctionCall} therefore has access to this \texttt{Scope} object in order to grow the stack.
  330. \subsubsection{MemoryInterface implementation}\label{sec:memory_interface}
  331. The default implementation of \texttt{MemoryInterface}, called \texttt{Memory}, supports the following interesting features:
  332. \begin{description}
  333. \item[Recursion] makes it easier to code certain algorithms, but in the general case (ignoring tail call optimization), has an unpredictable time and memory footprint.
  334. \item[Closures] are a feature in high-level, garbage-collected languages such as Python, Java and JavaScript. They allow functions to declare other functions, and return those functions as values. Those returned functions are allowed to refer to variables of the function in which they were declared, even if that function's stack frame has been popped. Closures only work in a garbage-collecting environment as stack frames must be allocated on the heap, and not be freed until functions that still have access to them go out of scope.
  335. \end{description}
  336. To support these features, the current implementation allocates stack frames on the heap, and maintains 2 singly-linked lists between stack frames:
  337. \begin{itemize}
  338. \item List of parents: Every stack frame has a pointer to its parent, i.e. the stack frame of the function that invoked (called) the current function, and hence created the current stack frame. When the current stack frame is popped, its parent becomes the current stack frame (again). This alone suffices to supports recursion.
  339. \item List of contexts: Every stack frame has a pointer to its context, i.e. the stack frame that was at the top of the stack when the function that created the stack frame was declared. If memory access to a negative offset relative to the current stack frame is requested, the list of contexts is used. This is to support closures.
  340. \end{itemize}
  341. Neither recursion nor closures are necessary or perhaps even desired for usage in statecharts. These are high-level features not encountered in e.g. an embedded environment. An upside is that with these features, it is impossible to write code that contains certain types of memory errors, such as accessing an invalid memory address. Support for these features can be dropped without changing the syntax of the language, but then the static analyzer \emph{should} detect and reject code making use of these features.
  342. If support for recursion and closures is dropped, the maximum size of the call stack can be easily computed statically, allowing for a much simpler implementation of \texttt{MemoryInterface}, that could work with a pre-allocated, fixed amount of memory, suitable for use in an embedded environment.
  343. \section{Statechart Language Implementation}\label{sec:statechart_impl}
  344. \begin{figure}
  345. \centering
  346. \includegraphics[]{xml_init_execute}
  347. \caption{Steps of loading and executing models in the statechart language}
  348. \label{fig:xml_init_execute}
  349. \end{figure}
  350. The statechart language implementation is central to this thesis. Part of the language is a parser/loader function, which parses models in an XML format to construct an AST, and performs an initialization step on this tree, calculating certain static properties of its elements required in our execution implementation. A loaded statechart can then be instantiated and executed.
  351. The execution part of the statechart language includes no queueing of input or output events. It consists only of the instance initialization function (entering the default configuration, must be called once) and the big-step function (responding to a set of input events, may be called repeatedly). Both these functions are only executed when explicitly called. The statechart language itself is therefore \emph{not autonomous}.
  352. Autonomous behavior is implemented through an event loop in the Controller, which is discussed in Section \ref{sec:controller_impl}. Although the statechart language does not depend on the Controller, statechart instances assume some implementation of an event loop, which they use for scheduling and canceling of (future) events, for timed transitions, as we will see.
  353. \subsection{Parser and Constructs}\label{sec:statechart_xml}
  354. Although statecharts are a visual, topological language, our runtime parses statechart models in an XML format. The XML format was originally based on the SCXML standard \cite{W3C2015SCXML}, but ``liberties'' were taken to deviate as desired: no intention of syntactical compatibility was ever on the agenda.
  355. In order to parse XML, we use the well-known Python library lxml \cite{lxml}, which uses the C-library libxml2 \cite{libxml2} under the hood.
  356. As the statechart language can contain action language expressions and statements in various places, the statechart parsing logic will also regularly invoke the action language parser (and static analyzer). An error in action language code ``bubbles up'' as an error in the statechart model.
  357. \subsubsection{Parsing logic and schema}
  358. Apart from parsing our input files, we also want to validate them against a schema. However, no schema was explicitly written in an XML schema language such as XML Schema \cite{W3C2012XSD}, because of the burden of keeping the schema in sync with the parser logic, which, in a system-under-development, is easily forgotten, and can feel like a useless endeavor, since parser logic and schema contain much of the same information. Instead, the parser code was converted into 2 parts:
  359. \begin{enumerate}
  360. \item A tree of nested declarations of expected XML elements, their order and multiplicities, and callbacks for handling those elements. Figure \ref{fig:cd_parse_rules} shows a class diagram of the structure of the parser rules.
  361. \item A generic parser function, built on top of lxml, using the tree of nested declarations and callbacks to carry out the parsing.
  362. \end{enumerate}
  363. \begin{figure}
  364. \centering
  365. \includegraphics[width=10cm]{cd_parse_rules}
  366. \caption{Class diagram (conceptual, not actually implemented as these classes) for structure of parser rules}
  367. \label{fig:cd_parse_rules}
  368. \end{figure}
  369. This way, the parsing logic \emph{is} the schema, with all the reusable functionality of checking multiplicities un-duplicated in a generic parser function. This function also shows very comprehensible error messages, printing out a fragment of the input file, with the error highlighted. The function is potentially reusable in other projects, and is also used in other parts of the SCCD project, such as the parsing of test files.
  370. \subsubsection{Statechart XML format}
  371. We'll introduce the XML format for statechart models. The root XML node is always \texttt{<statechart>}. It has the following children, in order:
  372. \begin{description}
  373. \item[\texttt{<semantics>}] (0..1) The semantic options chosen for the model. For every semantic option that is not specified, SCCD's default value for that option is used.
  374. \item[\texttt{<datamodel>}] (0..1) The initialization code (in action language) of the statechart's \emph{data model}. The data model is the set of variables (and their types) that are readable and writable from everywhere in the statechart.
  375. \item[\texttt{<inport>}] (*) An input port. A statechart model can only receive input in the form of \emph{input events}. An input port defines a set of input events.
  376. \item[\texttt{<outport>}] (*) An output port. A statechart model can only produce output in the form of \emph{output events}. An output port defines a set of output events.
  377. \item[\texttt{<root>}] (1) The root state of the \emph{state tree} of the statechart model.
  378. \end{description}
  379. \subsubsection{Syntactical constructs}
  380. A \texttt{<statechart>} XML node at the highest level is parsed into a \texttt{Statechart} object in the runtime. Figure \ref{fig:cd_statechart} shows a UML diagram of this class, and the other classes it is composed of. It is trivial how the XML format maps to these constructs. The \texttt{Statechart} object represents a loaded model and can be executed by the execution runtime. The \texttt{Statechart} object consists of a \texttt{StateTree} object, which owns the root of the state tree structure, which is a \texttt{State}. The \texttt{State} class and the other constructs making up the tree structure are shown in Figure \ref{fig:cd_state}.
  381. In the runtime, the \texttt{StateTree} object is created \emph{after} the state tree structure has been created (by the parser). Besides containing the root state, the \texttt{StateTree} object contains a lot of additional fields of information derived from the state tree structure. This information is used for efficiently executing the statechart model.
  382. \begin{figure}
  383. \centering
  384. \includegraphics[width=1.0\textwidth]{cd_statechart}
  385. \caption{Syntactical constructs of the statechart language, part 1}
  386. Dark-gray: Action language constructs, see Figure \ref{fig:cd_expression} and Figure \ref{fig:cd_statement}.
  387. Light-gray: Statechart ``tree'' constructs, see Figure \ref{fig:cd_state}.
  388. \label{fig:cd_statechart}
  389. \end{figure}
  390. We'll now introduce the statechart language constructs that make up the state tree structure (Figure \ref{fig:cd_state}). It is clear that the statechart language extends on the action language: For the action language constructs, we refer to Section \ref{sec:action_lang_constructs}.
  391. \begin{description}
  392. \item[\texttt{State}] The state class represents a basic state (if it has no children) or Or-state (if it has 1 or more children). %If a \texttt{State} is part of the current configuration, exactly one of its children is part of the current configuration. If a \texttt{State} has children, it also has a \emph{default state}.
  393. It is also the base class for other types of states. It can have a parent state and any number of children states, creating the state tree structure. The root of a state tree is always of this type (an Or-state).
  394. \item[\texttt{ParallelState}] represents an And state.%: If a \texttt{ParellelState} is part of the current configuration, all of its children are also part of the current configuration.
  395. \item[\texttt{HistoryState}] represents a history (pseudo-)state. Since this class is abstract, its subtypes \texttt{ShallowHistoryState} and \texttt{DeepHistoryState} are to be used instead.
  396. \item[\texttt{Transition}] represents a transition. A transition has a source and a destination state. Except for the root state, \emph{any} state can be the source and/or destination of a transition. The ``source'' is a bi-directional association: every transition is also listed in its source state's list of outgoing transitions.
  397. A transition can have a \emph{guard condition} (\texttt{guard}), which is just an \texttt{Expression} of our action language.
  398. A transition can have an \emph{event trigger} (\texttt{trigger}), which is a set of events that have to be present in order for the transition to be enabled.
  399. A transition can have \emph{actions}, such as raising events or executing action code.
  400. In the diagram, a transition is also shown to have a \texttt{Scope} object (see action language). This is the variable scope used for evaluating the guard condition, and executing actions. The scope contains the transition trigger's event parameters as variables, so they can be read.
  401. \item[\texttt{Trigger}] is a transition's trigger, a set of events required to be present for the transition to be enabled.
  402. It is also a base class for 2 other kinds of triggers: A \texttt{NegatedTrigger} also defines events \emph{not} allowed to be present; an \texttt{AfterTrigger} listens for an \emph{after-event}.
  403. \item[\texttt{Action}] is an \emph{action}. Actions may be executed when a transition fires. There are 2 types of places in the state tree where actions can be defined: (1) directly on a transition, or (2) as the enter- or exit-actions of a state, which are executed when any transition causes that state to be entered or exited, respectively.
  404. There are 3 types of actions:
  405. \begin{description}
  406. \item[\texttt{RaiseInternalEvent}] raises an internal event. If and when the internal event becomes visible to the statechart, depends on the semantic configuration chosen.
  407. \item[\texttt{RaiseOutputEvent}] raises an output event.
  408. The statechart language does not queue output events, instead they are ``delivered'' through a callback, synchronously called during transition execution. The callback's implementation may queue the output event, or immediately ``handle'' the event, performing some action (but caution is required since the statechart is in the middle executing of a transition).
  409. \item[\texttt{Code}] executes a piece of action language code.
  410. \end{description}
  411. \end{description}
  412. \begin{figure}
  413. \centering
  414. \includegraphics[width=1.0\textwidth]{cd_state}
  415. \caption{Syntactical constructs of the statechart language, part 2}
  416. Dark-gray: Action language constructs, see Figure \ref{fig:cd_expression} and Figure \ref{fig:cd_statement}.
  417. Light-gray: ``Action'' class, see bottom of diagram.
  418. \label{fig:cd_state}
  419. \end{figure}
  420. \subsubsection{Example state tree}
  421. As an example, we will show the XML format, visual representation and state tree of a statechart. The statechart implements a very simple control panel of a 4-burner stove, with 2 buttons: A button for increasing the heat of a burner, and a button for selecting the next burner. Holding the button for increasing the heat for longer than 1 second will cause the heat to be increased every 200 ms. There is no way to decrease the heat of a burner.
  422. \begin{figure}
  423. \centering
  424. \includegraphics{sc_stove}
  425. \caption{Statechart for 4-burner stove example}
  426. \label{fig:sc_stove}
  427. \end{figure}
  428. \begin{figure}
  429. \centering
  430. \includegraphics[width=\textwidth]{statetree_stove}
  431. \caption{State tree for 4-burner stove example}
  432. Detailed action language constructs omitted.
  433. \label{fig:statetree_stove}
  434. \end{figure}
  435. The visual representation is shown in Figure \ref{fig:sc_stove}. The state tree is shown in Figure \ref{fig:statetree_stove}. The XML format is as follows:
  436. \begin{lstlisting}[language=XML,frame=single]
  437. <statechart>
  438. <datamodel>
  439. burners = [0, 0, 0, 0];
  440. selected = 0;
  441. min = func(a: int, b: int) {
  442. if (a &lt; b) return a;
  443. return b;
  444. };
  445. increase = func {
  446. burners[selected] = min(burners[selected] + 1, 9);
  447. };
  448. </datamodel>
  449. <inport name="in">
  450. <event name="pressed_increase"/>
  451. <event name="released_increase"/>
  452. <event name="select_next"/>
  453. </inport>
  454. <root>
  455. <parallel id="p">
  456. <!-- upper orthogonal region -->
  457. <state id="heat" initial="Released">
  458. <state id="Released">
  459. <transition event="pressed_increase" target="../Pushed"/>
  460. </state>
  461. <state id="Pushed" initial="Waiting">
  462. <onentry>
  463. <code> increase(); </code>
  464. </onentry>
  465. <transition event="released_increase" target="../Released"/>
  466. <state id="Waiting">
  467. <transition after="1 s" target="../Increasing"/>
  468. </state>
  469. <state id="Increasing">
  470. <transition after="200 ms" target=".">
  471. <code> increase(); </code>
  472. </transition>
  473. </state>
  474. </state>
  475. </state>
  476. <!-- lower orthogonal region -->
  477. <state id="burner_select">
  478. <state id="BurnerSelect">
  479. <transition event="select_next" target=".">
  480. <code> selected = (selected + 1) % 4; </code>
  481. </transition>
  482. </state>
  483. </state>
  484. </parallel>
  485. </root>
  486. </statechart>
  487. \end{lstlisting}
  488. The targets of transitions are XPath-like paths. During parsing, the targets of transitions are only filled in after building the state tree, as an additional step.
  489. % We will not give a detailed explanation of the XML format for specifying state trees.
  490. % As an example, the following XML fragment results in the statechart and state tree shown in Figure \ref{fig:sc_simple}. This example statechart will also be used in the following section, where we explain transition execution.
  491. % \begin{lstlisting}{caption={XML representation of simple statechart model}}
  492. % <root initial="outer">
  493. % <state id="outer">
  494. % <transition target="/p/region2/s4"/>
  495. % </state>
  496. % <parallel id="p">
  497. % <state id="region1" initial="s1">
  498. % <state id="s1">
  499. % <onentry>
  500. % <raise event="s1"/>
  501. % </onentry>
  502. % </state>
  503. % <state id="s2">
  504. % <onentry>
  505. % <raise event="s2"/>
  506. % </onentry>
  507. % </state>
  508. % </state>
  509. % <state id="region2" initial="s3">
  510. % <state id="s3">
  511. % <onentry>
  512. % <raise event="s3"/>
  513. % </onentry>
  514. % </state>
  515. % <state id="s4">
  516. % <onentry>
  517. % <raise event="s4"/>
  518. % </onentry>
  519. % </state>
  520. % </state>
  521. % </parallel>
  522. % </root>
  523. % \end{lstlisting}
  524. % \begin{figure}
  525. % \centering
  526. % \begin{subfigure}[b]{.5\textwidth}
  527. % \centering
  528. % \includegraphics[width=0.9\linewidth]{test_parallel}
  529. % \caption{Statechart}
  530. % % \label{fig:sub1}
  531. % \end{subfigure}%
  532. % \begin{subfigure}[b]{.5\textwidth}
  533. % \centering
  534. % \includegraphics[width=0.9\linewidth]{statetree_simple}
  535. % \caption{State tree, annotating the transition as a dotted line, and a thick line indicating a \texttt{State}'s default state.}
  536. % % \label{fig:sub2}
  537. % \end{subfigure}
  538. % \caption{Simple, highly synthetic statechart model and its state tree}
  539. % \label{fig:sc_simple}
  540. % \end{figure}
  541. \subsection{Execution: overview}
  542. % The execution of a statechart is a loop, where repeatedly, as a response to a set of input events, zero, one or more transitions are executed, generating a set of output events.
  543. The rest of this chapter explains how statechart models are executed. In doing so, we will also mention regularly that certain properties of statechart constructs are \emph{statically computed}. This points to a static analysis step (or ``tree initialization'' in Figure \ref{fig:xml_init_execute}) that is performed by the parser, right after the state tree has been constructed. This step is not separately discussed.
  544. We will first explain our transition-firing implementation, as it can be understood separately from other parts of the execution runtime. Next, we explain how action language code can be used in statechart models, and how statechart execution causes this code to be executed. Then, we ``zoom out'' to a broad view of how a statechart's execution, i.e. how it is ``stepped'', and how semantic variation is possible in the implementation of this stepping. We then explain the stepping algorithm in detail, including our implementation of the semantic variation points described in BSMLs. Finally, we mention a number of implemented performance optimizations.
  545. \subsection{Firing a transition}\label{sec:firing_transition}
  546. In order to understand statechart execution in our runtime, a good place to start is the transition firing function, since it is independent of the various semantic options that can be chosen.
  547. We assume the transition to fire has already been chosen, and the set of current events is known.
  548. For now, the firing of a transition does 3 things, in the following order:
  549. \begin{enumerate}
  550. \item Exit a subset of the current states, removing them from the \emph{statechart configuration} (= the set of current states). States are exited in child $\rightarrow$ parent order. For each exited state, execute its exit actions, if there are any.
  551. \item Execute the transition's actions (in the order specified
  552. \item Enter a new set of states, adding them to the statechart configuration. States are entered in parent $\rightarrow$ child order. For each entered state, execute its enter actions, if there are any.
  553. \end{enumerate}
  554. The only non-triviality about firing a transition, is determining the sets of exited and entered states. Before we go into detail about how these sets are calculated, we must first explain how the statechart's configuration, and sets of states in general, are represented internally in the SCCD execution runtime.
  555. \subsubsection{Configuration as a bitmap}
  556. It is clear that, as a statechart executes, we have to keep track of its configuration (set of current states). There are many ways to represent the configuration. Conceptually, it is an unordered set. Originally, a Python list of \texttt{State} objects was used, ignoring its order. Simon Van Mierlo at some point introduced \emph{bitmaps} for representing configurations. In order to represent a statechart configuration as a bitmap, every state in the state tree is given a unique integer ID. The root state is given ID 0, and the other states are assigned incrementing numbers in a depth-first fashion, so children are always assigned larger IDs than their parent. Every state ID would be a position in the bitmap, 1 or 0 (in the current configuration or not). Because the resulting bitmaps are relatively small and \emph{dense}, we use Python integers to represent them.
  557. This usage of bitmaps was further extended to all places in the code where \emph{sets of states} had to be represented, because of significant advantages:
  558. \begin{itemize}
  559. \item Compact representation: For every state in the state tree, only a single bit is used.
  560. \item Efficient union and intersection operations: These operations are implemented using bitwise-OR and bitwise-AND operations. A single machine instruction can perform these operations for typical statecharts.
  561. \item When enumerating a bitmap's elements (by iteratively right-shifting the bitmap, and yielding when the least significant bit is 1), they are automatically sorted. This, combined with the depth-first fashion state IDs are assigned, means that when enumerating the ``current states'', they always appear in shallow $\rightarrow$ deep order.
  562. \item In Python, unlimited size: Our bitmap implementation uses an integer internally. There is no limitation on the maximum size of integers in Python 3, making our solution ``work'' with very large statecharts as well, although performance may take a hit.
  563. \item A bitmap can be used as key in a mapping. (Useful for caching of transition candidates, see later)
  564. \end{itemize}
  565. To find a \texttt{State} object based on its state ID, an array of all \texttt{State}s, indexed by their ID, is kept in the \texttt{StateTree} object (Figure \ref{fig:cd_statechart}).
  566. Now that we have seen how bitmaps are used to represent the statechart configuration, and can be used to represent sets of states in general, we can explain how the sets of exited and entered states of a transition can be efficiently calculated.
  567. \subsubsection{Calculating set of exited states}
  568. The set of exited states is always equal to the intersection of the set of the transition arena's descendants (not including the arena state itself), and the current configuration. The arena of a transition is the lowest-common-ancestor of its source and target, that is an Or-state. For every transition, the arena's set of descendants can be statically computed. This statically computed set is stored as a bitmap in the \texttt{Transition} object. Now that both the arena's set of descendants and the statechart configuration are bitmaps, the intersection is therefore a simple bitwise-AND operation, yielding the set of exited states, as a bitmap. Enumerating the elements in this bitmap, in reverse, yields the IDs of the states-to-exit in the right order, i.e. from child to parent.
  569. \begin{figure}
  570. \centering
  571. \includegraphics[]{enter_exit_set}
  572. \caption{Statechart with a complex transition $t$, as an example for entered and exited sets of states.}
  573. \label{fig:enter_exit_set}
  574. \end{figure}
  575. \textbf{Example:} Figure \ref{fig:enter_exit_set} shows a statechart and a transition $t$. First, we show that the set of exited states depends on the statechart configuration, and can therefore not be computed statically: As $t$ is fired, it is clear that we exit the AND-state $S$, and therefore all descendants of $S$ are exited as well. The transition $t$ can be fired regardless of $D$ or $E$ being current states, so either $D$ or $E$ should be exited depending on the configuration. Second, we give an example of what the exited states set could look like. Suppose the statechart configuration consists of the states $\{ root, S, U, C, V, D \}$ The arena of transition $t$ is the root state, so the arena's descendants set consists of all other states $\{ root, S, U, A, B, C, V, ... \}$. The intersection between these 2 sets is the exit-set $\{ S, U, C, V, D \}$. After these states have been exited, only the root state is a current state.
  576. \subsubsection{Calculating set of entered states}
  577. The set of entered states is more complex, but can be computed entirely statically for any transition, if the transition's target state is not a history state. (Our implementation supporting history states is an extension to the current algorithm and will be explained later.) As a start, we take the intersection set between the target's state set of ancestors and the arena's descendants, and we call this set the \emph{enter path}. When enumerating the states of the enter path, a sequence of states is obtained, such that the next state is always a child of the current, from the arena to the target state (not including the arena or target state). All states on the enter path are in the entered states set. Naturally, the target state itself is also entered, and if the target state has children, some of those children should also be entered, depending on the type of the target state:
  578. \begin{itemize}
  579. \item If the target state is an And-state, the And-state and all of its children should be considered targets, recursively.
  580. \item If the target state is an Or-state, the Or-state and its default state should be considered a target, recursively.
  581. \end{itemize}
  582. This behavior is implemented for all states in the polymorphic \texttt{\_effective\_targets} method. It returns a set of states to enter, if the state is the target of a transition. This includes the state itself, and a subset of the children, as described above. This method is called during calculation of the static set of a transition's entered states.
  583. \textbf{Example:} In Figure \ref{fig:enter_exit_set}, the transition $t$'s enter path consists solely of the state $T$. The state $G$ is also entered, as it is the target of the transition, and calling the \texttt{\_effective\_targets} method on $G$ yields its default state $Z$, which is also added to the set of entered states.
  584. But we are not done: in the previous example, the state $H$ should also be added to the enter-set. This is because on the enter path, there was an And-state $T$. And-states occuring on the enter path add their children that are not on the enter path as target states. The polymorphic method \texttt{\_additional\_effective\_targets} behaves this way for And-states, and is called for every state on the enter path.
  585. We have now successfully calculated the set of entered states statically, and this set is stored as a bitmap in the \texttt{Transition} object, so that it can be quickly looked up during transition firing.
  586. \subsubsection{History implementation}
  587. As we have mentioned before, if the target of a transition is a history state, the entered states set of a transition cannot be computed entirely statically. However, our previous calculation is still correct, except that the target state (in this case, a history state) itself is not entered (history states are pseudo states, and cannot be entered!), but instead, the \emph{history value} for that state should be looked up. A history value is simply the set of states to be entered if its history state is the target of a transition, and is simply added to (union-ed with) the statically calculated entered states set. The history value is another bitmap that we keep for every history state in the statechart, making up the ``dynamic'' part of the statechart, together with the statechart configuration.
  588. How to generate the history values? If we exit a state $A$, we have to save some history value if $A$ has a history state as a direct child. The value to save depends on the type of history:
  589. \begin{itemize}
  590. \item If a \emph{deep history state}, the history value to be saved is the intersection of $A$'s descendants with the \emph{exited states set}, which was of course calculated as explained above, before any state was exited. The descendants bitmap of every state is pre-calculated, so this operation is another simple intersection of 2 bitmaps.
  591. \item If a \emph{shallow history state}, state $A$ must be an Or-state, since shallow history states make no sense for And-states. The history value to be saved is the result of calling the method \texttt{\_effective\_targets} on $A$'s exited child: When the history state is a transition target, $A$'s exited child should be treated as the target of the transition, also recursively entering $A$'s default state(s), if there are any. For a given state tree structure, the result of calling the recursive (and therefore slow) \texttt{\_effective\_targets} on any state never changes, so it is also computed statically.
  592. \end{itemize}
  593. So even for shallow history states, the saved history values are deep, i.e. \emph{partial configurations} (instead of recording a single state). With this approach, when restoring a history value, it is not necessary to treat shallow and deep history states separately.
  594. \textbf{Example:} In Figure \ref{fig:history}, when the transition from $C$ to $D$ is made, the shallow history state records the history value $\{ Y, B \}$. If the history state were deep, the recorded history value would be $\{ Y, C \}$.
  595. % \todo{Oorsponkelijk dacht ik hier een voorbeeld te geven, maar er is al een voorbeeld gegeven in Background, sectie \ref{sec:history} ...}
  596. At the beginning of a statechart's execution, there would be no history values unless we explicitly initialize them. We do this, to have \emph{sane} (non-crashing) behavior when a history state is entered before its history value has been saved. The default history values for all states are equal to the result of calling the method \texttt{\_effective\_targets} on the parent of the history state, i.e. we simply enter the ``default'' states.
  597. In order to efficiently set and look up history values in our ``dynamic'' part of the statechart, every history state has a unique, statically-assigned \texttt{history\_id} (incrementing from 0, depth-first), and we store all history values in an array, indexed by history id. We cannot write history values directly to the \texttt{HistoryState} object itself, since it belongs to the ``static'' part of the statechart, which should never change during execution.
  598. \subsubsection{Timed transitions}
  599. There is one more feature that is yet unsupported by the transition firing algorithm as explained so far: timed transitions, i.e. transitions with an after-trigger.
  600. In visual syntaxes of the statecharts, as well as in our statechart XML format, a timed transition is given no event trigger, but a \emph{time duration expression}, such as $100 ms$, indicating that after the source of the transition having been part of the configuration for $100 ms$, the transition becomes enabled.
  601. % \todo{Idem: dit is ook al uitgelegd in Background... }
  602. In the implementation, an after-trigger behaves much like a regular trigger, in the sense that it also responds to an event. This event, called an \emph{after-event}, is hidden to the modeler. It is \emph{scheduled} with its future timestamp upon entering the source state of the transition, and \emph{canceled} upon exiting it.
  603. There are 2 types of events a statechart can respond to: \emph{Input events} come from the environment, and \emph{internal events} are raised by fired transitions in the statechart itself. At first intuition, it may seem like after-events are internal events, since they are scheduled by the statechart itself, but this is not true: internal events cannot have a time delay, they always occur (conceptually) at the same timestamp as they were raised, even if the transition that raised the internal event occured \emph{logically} before the transition responding to it. Input events, by contrast, can be given any timestamp, and are put in a global \emph{event queue}, where they are sorted by timestamp, and popped when they become due. It has been mentioned before that the implementation of this event queue is not part of the statechart language, yet a statechart instance expects \emph{some implementation}, and must be given callbacks for scheduling and canceling events upon instantiation.
  604. % For now, it suffices to know that upon entering a state with outgoing timed transitions, after-events have to be scheduled for those transitions, and when exiting such state, all of its scheduled after-events have to be canceled. Every statechart instance therefore has access to the global event queue, but only uses it for scheduling (future) events, and canceling scheduled events. When scheduling an event, it receives a scheduled ID, which it can use for canceling later on.
  605. Just like the history values, we cannot store the scheduled IDs in the state tree (\texttt{State}, \texttt{Transition} or \texttt{AfterTrigger}, ... objects), as shouldn't be altered by execution. We therefore statically assign every \texttt{AfterTrigger} a unique after-ID (incrementing from 0, depth-first), which serves as index in an array of scheduled IDs, stored in the ``dynamic'' part of the statechart.
  606. \subsubsection{Final algorithm}
  607. To summarize, the transition firing algorithm looks as follows:
  608. \begin{enumerate}
  609. \item Calculate the sets of entered and exited states as described above.
  610. \item Exit states in order child $\rightarrow$ parent. For every exited state $A$:
  611. \begin{enumerate}
  612. \item If $A$ has a history state as its direct child, save the history value for that history state, as described above.
  613. \item For all outgoing transitions of $A$ that have an after-trigger, cancel the scheduled after-event.
  614. \item Perform the exit-actions of $A$.
  615. \item Remove $A$ from the configuration.
  616. \end{enumerate}
  617. \item Perform the transition's actions.
  618. \item Enter states in order parent $\rightarrow$ child. For every entered state $B$:
  619. \begin{enumerate}
  620. \item Add $B$ to the configuration.
  621. \item Perform the enter-actions of $B$.
  622. \item For all outgoing transitions of $B$ that have an after-trigger, schedule their after-event.
  623. \end{enumerate}
  624. \end{enumerate}
  625. The dynamic part of a statechart consists of the following values:
  626. \begin{itemize}
  627. \item The statechart configuration (set of current states), represented as a bitmap.
  628. \item The history values of all history states in the statechart, represented as a history-ID-indexed array of bitmaps.
  629. \item The scheduled IDs for current states that have timed outgoing transitions, represented as an after-ID-indexed array of ``None'' (no scheduled event) or a ``scheduled ID''.
  630. \end{itemize}
  631. These 3 values are fields in an object called \texttt{StatechartExecution}. This is also the object that implements the method for firing a transition (\texttt{fire\_transition}).
  632. \subsection{Action language in a statechart}
  633. Action language code can occur in various places in a statechart model:
  634. \begin{itemize}
  635. \item The statechart's \emph{data model}, which is a \texttt{Block} (a sequence of \texttt{Statement}s), declaring and initializing the statechart's variables.
  636. \item The guard condition of transitions is an action language \texttt{Expression}.
  637. \item The \emph{delay} of an after-trigger is also an \texttt{Expression}.
  638. \item The various types of actions (enter state, exit state, or fire transition):
  639. \begin{itemize}
  640. \item Code-actions execute a \texttt{Block}.
  641. \item Raise event-actions have \texttt{Expression}s for the raised event's parameters.
  642. \end{itemize}
  643. \end{itemize}
  644. \begin{figure}
  645. \centering
  646. \includegraphics[]{sc_counter}
  647. \caption{``Counter'' statechart model, with a datamodel and action language expressions and statements}
  648. \label{fig:sc_counter}
  649. \end{figure}
  650. \textbf{Example:} Figure \ref{fig:sc_counter} shows a statechart model containing action code. When the statechart is initialized, the datamodel code is executed, initializing the \texttt{ctr} variable to 0. Both transitions have a guard condition reading the \texttt{ctr} variable. The transition from Counting to itself updates \texttt{ctr} by incrementing it by one.
  651. The following desired features influenced the design of the action language, and its integration in the statechart language:
  652. \begin{itemize}
  653. \item Variables and functions declared in the statechart's data model should be readable and writeable in all parts of the statechart.
  654. \item Variables and functions can be declared outside of the data model section, but they are temporary and local to the fragment (scope) where they appear.
  655. \item If a transition's trigger defines parameters on its events, those parameters should be readable from the transition's guard condition, and its actions.
  656. \item As part of the statechart language, builtin functions (and possibly variables) can be defined, which are readable (not writable) in all parts of the model. An example of a useful builtin function, is the \emph{in\_state} function, checking if a given state (or lists of states) is part of the current configuration. Another useful builtin function \emph{log}, logs text to the console.
  657. \end{itemize}
  658. \subsubsection{Action language static analysis (in a statechart)}\label{sec:statechart_scopes}
  659. As explained in Section \ref{sec:action_lang_impl}, every parsed expression or statement in the action language has to be statically analyzed before it can be executed. For static analysis, a \texttt{Scope} object must always be given, in which variables are looked up, and to which declared variables are added.
  660. The solution is to first define a special ``builtin'' scope, consisting of our builtin functions, which are declared as ``constant'' (read-only). Then an ``instance'' scope, with as parent the builtin scope, is created, and passed to the static analysis function of the statechart's datamodel, adding all of the variables declared in the datamodel to that scope. Finally, all other occurrences of action code are given their own scope, with the instance-scope as parent. To allow transition's guard conditions and action code to read event parameters, transitions themselves also get their own scope.
  661. \begin{figure}
  662. \centering
  663. \includegraphics[width=1.0\textwidth]{sc_scopes}
  664. \caption{A statechart model (upper left) and its hierarchy of scopes}
  665. \label{fig:sc_scopes}
  666. \end{figure}
  667. \textbf{Example:} Figure \ref{fig:sc_scopes} shows a statechart model and its scope hierarchy. The variable \texttt{y}, declared in the datamodel, is part of the instance-scope. Both transitions have their own scope, containing their event parameters, if there are any. Action and guard scopes have their transition's scope as parent, so they can read event parameters. Also note that the function $f$ declared in the datamodel also has its own scope, not explicitly created by the statechart parser, but by the action language itself.
  668. \subsubsection{Action language execution (in a statechart)}
  669. In the action language, the hierarchy of \texttt{Scope} objects is a static representation of stack memory during execution. If the action code contains no recursive function calls, an upper bound can be put on the maximum amount of stack memory required for a statechart's execution. The absense of recursive calls would also guarantee halting for every piece of action code, as the action language has no loop-structures. However, neither the action language nor the statechart language checks for the presence of recursive calls to claim any of these model properties.
  670. At the beginning of a statechart's execution, we start out with an empty (zero-length) stack memory object. Then, the stack frame for the builtin-scope is pushed, and the builtin values (function implementations) copied to that frame. Next, the stack frame for the instance-scope is pushed, and the action code of the statechart's datamodel is ran, initializing that stack frame. Now the statechart's memory has been initialized, and default states can be entered (possibly triggering more action code) to complete the statechart's initialization. For transition execution and guard evaluation, stack frames are pushed and popped, always symmetrically. In between the execution of transitions, the stack frames of the builtin-scope and instance-scope are never popped. When e.g. evaluating the guard condition of the upper transition in Figure \ref{fig:sc_scopes}, the stack frames of the transition's event parameters and of the guard condition (the latter is an empty frame) are pushed, and popped when evaluation is complete.
  671. \subsection{Broader picture: Stepping of a statechart}
  672. We've looked at the transition execution algorithm and the way action language fragments in a statechart are statically analyzed and executed. We ``zoom out'' now, to look at the statechart interface ...
  673. We treat statecharts as a subclass of Big-Step Modeling Languages \cite{sabzali2010deconstructing}, where at the highest level, a model's execution is a sequence of \emph{big-steps}. A big-step is a model's response to a set of input events, and consists of the execution of zero or more transitions, changing the model's state and producing output events.
  674. As we have seen in Section \ref{sec:bsmls}, it is here that many semantic variations can occur, such as:
  675. \begin{itemize}
  676. \item If multiple transitions are enabled, which subset and in what order to fire them?
  677. \item If the firing of transitions raises internal events, when and for how long can those enable transitions?
  678. \item If transitions make changes to the statechart's memory, at what point do they become visible to other transitions?
  679. \end{itemize}
  680. We will now cover our implementation of the statechart's stepping implementation, which is where all of the semantic variation points are implemented.
  681. \subsection{Rounds}\label{sec:rounds}
  682. In BSMLs, a big-step consists of any sequence of \emph{small-steps}. Small-steps are sets of concurrently executed transitions, but since concurrency is not yet implemented in the runtime, a small-step always consists of a single transition. Depending on the semantic options chosen, sequences of small-steps may also be grouped into \emph{combo-steps}, with a sequence of combo-steps making up a big-step. \emph{Maximality}-semantic options exist for both big-steps and combo-steps, defining restrictions on the transitions that can be taken together in such a step.
  683. \begin{figure}
  684. \centering
  685. \includegraphics[]{big_step}
  686. \caption{Left: A big-step without combo-step semantics. Right: A big-step with combo-step semantics.}
  687. \label{fig:big_step}
  688. \end{figure}
  689. In the implementation, this grouping (big-step $>$ combo-step $>$ small-step) was generalized into the \texttt{SuperRound} class (Figure \ref{fig:cd_round}). There is also a \texttt{SmallStep} class, which has the superclass \texttt{Round} in common with \texttt{SuperRound}. Big-steps and combo-steps are implemented as \texttt{SuperRound}s. A \texttt{SuperRound} has a fixed \emph{subround} object, of type \texttt{Round}, and can therefore be another \texttt{SuperRound}, or a \texttt{SmallStep} round. This way, arbitrary numbers of levels of groupings can be constructed.
  690. \begin{figure}
  691. \centering
  692. \includegraphics[width=1.0\textwidth]{CD_Round}
  693. \caption{Class Diagram for the runtime implementation of rounds and transition candidate generation}
  694. \label{fig:cd_round}
  695. \end{figure}
  696. A \texttt{Round} can be executed, which may cause transitions to be fired. If the execution of a round caused no transitions to be fired, we say the round was \emph{empty}. When a \texttt{SuperRound} is executed, it will repeatedly execute its subround, until its subround becomes empty.
  697. The round execution method is called \texttt{run\_and\_cycle\_events}, and returns a pair of bitmaps containing information about the transitions that were fired during the round:
  698. \begin{description}
  699. \item[arenas\_changed] A bitmap containing the arenas of the transitions fired during round execution.
  700. \item[arenas\_stabilized] A bitmap containing the arenas of the transitions fired during round execution that have a target state syntactically marked as ``stable''.
  701. \end{description}
  702. If a round was empty, both bitmaps will be zero.
  703. \subsubsection{Maximality implementation}
  704. Every SuperRound has a \emph{maximality} setting. This setting enforces restrictions on the transitions that can be taken together during a round's execution. Possible maximality settings are:
  705. \begin{description}
  706. \item[TakeMany] No restrictions
  707. \item[TakeOne] No 2 transitions with overlapping arenas can be taken together
  708. \item[Syntactic] No 2 transitions with overlapping arenas entering ``stable'' states can be taken together
  709. \end{description}
  710. These restrictions are implemented by maintaining for every SuperRound an ever-growing set of \emph{forbidden arenas}, i.e. arenas that new transition candidates are not allowed to overlap with. At the beginning of every round, the initial set of forbidden arenas is received as a parameter. For a big-step, this parameter will be 0. For other rounds, this parameter are the forbidden arenas mandated by its parent round. After every fired transition, the forbidden arenas-set grows according to the maximality option for the SuperRound.
  711. The set of forbidden arenas is implemented as a bitmap. When an arena is ``in the set'', not just the state ID-bit of the arena is ``1'', but also the state ID-bits of its \emph{descendants}. This way, checking for overlapping arenas (binary-AND) when considering transition candidates can be efficiently done, as well as adding new arenas to the set (binary-OR).
  712. \textbf{Example:} Figure \ref{fig:rounds_execution}. shows a sequence diagram of the execution of a big-step. In the example, there are 3 levels of rounds: The superrounds ``big\_step'' and ``combo\_step'', and ``small\_step''. At the highest level, big-step execution is initiated by another part of the runtime, with the forbidden arenas set to 0, because at the beginning of a big-step, no transitions have yet been fired, and therefore no restrictions exist. The big-step executes the first combo-step, which in turn executes the first small-step. The forbidden arenas for the first small-step are still 0, as no transitions have yet been executed. When the first small-step has executed, a pair (\texttt{arenas\_changed1}, \texttt{arenas\_stable1}) is returned to the combo-step. The combo-step records these values, but in its next execution call to the small-step, it only passes \texttt{arenas\_changed1} as the set of forbidden arenas, since its maximality setting (TakeOne) does not care about stable states. The next small-step returns a pair like the first one did. The combo-step's 3rd attempt to execute a small-step results in an empty small-step, causing the combo-step to end. At the end of the combo-step, the union of the \texttt{arenas\_changed}- and \texttt{arenas\_stabilized}-values of both small-steps are returned, for the big-step to deal with. The big-step maximality setting (Syntactic) only looks at the \texttt{arenas\_stabilized}-value, and passes it on as the forbidden arenas-set in the execution of the 2nd combo-step. The second combo-step consists of only a single small-step. An attempt at a 3rd combo-step yields an empty step, causing the big-step to end.
  713. \begin{figure}
  714. \centering
  715. \includegraphics[width=1.0\textwidth]{rounds_execution}
  716. \caption[Sequence diagram for the execution of a big-step]{Top: Sequence diagram for the execution of a big-step.
  717. Bottom: An example statechart producing the big-step sequence above, from its initial state, upon any input event. All transitions are triggerless. States with checkmark-symbol are stable states.
  718. Note: the method \texttt{run} is actually called \texttt{run\_and\_cycle\_events} in the runtime.}
  719. \label{fig:rounds_execution}
  720. \end{figure}
  721. Using our constructs of rounds and superround-maximality, all combinations of Big-Step Maximality and Combo-Step Maximality can be implemented. Figure \ref{fig:rounds_example} shows the rounds-implementations for all combinations of maximality options currently supported.
  722. \begin{figure}
  723. \centering
  724. \includegraphics[width=1.0\textwidth]{rounds_example}
  725. \caption[Implementations of currently supported maximality configurations]{Implementations of currently supported maximality configurations. Note that the parent of a small-step is always a \textsc{TakeOne}-maximality superround. This assures fairness.}
  726. \label{fig:rounds_example}
  727. \end{figure}
  728. \subsubsection{Detection of never-ending rounds}
  729. \textsc{Take Many} and \textsc{Syntactic} allow for an infinite number of transitions to be taken. Similar to Rhapsody \cite{harel2004rhapsody}, this is dealt with by counting the number of sub-rounds in a \texttt{SuperRound}. When a certain limit is exceeded, a runtime error is thrown. This limit is currently hardcoded at 100, but could be a property of the model.
  730. \subsubsection{Fairness under all circumstances}
  731. It is important to note that in Figure \ref{fig:rounds_example}, the parent of a small-step is always a superround with TakeOne-maximality. Even if no TakeOne-semantic option has been chosen in the statechart's semantic options, a TakeOne-superround is secretly inserted, to ensure \emph{fairness}: Even if there never is any restriction on the transitions that can execute, we still demand them to be executed in a fair order, i.e. transitions with non-overlapping arenas are given a higher priority.
  732. \subsubsection{Event Lifeline implementation}
  733. The event lifeline semantic options of BSMLs specify when input- and internal events are present. They can all be considered relative to some round (big/combo/small-step), and put into two categories of their relationship to that round (with exceptions, discussed later): Either the event becomes present in the \emph{current}, or in the \emph{next} round. Table \ref{tab:event_lifeline} shows these categories.
  734. \begin{table}
  735. \centering
  736. \begin{scriptsize}
  737. \begin{tabular}{ | r | c | c | c | }
  738. \hline
  739. & \textbf{Current round (remainder of)} & \textbf{Next round} & \textbf{Special}\\
  740. \hline
  741. Big Step & input: \textsc{Whole}, internal: \textsc{Remainder} & - & internal: \textsc{Queue} \\
  742. Combo Step & input: \textsc{First Combo Step} & internal: \textsc{Next Combo Step} & \\
  743. Small Step & input \textsc{First Small Step} & internal: \textsc{Next Small Step} & internal: \textsc{Same} \\
  744. \hline
  745. \end{tabular}
  746. \end{scriptsize}
  747. \caption{Event Lifeline options in ``rounds'' implementation}
  748. \label{tab:event_lifeline}
  749. \end{table}
  750. The implementation of the \texttt{Round} class offers methods to add an event to the current or next round. Depending on the semantic options chosen for the statechart, these methods bound to the ``right'' \texttt{Round}-objects, serve as ``the'' callbacks for raising an internal event and adding input events, during execution.
  751. At the beginning of every big-step, the big-step's input events are added to the right round, always in the category ``remainder''.
  752. ``Remainder'' and ``next'' events are added to the lists \texttt{Round.remainder\_events} and \texttt{Round.next\_events}, respectively (shown in Figure \ref{fig:cd_round}). At the end of every round, those lists are cycled:
  753. \begin{python}
  754. remainder_events = next_events
  755. next_events = []
  756. \end{python}
  757. At any point in time, the current set of enabled events is the union of all \texttt{remainder\_events} of all \texttt{Round}s. Retrieving this union is recursively implemented in the \texttt{Round.enabled\_events} method, merging the remainder-events with the enabled events of the parent round. It is always the \texttt{SmallStep} object initiating this recursive request, as it needs to know the current set of enabled events in order to generate transition candidates (see next section).
  758. There are 2 exceptions to the categories ``remainder'' and ``next''. The first is the Internal Event Lifeline option \textsc{Queue} makes raised events present in a later big-step. This is not an option mentioned in the BSMLs paper, but it was the standard behavior of the original version of SCCD. It is included merely for completeness. A major drawback of this option is the unpredictability of when a raised event will be responded to: raised events are added to the global event queue, where they may be interleaved with input events.
  759. The second exception is the Internal Event Lifeline option \textsc{Same}, which is only to be used with concurrency-semantics. Although SCCD currently does not support \emph{true} concurrency, there is a concurrency option, allowing more than one transition to be included in a small-step. When this option is chosen, an event raised by one transition may be responded to by another transition in the same small-step. There is however still a causality relation between the transition raising the event, and the transition responding to it. It is therefore not a ``true concurrency'' and the same semantics can be achieved by replacing the small-steps with combo-steps.
  760. \subsection{Transition candidate generation}
  761. In order to execute a small-step, conceptually, a set of transition candidates is generated, and from this set, a candidate is chosen, depending on Priority semantics. This candidate is fired, concluding the small-step.
  762. The set of transition candidates can be understood as a \emph{series of filters} applied to the set of all transitions. Those filters are:
  763. \begin{enumerate}
  764. \item Is the transition's source-state part of the statechart's \textbf{current configuration}?
  765. \item Is the transition's trigger is satisfied with respect to the set of \textbf{currently enabled events}? No distinction is made here between input events or internal events.
  766. \item Does the transition's arena overlap with any of the \textbf{forbidden arenas}? This set is determined by the maximality-semantics chosen.
  767. \item Is the transition's \textbf{guard condition} satisfied with respect to the current state of the statechart's memory?
  768. \end{enumerate}
  769. We'll refer to these filters by their number in the enumeration above.
  770. \subsubsection{Efficient candidate generation}
  771. These filters could be applied in any order without changing the result, but not all orders have the same performance: Filter (4) is a potentially expensive operation (it may cause a billion decimals of $\pi$ to be calculated), so it is best to apply this filter at the end, when as many transition candidates as possible have already been eliminated.
  772. The other filters, (1), (2) and (3), have the interesting property, that for the same input (current configuration, currently enabled events, forbidden arenas), they will give the same output: A transition's source state, trigger, or arena does never change! This means that, theoretically, we can pre-calculate their results for all inputs. However, the number of possible inputs would be too large, to compute them all.
  773. A better alternative is to \emph{cache} computed semi-filtered sets of candidates at runtime\footnote{Credit for this feature originally goes to Simon Van Mierlo}. This is possible since all filter inputs are representable as bitmaps (including the set of enabled events, if we assign a unique integer ID to every event), so they (and tuples of them) can serve as keys in a hashtable or search tree. We simply use Python's dictionary type, which uses a hashtable.
  774. The rationale behind caching transition candidates is that a statechart will re-visit the same state configuration / enabled events / forbidden arenas during its lifetime. Some consideration needs to be made when choosing our caching key. We could cache the candidate sets of applying filters (1), (2) and (3), but then our cache be highly specific, making it grow very large, and \emph{cache hits} would be rare. The opposite choice of a non-specific filter, such as only (1), would make the cache smaller and cache hits more frequent, but would yield candidate sets that still have to be filtered further by the unapplied filters, possibly reducing performance.
  775. Filter key to be cached can be swapped out in the SCCD runtime. They are called GeneratorStrategies (see Figure \ref{fig:cd_round}). 2 strategies are included:
  776. \begin{description}
  777. \item[CurrentConfigStrategy] Computes and caches candidates based on the tuple (current configuration, forbidden arenas), then filters based on enabled events, and finally filters on guard condition evaluation.
  778. \item[EnabledEventsStrategy] Computes and caches candidates based on the tuple (enabled events, forbidden arenas), then filters based on current configuration, and finally filters on guard condition evaluation.
  779. \end{description}
  780. A performance comparison of these strategies, plus additional ones could be considered future work.
  781. \subsubsection{Cache initialization: Partial pre-computation of candidates}
  782. We have seen that complete pre-computation of transition candidates is intractable. We can however initialize the candidate-cache with pre-computed values that are expected to be requested frequently. This would pass for partial pre-computation of candidates, and mixes nicely with caching.
  783. Currently, pre-computation is only implemented for the EnabledEventsStrategy, computing candidate sets for the following values:
  784. \begin{itemize}
  785. \item (0, 0): No current events and no forbidden arenas. (Containing all triggerless transitions)
  786. \item ($\{e\}$, 0): Where $\{e\}$ is the singleton-set of event $e$, $e$ being any event that serves as a trigger for a transition in the statechart. No forbidden arenas.
  787. \end{itemize}
  788. This pre-computation scheme significantly increases the number of cache hits at the beginning of a statechart's execution, since very frequently, the set of currently enabled events consists only of a single event, or of no events.
  789. \subsubsection{Priority implementation}
  790. In the BSML paper, Priority and Order of Small Steps (OOSS) are considered separate semantic aspects, but in our implementation they are considered one and the same option. We described in Section \ref{sec:ooss_priority} how they both produce partial orderings on the transitions in a statechart, and how their combination may produce a total ordering on all sets of potentially simultaneously enabled transitions in the statechart, in which case the model behaves deterministically, and in a way that the modeler has full control over.
  791. In SCCD, priority is implemented by allowing the modeler to select and combine different priority options. these options are:
  792. \begin{description}
  793. \item[Same source] \textsc{None} (disable priorities), \textsc{Explicit Priority} (takes XML document order for transitions that have the same source state)
  794. \item[Hierarchical] \textsc{None} (disable priorities), \textsc{Source-Parent}, \textsc{Source-Child}, \textsc{Arena-Parent}, \textsc{Arena-Child} (see Section \ref{sec:ooss_priority})
  795. \item[Orthogonal] \textsc{None} (disable priorities), \textsc{Explicit Ordering} (takes XML document order of orthogonal regions)
  796. \end{description}
  797. In the general case, the combination of these options yields a partial ordering among the transitions of the statechart, which is explicitly constructed as a directed graph (or more precisely, a list of graph edges). First, this graph is checked for cycles. A cycle represents a conflict in the priority options chosen, and is a static error. Next, a single global ordering of all transitions (as a list) is constructed from the partial ordering, by introducing missing orderings between transitions \emph{non-deterministically}. The eventual behavior is still deterministic, because orderings are only added between transitions that cannot be enabled simultaneously. The algorithm is as follows:
  798. \begin{enumerate}
  799. \item Initialize our total ordering $T$ to be an empty list.
  800. \item In the priority graph, find the set of \emph{highest-priority} transitions $H$. This is the set of transitions that have no incoming edges. We know this set will be non-empty, because we have already assured that there are no cycles in the graph.
  801. \item Check whether in $H$, there exists any pair of transitions $t1$ and $t2$ that can be enabled simultanously. Concretely, this is the case if:
  802. \begin{itemize}
  803. \item $t1$ and $t2$ have the same source state
  804. \item $t1$'s source is an ancestor of $t2$'s source, or vice versa
  805. \item $t1$'s source is orthogonal to $t2$'s source, i.e. the lowest-common ancestor of the sources is an And-state.
  806. \end{itemize}
  807. If this is the case, the model is found to be non-deterministic, and an error is thrown.
  808. \item Add the transitions of $H$ to $T$ in \emph{any order}. This the part where we non-deterministically introduce an ordering among the transitions in $H$.
  809. \item Remove all edges involving the transitions of $H$ from the priority graph.
  810. \item Repeat from step (2) until the graph has no more edges (and $T$ contains all transitions in the statechart)
  811. \end{enumerate}
  812. This total ordering of transitions is statically constructed as a global list (one list for the entire statechart). In order to generate transition candidates, a series of filters is applied as explained in the previous sections, in an order-preserving way, producing a new list of candates, ordered by priority. If this candidate list is non-empty, its first element is always the selected candidate.
  813. % Move this to Chapter "Evaluation"?
  814. Note that the hierarchical arena-options do not cover all situations where one transition's source state is an ancestor of another transition's source state, producing a non-deterministic model. They may also conflict (a cycle in priority graph) with e.g. ``Same source''-priority. For instance, Figure \ref{fig:priority_arena_nondeterminism} shows two transitions that can be enabled simultaneously, have different source states, are not orthogonal, yet would not be assigned a priority with respect to each other, because they have the same arena. It is therefore our opinion that these options have little practical use. They were implemented nevertheless, because it was only a small task to do so.
  815. Also note that every priority option can be disabled by choosing \textsc{None}. SCCD may be the only statechart tool that allows the modeler to do this. It is highly likely however, that the resulting model will be found non-deterministic, and hence rejected by the runtime.
  816. The default semantics of SCCD are \textsc{Explicit Priority}, \textsc{Explicit Ordering} and \textsc{Source-Parent}. These options always yield a deterministic model.
  817. \begin{figure}
  818. \centering
  819. \includegraphics[]{priority_arena_nondeterminism}
  820. \caption{Transitions with different source states, but the same arena}
  821. \label{fig:priority_arena_nondeterminism}
  822. \end{figure}
  823. \subsubsection{Lazy evaluation of transition candidates}
  824. We explained how the list of transition candidates is the result of applying a sequence of filters on a totally ordered list of transitions. Every consecutive filter operation could produce a new (smaller) list, but we only fully construct such a (intermediate) list if the result is being cached. Otherwise, we \emph{lazily evaluate} the list, as we are only interested in its first element (= the next transition to fire), or whether the list is empty (= no more enabled transitions). For lazy evaluation, we use Python's generator expressions.
  825. \subsection{Memory snapshots}
  826. SCCD supports the Enabledness Memory Protocol and Assignment Memory Protocol semantic aspects. (Almost always, one would chose the same option for both Enabledness and Assignment Memory Protocol.) For each aspect, the options \textsc{Big Step}, \textsc{Combo Step} and \textsc{Small Step} exist. These options determine the ``moment in time'' that action language expressions ``see'' when evaluating variables. This moment in time is always the beginning of some step: E.g. if the \textsc{Combo Step}-option is chosen, all guard conditions evaluated and transitions executed as part of the combo-step will read the statechart's variables as they were at the beginning of the combo-step.
  827. This behavior is trivially implemented by creating a snapshot of a statechart's memory at the beginning of the step chosen in the Memory Protocol option. The snapshots are used for reading, while the actual memory is used for writing.
  828. For any given statechart, the size of the snapshot is always the same. Snapshots are taken in between transitions, when the only stack frames in the statechart's memory are the ``builtin'' and ``instance'' scopes. The ``builtin''-scope is read-only, so does not have to be snapshotted. Therefore the size of every snapshot is equal to the size of the ``instance''-scope, which is statically computed from the statechart's datamodel section (see Section \ref{sec:statechart_scopes}).
  829. The snapshotting is implemented in the \texttt{MemoryPartialSnapshot} class (Figure \ref{fig:cd_memory_snapshot}). It wraps around a normal \texttt{Memory} object (the default implementation of \texttt{MemoryInterface}, expected by action language constructs for execution), which it uses as a delegate for all write-operations, as well as read-operations outside of the snapshotted memory (i.e. builtin variables and temporary variables, local to the action code fragment being executed). At the end of every Memory Protocol-step, the snapshot is refreshed (\texttt{flush}). This is done by registering a callback on the correct \texttt{Round} object.
  830. \begin{figure}
  831. \centering
  832. \includegraphics[]{cd_memory_snapshot}
  833. \caption{Class diagram of MemoryPartialSnapshot class.}
  834. Dark-gray classes defined in action language package.
  835. \label{fig:cd_memory_snapshot}
  836. \end{figure}
  837. \subsubsection{Race condition detection}
  838. If more than one transition writes a value to the same location in memory during the same Memory Protocol-step, a race condition exists. Race conditions are detected by maintaining a bitmap (\texttt{round\_dirty} in \texttt{MemoryPartialSnapshot}) of memory locations written during the current Memory Protocol-step. A memory location written twice causes a \emph{runtime error}. Race conditions cannot be detected statically. At the end of every Memory Protocol-step, when the snapshot is refreshed, the bitmap is reset to 0.
  839. \subsubsection{Locally observable writes}\label{sec:locally_obs_writes}
  840. It would be counter-intuitive if due to the Memory Protocol option chosen (e.g. \textsc{Combo Step}), a transition writing to a statechart's variable $x$ would not be able to consequently read the variable $x$ and retrieve the value it had just written. This is taken care of in \texttt{MemoryPartialSnapshot} by maintaining another bitmap, \texttt{trans\_dirty} of memory locations written during the current transition. For these locations, consequent read operations are performed on the actual memory, not the snapshot. Consequent writes to these locations are also not erroneously detected as race conditions.
  841. \subsection{Various optimizations}
  842. In the previous sections, the following performance optimizations were described:
  843. \begin{itemize}
  844. \item Use of bitmaps to represent sets of states and sets of events.
  845. \item Static calculation of a transition's arena, and arena's descendants, allowing for efficient calculation of a transition's \emph{exited states} when firing a transition.
  846. \item Static calculation of a transition's \emph{entered states}.
  847. \item Swappable transition candidate generation strategies, with caching.
  848. \item Lazy evaluation of transition candidates.
  849. \end{itemize}
  850. In this section, we discuss a few optimizations that haven't been covered yet.
  851. % \subsubsection{Sorting lists of enabled events}
  852. % The following pieces of action code in a statechart have access to the event parameters of a transition:
  853. % \begin{itemize}
  854. % \item The transition's guard condition, an expression.
  855. % \item The transition's action code, a block of statements.
  856. % \end{itemize}
  857. % Before executing them, the transition's event parameters must be copied to the stack, where they occupy the transition's stack frame. In order to copy event parameters, the set of current events has to be iterated
  858. % If the guard conditions of many different transitions that have event parameters have to be evaluated, this potentially leads to the copying
  859. % The order in which they are expected on the stack is defined as ordered by of their event ID, followed by the order of the parameter within the event (parameters are ordered within events).
  860. \subsubsection{No semantic-option-dependent conditional branches in execution loop}
  861. The semantics of a statechart model never change during execution, therefore, no sort of execution loop should waste time checking them with each iteration.
  862. Upon initialization of a statechart instance, based on the semantics defined in the statechart model, objects and relationships between them are constructed in a way that they will carry out statechart execution according to the semantics specified, but the objects themselves are unaware of the semantics they represent.
  863. For instance, Figure \ref{fig:rounds_example} shows the construction of different hierarchical \texttt{Round}-structures, as constructed based on different maximality-semantics, but the \texttt{Round}-objects themselves do not have access to the semantic configuration of the model.
  864. \subsubsection{Converting all durations to the same unit}\label{sec:model_delta}
  865. The action language has a builtin \emph{time duration} type. Duration-literals make time durations unambiguous by including a duration-suffix (e.g. \texttt{ms} for milliseconds). In a statechart, the duration type is expected for the delay-value of timed transitions.
  866. Internally, a duration is an integer value with a \emph{unit}. Units reflect the duration-suffixes used, but upon construction, every duration is automatically converted to the largest possible unit, without losing information, called \emph{normalization}. For arithmetical operations between durations, such as addition or subtraction, they have to be converted to their \emph{greatest common divisor}-unit before the operation can be performed, again followed by normalization. Units have no absolute ``size'' (they are defined only relative to each other), offering great flexibility and an unlimited range of units to be defined, but also making these duration conversions expensive operations.
  867. Because units can be arbitrarily mixed in a statechart, potentially requiring many unit conversions during execution, hurting perfomance, all durations are transparently converted to \emph{integer} values representing multiples of the same time unit (or more precisely: time duration), called the \emph{model delta}. The model delta is the smallest representable amount of time during model execution. Even if a model only contains durations in the order of seconds, it is wise to set the model delta much smaller, because the timestamps assigned to input events are also expressed in model delta units, and are rounded down if necessary. By default, a model delta of $100 \mu s$ is used, allowing for maximum durations (and timestamps) of $ 5.8 * 10^{7}$ years if 64-bit unsigned integers are used for timestamps. The model delta can be overriden in the model.
  868. \subsubsection{Python-specific optimizations}
  869. Our implementation contains some optimizations specific to Python, the languge in which it is written:
  870. \begin{description}
  871. \item[Native integer type vs. SCCD's Bitmap class] Bitmaps are used in many parts of the code. A \texttt{Bitmap} type was defined, inheriting Python's integer type, overriding its string-representation \texttt{\_\_str\_\_}-method to return a bitstring instead of a decimal number. Arithmetic operators between Bitmaps were also overridden to return Bitmaps, not integers. It was found however, that this had a significant negative impact on performance, simple arithmetic operations on bitmaps requiring function calls. The solution was to disable our custom bitmap implementation when the environment variable \texttt{SCCDDEBUG} was not set, falling back to native integers. The Python language is very flexible in this regard, the solution having the following form:
  872. \begin{python}
  873. if DEBUG:
  874. class Bitmap:
  875. ...
  876. else:
  877. Bitmap = int
  878. \end{python}
  879. With this modification alone, when executing all models from the test framework, the average time of executing a transition went from 0.2 ms to 0.16 ms, a 20\% speedup!
  880. \item[Slotted classes] By default, Python allows arbitrary attributes to be added to objects. This is implemented by storing the attributes of objects in (modifiable) dictionaries. This introduces a significant memory overhead and slows down attribute access. Python offers an alternative in the form of \emph{slotted classes}, that declare a sequence of fields to always be part of the object.
  881. Slotted classes have drawbacks, such as not supporting multiple inheritance. They also require additional syntax, for the declaration of the ``slots''. We \emph{make the common case fast} and use slotted classes in much of the \texttt{statechart.static} package defining syntactic constructs, whose attributes are constantly accessed during execution.
  882. \end{description}
  883. \section{Controller Implementation}\label{sec:controller_impl}
  884. The Controller is the main primitive for executing SCCD models.
  885. \subsection{SCCD models}\label{sec:single_instance_xml}
  886. An SCCD model is at the highest level a \emph{class diagram} (CD) of statechart instances that may be created and destroyed at runtime. In this thesis, the class diagram always consists of a single class, of which a single instance is created. To work with these models, we developed an ad-hoc XML format with an extremely simple structure:
  887. \begin{lstlisting}
  888. <single_instance_cd>
  889. <model_delta>100 us</model_delta>
  890. <statechart> ... </statechart>
  891. </single_instance_cd>
  892. \end{lstlisting}
  893. The \texttt{<model\_delta>} node is optional and sets the ``model delta'', the smallest possible time duration that can be simulated in the model. The \texttt{<statechart>} node is the root node of a model in our statecharts XML format, described in Section \ref{sec:statechart_xml}.
  894. This format is parsed into a \texttt{SingleInstanceCD} object (Figure \ref{fig:cd_controller}), implementing the \texttt{AbstractCD} interface for SCCD models. The \texttt{SingleInstanceCD} object can be passed to the Controller's constructor.
  895. \subsection{Controller Interface}\label{sec:controller_interface}
  896. The Controller's interface is a primitive for executing SCCD models. The Controller maintains a single global \emph{priority queue} of timestamped events. Events serve as input for one or more statechart instances. (An event with more than one target instance is a multicast or broadcast event.) Timestamps in the Controller are just integers, they have no physical meaning, only logical.
  897. The Controller maintains an integer value of \emph{simulated time}, which is 0 right after the Controller's creation, and always equal to the timestamp of the most recently popped event from the queue. The simulated time can only increase (or stay the same).
  898. These are the Controller's most important methods, towards its environment:
  899. \begin{description}
  900. \item[\texttt{add\_input(timestamp: int, ..., event\_name: str, ...)}] Adds an input event to the controller's queue.
  901. \item[\texttt{run\_until(timestap: int)}] Advances simulated time. In a loop, pops events from the queue and delivers them as input to their target instances. The target instances respond to the input by making a big-step. The call does not return until either (1) the queue is empty, or (2) the next event to be popped would advance simulated time further than the \texttt{timestamp} parameter of the call.
  902. The following fragment is almost literally taken from the source code:
  903. \begin{python}
  904. def run_until(self, now: int):
  905. for timestamp, entry in self.queue.due(now):
  906. self.simulated_time = timestamp
  907. for instance in entry.targets:
  908. instance.big_step([entry.event])
  909. \end{python}
  910. \item[\texttt{next\_wakeup(): int}] Getter. Returns the timestamp of the earliest event in the controller's queue.
  911. \end{description}
  912. All methods are synchronous (taking control of the current thread until they return). Using these 3 methods, the 3 different runtime \emph{platforms} foreseen in the original version of SCCD can be implemented. Section \ref{sec:platforms} contains a description of these platforms and a demonstration of their implementation using the Controller as a primitive.
  913. \subsubsection{Integer timestamps}
  914. Figure \ref{fig:controller_int_timestamps} shows how the Controller only ``talks'' integer timestamps with its environment, as well as with its model. If meaning must be given to the Controller's timestamps (e.g. for real-time simulation), it is up to the environment to query the model for its ``model delta'', which is a time duration of which the controller's timestamps are multiples (see also Section \ref{sec:model_delta}).
  915. These are the motivations for making the Controller work with timestamps only as logical entities:
  916. \begin{itemize}
  917. \item The Controller's environment may not care about real-time simulation: E.g. when running a model test (a model + timed inputs + expected, timed outputs), we are only interested in the correctness wrt. its output, and run the model as fast as possible.
  918. \item It allows the Controller's timestamps to be interpreted as a wide range of time units. This could also be done by using our \texttt{Duration} type (which also offers a wide range of units) instead of raw integers, but its performance is worse, because of possible unit-conversions.
  919. \item Following the single-responsibility principle of OO-design, the Controller's purpose is to queue incoming events, and to handle them in the right order, when simulated time is requested to advance, and nothing more.
  920. \end{itemize}
  921. \begin{figure}
  922. \centering
  923. \includegraphics[width=1.0\textwidth]{controller_int_timestamps}
  924. \caption{The Controller only ``talks'' integer timestamps}
  925. \label{fig:controller_int_timestamps}
  926. \end{figure}
  927. \subsubsection{Output}
  928. It was mentioned before that statecharts can produce output in several ways:
  929. \begin{enumerate}
  930. \item As \emph{output events}, produced as the result of a big-step.
  931. \item By assigning values to \emph{output variables}, that can be read at any time by the environment.
  932. \item By calling \emph{synchronous functions} from action code, i.e. during the firing of a transition.
  933. \end{enumerate}
  934. Of these, SCCD supports (1) and (3). Output variables are currently not supported.
  935. In BSML, output events are unordered sets, produced at the end of every big-step. The Controller deviates here by delivering every output event immediately, when it is raised, via a callback function, i.e. during transition firing and from the controller's thread. This has the following benefits:
  936. \begin{itemize}
  937. \item It saves the overhead of adding output events to an output queue.
  938. \item If desired, the environment can still queue output events.
  939. \end{itemize}
  940. The implementation of the callback function should take care not to block, as it will block the model's execution.
  941. No attempt is made to add output events of a big-step together in a ``bag of events'', but a special event signals the end of a big-step, so these ``bags'' can still be constructed (e.g. the test framework does this)
  942. \subsection{Design}
  943. \begin{figure}
  944. \centering
  945. \includegraphics[width=1.0\textwidth]{CD_Controller}
  946. \caption{Class diagram of Controller and other classes involved in SCCD model execution}
  947. \label{fig:cd_controller}
  948. \end{figure}
  949. Figure \ref{fig:cd_controller} shows the class diagram of the \texttt{Controller} class and several other classes that play a role in SCCD model execution. The Controller has an \texttt{EventQueue} to which input events are added. The Controller also has an \texttt{ObjectManager}, which maintains a list of all created instances during execution (in our case, only a single instance), and checks adherence of these instances to the class diagram (not yet implemented in our branch). \texttt{Instance}s perform \emph{big-steps} in response to a set of input events.
  950. \texttt{AbstractCD} is an interface that loaded SCCD models implement. The \texttt{SingleInstanceCD} class is an ad-hoc implementation of this interface for models that at all times only consist of a single statechart instance. The Controller receives an \texttt{AbstractCD} model as its constructor parameter, and passes it on to the ObjectManager, which uses it to instantiate the \emph{default class} (in our case, the single statechart the model consists of).
  951. The classes below the dotted line, \texttt{EventLoop} and \texttt{EventLoopImplementation} are not part of the ``core runtime'', and optionally wrap around the controller. They are mentioned in the next section.
  952. \subsection{Runtime platforms}\label{sec:platforms}
  953. \begin{figure}
  954. \centering
  955. \includegraphics[]{platforms}
  956. \caption{Platforms supported in original SCCD.}
  957. Figure taken from \cite{VanMierlo2016}.
  958. \label{fig:platforms}
  959. \end{figure}
  960. An important feature in original SCCD was the ability to select a type of runtime ``platform'' for generated code. The available platforms were (Figure \ref{fig:platforms}):
  961. \begin{description}
  962. \item[Event Loop] The event loop platform is intended for easy integration with existing 3rd party event loop implementations, as typically found in UI toolkits, like TkInter. This platform can be integrated with any existing event loop implementation that offers functions for
  963. \begin{enumerate}
  964. \item scheduling a future callback
  965. \item clearing a scheduled callback
  966. \end{enumerate}
  967. Using these callbacks, the platform will attempt to let the statechart simulation run in sync with wall-clock time, scheduling periods of statechart execution (i.e. responding to a statechart input events) as tasks in the 3rd party event loop. The statechart execution would thus run from the same thread as the 3rd party event loop, interleaving it with e.g. UI events.
  968. The runtime library comes with implementations for TkInter and JavaScript (browser, NodeJS).
  969. \item[Threads] The threads platform has its own native event loop implementation, using only the target language's standard library. This event loop implementation will also attempt to run the simulation in real-time. When it is started, the threads platform takes over the current thread, so the user is required to write any input or output logic in separate threads.
  970. \item[Game Loop] The game loop platform is perhaps the simplest of all, as it only offers a function that advances the simulated time to ``now'' (wall-clock time). The function is blocking and runs as-fast-as-possible. Traditionally, with each iteration of a game loop, the function would be called to update ``the world'', followed by rendering the display.
  971. \end{description}
  972. \subsubsection{Implementation with Controller primitive}
  973. In original SCCD \cite{VanMierlo2016}, these platforms were explicitly implemented side-by-side, but shared a lot of common (duplicated) logic. Now, with the Controller's interface as a primitive, all 3 ``platforms'' can be built, as we demonstrate.
  974. The most trivial is \emph{game loop} integration, where with each iteration, the simulated time advances up to the current wall-clock time:
  975. \begin{python}
  976. start_time = now()
  977. while True:
  978. controller.add_input( ... ) # e.g. keyboard or mouse events
  979. controller.run_until(now() - start_time)
  980. render()
  981. \end{python}
  982. The \emph{threads} platform is slightly more complicated, at least if we want to run the simulation in real time. We use a \texttt{threading.Condition} object instead of the sleep-function, in order to be woken up when there is an input:
  983. \begin{python}
  984. input_queue = queue.Queue() # thread-safe queue
  985. condition = threading.Condition()
  986. def controller_thread():
  987. while True:
  988. while event := input_queue.pop():
  989. controller.add_input(..., event, ...)
  990. controller.run_until(now()) # this may take a while
  991. sleep_duration = controller.next_wakeup() - now():
  992. if sleep_duration > 0:
  993. with condition:
  994. condition.wait(sleep_duration)
  995. def add_input(event):
  996. input_queue.put(event)
  997. with condition:
  998. condition.notify() # wake up controller thread
  999. thread = threading.Thread(target=controller_thread)
  1000. thread.start()
  1001. \end{python}
  1002. The \emph{event loop} platform is similar, but instead of sleeping, we schedule a future callback in the event loop we are integrating with. Also, we do not need a thread-safe input queue, as it is safe to directly call \texttt{controller.add\_input} from everywhere. The event loop platform is as follows:
  1003. \begin{python}
  1004. scheduled_id = None
  1005. def run():
  1006. controller.run_until(now()) # this may take a while
  1007. sleep_duration = controller.next_wakeup - now():
  1008. if sleep_duration > 0:
  1009. scheduled_id = schedule(sleep_duration, run)
  1010. else:
  1011. scheduled_id = schedule(0, run)
  1012. schedule(0, run) # start controller when event loop starts
  1013. def add_input(event):
  1014. controller.add_input(..., event, ...)
  1015. # "wake up":
  1016. cancel(scheduled_id)
  1017. run()
  1018. \end{python}
  1019. Finally, an example from our \emph{test framework}, where for the execution of a test case, all input events are known from the beginning, and simulation runs as fast as possible. The controller runs in its own thread, so that test output can be verified in parallel:
  1020. \begin{python}
  1021. for i in test_case.input:
  1022. controller.add_input(..., i.event, ...)
  1023. pipe = queue.Queue() # thread-safe queue
  1024. def controller_thread():
  1025. controller.run_until(None) # None here means: +infinity (return when event queue empty)
  1026. pipe.put(None) # signal end of run
  1027. thread = threading.Thread(target=controller_thread)
  1028. thread.start()
  1029. while True:
  1030. output = pipe.get()
  1031. ... # verify test output
  1032. \end{python}
  1033. \subsubsection{Event loop library}
  1034. Part of the SCCD project is a library implementing the logic from the above \emph{event loop} example as a class, called \texttt{EventLoop}. Figure \ref{fig:cd_controller} shows how this class wraps around the Controller. In reality, the class does more than shown in the example, because:
  1035. \begin{enumerate}
  1036. \item The schedule-callback of the chosen event loop implementation may expect a timeout in a different unit than the (Python) time function in use, which may in turn differ from the time unit expected by the model itself. See also Figure \ref{fig:controller_int_timestamps}.
  1037. \item If the simulation continuously cannot keep up with the wall clock time (e.g. because the computer is too slow), with a naive implementation, invocations of \texttt{run\_until} will take longer with each invocation, increasingly starving other tasks (such as adding input events).
  1038. \end{enumerate}
  1039. To solve the first problem, event loop implementations are communicated with the \texttt{EventLoop} class in a declarative manner (see \texttt{EventLoopImplementation in Figure \ref{fig:cd_controller}}). Similarly, the time function and its unit are also declared (not pictured). Because time unit conversions have to happen all the time, the conversion ratios are pre-calculated for efficiency.
  1040. To solve the second problem, it is checked whether the \texttt{sleep\_duration} variable calculated is a negative number. This indicates wall-clock time running faster than our simulation. If this is the case, the negative number is added to the parameter of \texttt{run\_until} in the next round (reducing the amount of time-to-simulate), purposefully making the model run at a reduced speed. This solution was evaluated to effectively keep the model responsive to input under heavy load. The simulation would also recover (``catch up'') with wall-clock time when load was reduced.
  1041. % ^ these problems apply to all real-time types of execution
  1042. % TODO:
  1043. % - model I/O primitives (input: events, output: synchronous callbacks)
  1044. % - overview: controller's "global" event loop
  1045. \section{Important changes from original SCCD}\label{sec:changes}
  1046. As mentioned in the beginning of this chapter, our fork of SCCD differs significantly from the original. The most drastic change is that SCCD is no longer a code generator. We'll first motivate our decision of abandoning the code generation approach. Next, we explain how the 3 different runtime platforms (each with their own interface) are no longer a fundamental part of the ``core'' runtime, and instead use a single primitive interface usable by all 3 platforms. Finally, we list a number of smaller improvements.
  1047. \subsection{No more code generation}
  1048. % Intermediate language
  1049. In order to support multiple target languages without having to replicate much of the compilation logic, the original SCCD compiler transformed input models to an intermediate procedural language. This language did not have a textual syntax, only abstract. It was developed to be the greatest common divisor of the target languages supported. All supported target languages (Python, JavaScript, C\#) were similar enough (procedural, object-oriented and garbage collected) to make this approach work.
  1050. % Can not support all languages
  1051. However, it would have been non-trivial, to add a language like C, because of its manual memory management. This limitation contributed to abandoning this compilation strategy.
  1052. % Difficult to understand code:
  1053. Another, more important problem, was that the part of the compiler that generated the intermediate language code (or better: AST) was very hard to understand. The construction of the AST tree looked nothing like readable code. Some improvement was made by using a stateful ``writer'' object to build the syntax tree, but then still identifiers in the target language were strings in the compiler code, making it impossible to statically check them for errors. Example:
  1054. \begin{python}[caption={A fragment of \texttt{generic\_generator.py} of original SCCD},captionpos=b]
  1055. self.writer.beginConstructor()
  1056. self.writer.addFormalParameter("controller")
  1057. self.writer.beginMethodBody()
  1058. self.writer.beginSuperClassConstructorCall("ObjectManagerBase")
  1059. self.writer.addActualParameter("controller")
  1060. self.writer.endSuperClassConstructorCall()
  1061. self.writer.endMethodBody()
  1062. self.writer.endConstructor()
  1063. \end{python}
  1064. Finally, perhaps the most important reason for abandoning the code generator, was the fact that the execution runtime library had already become the place where most of the complexity was located: it was where most of the semantic variability was implemented, as well as a few runtime optimizations (such as transition candidate caching).
  1065. In order to extend SCCD with additional semantic variability, in a clean way, the decision had to be made whether to move all complexity to the code generator, or to the execution runtime. Because of the reasons just mentioned, the latter was chosen.
  1066. Our decision to go with the runtime approach still does not rule out the future addition of a code generator to the SCCD project: The insights obtained from implementing statechart semantic variability as a runtime may be used as a foundation for a code generator.
  1067. \subsection{No more explicit runtime platforms}
  1068. In original SCCD, there were 3 types of controllers, each implementing one of the 3 runtime platforms described in Section \ref{sec:platforms}.
  1069. \subsubsection{Limitations}
  1070. The original, side-by-side implementation of the 3 platforms had some shortcomings:
  1071. \begin{itemize}
  1072. \item Complex design: The available platforms were implemented in the SCCD runtime library, so, in principle, they should have been selectable independently of the compilation step. However, due to contrains in the original design, caused by heavy reliance on class inheritance (with many overrides), the platform had to be selected at compile time, and the generated code depended on a specific platform. This is shown in Figure \ref{fig:cd_old_controller}.
  1073. \begin{figure}
  1074. \centering
  1075. \includegraphics[width=1.0\textwidth]{CD_old_controller_platforms2}
  1076. \caption{Class diagram of original SCCD's implementation of the 3 platforms}
  1077. \label{fig:cd_old_controller}
  1078. \end{figure}
  1079. \item Use of floating point numbers for timestamps: Older Python versions use floats for timestamps, so this choice seemed natural. However, floats have non-uniform precision. At some point, this led to a problem, where a very small update in the simulated time would be rounded down, unexpectedly not incrementing the simulated time, and freezing the simulation. A workaround was developed, detecting this issue, and forcing the result to be rounded up. However, the use of integer timestamps is a much safer choice, for its uniform, predictable precision. Also, since Python 3.7, a more precise time function exists, returning time as an integer.
  1080. \item Wall-clock time-aware: All \texttt{Controller}-classes would query the current wall-clock time, to attempt to let the simulation run in (soft) real time. In some cases however, it is desirable to run a simulation at lower or higher speed. For instance, when (non-interactively) testing a model, the simulation typically needs to run at the highest possible speed, to produce results (test passed/failed) as soon as possible. Simulations for scientific research would also be run at the highest possible speed, if one is only interested in the eventual outcome.
  1081. \end{itemize}
  1082. \subsubsection{Solution}
  1083. As explained in Section \ref{sec:platforms}, there is now a single Controller primitive that can be used to create any of the 3 runtime platforms, and can be used to run the simulation at any speed relative to wall-clock time, including as-fast-as-possible execution. The event loop platform is included as a library that wraps around the controller.
  1084. \subsection{Single event queue}
  1085. In the original version of SCCD, every statechart instance had its own event queue, as well as a separate event queue in the Controller. The vague intent was to allow parellel execution of instances, but this goal was never accomplished. The existance of multiple event queues made it complex to find the timestamp of the next event to be processed (having to check all queues).
  1086. Now, there is only a single event queue, in the Controller, for containing the input events for all statechart instances in the runtime. Hypothetically, parallel execution is still possible by making the event queue thread-safe, and sharing it among ``worker threads''.
  1087. \subsection{Duration units}
  1088. Treating time durations as numbers is confusing, because the unit may differ depending on the context. All time durations in after-transitions in original SCCD were expressed in seconds, as floating point values. Now, every time duration has a mandatory suffix, and the action language's static type system considers durations their own type, and cannot simply be casted to or from a number. Suffixes are `fs' (femtoseconds), `ps' (picoseconds), `ns' (nanoseconds), `us' (microseconds), `ms' (milliseconds), `s' (seconds), `m' (minutes), `h' (hours) and `D' (days).
  1089. \subsection{More powerful test framework}
  1090. Since we are interested in comparing the behavior of different semantic configurations, it is useful to see how the same statechart model behaves if we change the semantics. In original SCCD, the semantic configuration was inseparable of the model, which makes sense, because a model is usually developed only with a specific semantic configuration in mind. So now in SCCD, this is currently still the case, but it is also possible to share statechart models between test cases, and override the semantics, achieving our goal.
  1091. Another useful feature is that when defining a semantic configuration for a test case, for each semantic aspect, one can use the wildcard symbol ``$*$'' or a comma-separated list to express that multiple options should cause the test to succeed. If multiple options are chosen for multiple semantic aspects, all configurations making up the cartesian product are tested.
  1092. Finally, one can denote tests that are expected to fail with the filename prefix \texttt{fail\_} instead of the usual prefix \texttt{test\_}. This feature is mostly used to check whether syntactically invalid models are detected as such.