ARTICLE IN PRESS
The Journal of Systems and Software xxx (2005) xxx–xxx www.elsevier.com/locate/jss
Designing state-based systems with entity-life modeling Bo Sande´n a
a,*
, Janusz Zalewski
b
Computer Science, Colorado Technical University, 4435 N. Chestnut St., Colorado Springs, CO 80907, USA b Computer Science, Florida Gulf Coast University, Fort Myers, FL 33965, USA Received 18 April 2004; received in revised form 8 February 2005; accepted 8 February 2005
Abstract This article introduces the entity-life modeling (ELM) design approach for multithread software. The article focuses on problems that can be described by state machines with associated activities. We compare ELM with a traditional design approach based on a dataflow model. Using a cruise controller for a car as an example, we show that entity-life modeling is a more direct and less ceremonious approach that produces a much simpler architecture. 2005 Elsevier Inc. All rights reserved. Keywords: Cruise control; Software architecture; Multithreading; State machine
1. Introduction With the greater availability of multithreading, concurrent solutions are becoming more common in various kinds of control systems (Sanz and Zalewski, 2003). In this article we present the entity-life modeling design approach (ELM). It leads to multithreaded software solutions, which are modeled on concurrency inherent in the problem environment (Sande´n, 1997; Sande´n, 2003). We develop an ELM solution to a simple control system, a cruise controller for a car, and compare it to a published COMET solution (Gomaa, 2000). COMET is a traditional design method based on dataflow between threads.
2. Entity-life modeling ELM is a design approach for multithreaded software that must respond to events in the problem domain. * Corresponding author. Tel.: +1 719 590 6733; fax: +1 719 598 3740. E-mail addresses:
[email protected] (B. Sande´n),
[email protected] (J. Zalewski).
0164-1212/$ - see front matter 2005 Elsevier Inc. All rights reserved. doi:10.1016/j.jss.2005.02.004
Borrowing a term from the area of agile development, ELM is ‘‘low in ceremony’’ (Fowler, 2004). This means that it does not outline every step to the final design, and leaves many details to the designers. It is intended for designers who want to identify the major candidate thread architectures for a given problem. Software engineers are often too eager to start programming to devote much effort to upfront design. At the same time, choosing the right architecture is crucial because one architecture may require a much greater design and programming effort than another, and may incur much more run-time overhead. ELM lets you identify the possible architectures without a lengthy process. ELM introduces two novel, interrelated ideas. First, it models thread architectures on concurrent structures in the problem much as object-oriented methods model software objects on domain objects (Sande´n, 2003). ELM extends modeling to multithreading. It takes the view that threading primarily has to do with timing: Computations should be in different threads if they can be performed simultaneously. Second, ELM introduces the concept of a concurrency level in order to determine the number of threads
ARTICLE IN PRESS 2
B. Sande´n, J. Zalewski / The Journal of Systems and Software xxx (2005) xxx–xxx
that is, in a particular sense, optimal for a given problem. The concurrency level of a problem is the number of events that can ever occur at the same time (or, more precisely, in an arbitrarily short interval) in the problem environment. ELM uses the concurrency level as guide to eliminate unnecessary threads and context switches by letting each event occurrence be handled by a single thread. 2.1. Event threads and thread models Where the object oriented analyst looks for objects in the domain that can be modeled in the software, the designer using ELM also discovers event threads in the problem and models control threads on them.1 An event thread is a sequence of event occurrences in the problem, which are relevant to the software (Sande´n, 2003). The relevant events are those that the software must react to. Events occurring in the problem domain are often signaled by sensors. You must also include time events, which must usually be created by the software. A time event is the event that a certain amount of time has elapsed. A thread model of a problem is a set of event threads that account for all relevant event occurrences in the problem and is such that each event occurrence belongs to a single event thread. Each event thread in the model generally becomes a control thread in the software. Event threads are said to co-occur if they have events occurring at the same time. A thread model is said to be optimal if it has as many threads as the concurrency level of the problem. All the threads in an optimal model cooccur, so there are times—at least theoretically—when every thread in an optimal model has an event occurring. (In some problems the concurrency level cannot be calculated, but an upper limit can be found.) Optimality is seen as a guideline for eliminating unnecessary threads. It is up to the designer to decide how close to optimal the thread model should be. A nonoptimal model may be more intuitive or adaptable or may separate concerns better. It may, however, have many threads that spend most of their time waiting for a few shared resources. 2.2. Modeling techniques Instead of prescribing a development process, ELM provides techniques for discovering event threads and implementing them as control threads in software. Thread identification is a basic technique where you discover individual event threads until a thread model has been found that covers all the event occurrences in the problem domain. You can often associate event threads 1 We use the term ‘‘control thread’’ for a software thread, such as in Java (Appendix A).
with problem-domain entities such as a human user or a device such as an elevator. The thread captures the entityÕs life history. In practice, it is often possible to recognize patterns of event threads in the problem rather than individual threads. Two common ELM patterns, the resource-user thread and the resource thread, deal with resource users and shared resources in the domain (Sande´n, 2003). A third ELM pattern is concurrent activities, which applies to many control systems such as the cruise controller (Sande´n, 2001). We describe it in detail next.
3. The concurrent activities pattern The concurrent activities pattern bases the threads on the activities specified for a state machine. State machines can be represented by statecharts, which are part of UML (Appendix B) (Fowler, 2004). State machines are among the most successful formal models for software specification. They are particularly useful for reactive systems, defined as those that are not adequately described in terms of the output that results from a set of inputs but instead in terms of the relationship of inputs and outputs over time (Harel and Pnueli, 1985). A statechart captures nicely the logic of many a mechanical and electro-mechanical device from an automated garage door, an answering machine, a cruise controller and a window elevator for a car up to systems that control satellites or aircraft. Statecharts are further described in Appendix B. A state machine can be implemented in software quite straightforwardly. In fact, once you have represented the behavior of a device as a state machine, you have essentially designed a program that controls the device. The concurrent activities pattern (Sande´n, 2001) shows how a state machine and its activities can be implemented in a multithread environment. The state machine is implemented as a synchronized object, and any activities are implemented as threads. The concurrent activities pattern differs from the State Pattern (Gamma et al., 1995), which deals with state dependent behavior by defining a class for each state and delegating each request to an object of the class representing the current state. Although the State Pattern could be used internally within the statemachine synchronized object, we choose the simpler implementation where the operations explicitly manipulate a state variable. We next describe the pattern participants: the state machine synchronized object and the activity threads. To discuss the implementation, we rely on JavaÕs terminology for concurrency, which is perhaps familiar to the most readers. Java provides control threads and synchronized classes, whose instances can safely be accessed by different threads. Further details are given in Appen-
ARTICLE IN PRESS B. Sande´n, J. Zalewski / The Journal of Systems and Software xxx (2005) xxx–xxx
dix A. ELM is not Java specific and can be used with similar primitives provided under different names by other languages. 3.1. State-machine synchronized object The state machine is implemented as a synchronized class. In a system such as the cruise controller, there is a singleton synchronized object. Other problems may require multiple instances of a certain state machine. The attributes of the state-machine synchronized object include a state variable, which indicates the current state, and other parameter variables, which are used to record the results of actions. For example, the cruise controller state-machine object may have a variable that holds the current desired cruising speed. Operations on the state machine synchronized object are invoked either by activity threads (discussed below) or as the result of interrupts. Interrupt handling is language and system dependent. Real-time Java has asynchronous event handlers, which can call the operations on the state machine synchronized object (Bollella and Gosling, 2000). In Ada, the state machine is implemented as a protected object, whose operations can themselves be interrupt handlers (Burns and Wellings, 1998). The operations defined for a state-machine synchronized object are of the following types: • An event-reporting operation is called when an event has occurred. If the event causes a state transition, the operation updates the state variable and notifies any threads blocked pending the state change. It also takes any actions associated with the event. It is often called either as the result of an interrupt or by an activity thread that samples (polls) some quantity in the problem environment. For example, a cruise controller may have a thread that samples the lever that the car driver uses to control cruising. This sampler thread reports any newly pressed or newly released button by calling event-reporting operations. • A state query takes a (super)state identity as a parameter and returns a Boolean value indicating whether the state machine is currently in that (super)state. In an alternative implementation, it may return the identity of the current state. (A superstate can be any group of states (Appendix B).) • A state wait operation takes a (super)state identity as a parameter and blocks the calling thread until that (super)state is entered. • A parameter query returns the value of a parameter variable (or some derived value). In the case of the cruise controller, it may return the desired cruising speed.
3
• A state-dependent operation can be used if an activity defined for a superstate has different effect depending on the current substate. For example, an activity thread may periodically issue a signal, which is suppressed in an emergency substate. The events and actions of a state machine are instantaneous. To emulate this, all the operations on a statemachine synchronized object must be approximately instantaneous. Lengthy operations—activities—must be performed by threads while they are not holding the state machine object locked. As a rule of thumb, an operation is not approximately instantaneous if it can be interrupted by the occurrence of an event. 3.2. Activity threads An activity thread implements one or more of the activities of a state machine. It communicates with the state machine synchronized object by calling its operations. It finds out about state transitions through state query or state wait operations. In the simplest case, the thread queries the state periodically, which is particularly straightforward with threads that perform some periodic action anyway. The throttle thread in the cruise controller is an example. While automatic cruising is on, throttle periodically checks the state of cruising and adjusts the physical throttle as necessary. The following two kinds of activity threads are necessary: 1. If event occurrences are detected by sampling, a sampling activity must be defined for the appropriate (super)state, and the corresponding activity thread must be implemented. 2. In some programming languages such as Ada, time events require threads with delay statements (Burns and Wellings, 1998). The time event occurs when control is returned from the delay. Real-time Java instead provides special timer classes (Bollella and Gosling, 2000). As a first cut, give every activity its own thread. Then optimize the solution by identifying activities that do not co-occur. Such activities can often be assigned to the same thread. In the cruise controller, for example, the activities Maintain speed and Increase speed are defined for different states and do not co-occur. The activities are semantically related by operating on the physical throttle so can conveniently be combined into the throttle thread. Separate activity threads are generally warranted if one activity is defined for a superstate and another for a substate. Even so, the activities can sometimes be combined into a single thread by letting the activity thread of the superstate take on additional functions in certain
ARTICLE IN PRESS B. Sande´n, J. Zalewski / The Journal of Systems and Software xxx (2005) xxx–xxx
4
substates. On the other hand, a single state can have two or more co-occurring activities defined. Combining activities in different states into one thread can simplify state transitions. Changing from one activity thread in one state to a different activity thread in another may otherwise require an elaborate and costly state change protocol.
•
• 4. COMET We compare ELM to the more traditional design method COMET, which stands for Concurrent Object Modeling and Architectural Design Method (Gomaa, 2000). COMET is a dataflow design approach that traces its history back to structured analysis. It uses UML (Fowler, 2004) to describe the design, and provides structuring criteria for the objects, subsystems and threads. COMET is much more ceremonious than ELM and produces a requirements model, an analysis model and a design model in that order. Requirements modeling consists of identifying actors, use cases and their relationships. Typical for object oriented approaches, the analysis phase includes a static model with class diagrams and a dynamic model with interaction diagrams and statecharts. The COMET analysis phase also produces a system context model, which shows how external classes interface to the system being developed. We focus here on the design phase, which consists of the following major steps: 1. 2. 3. 4. 5. 6. 7. 8.
Design the high-level architecture Structure the subsystems Develop a distributed architecture, if applicable Design threads Analyze performance of the design Design classes for each subsystem Develop detailed design Conduct detailed performance analysis
We discuss each step except step 3 and the performance steps 5 and 8. 4.1. Steps 1–2: Designing the high-level architecture, and structuring the subsystems In COMET, a subsystem is a set of objects. COMET defines the following subsystem types2: • A control subsystem controls some aspect of the system. It normally receives inputs from and generates outputs to the problem environment, usually without human intervention. As an example, the cruise con2 There are also I/O and system functions subsystems, which we do not discuss here.
•
•
•
trollerÕs control subsystem involves inputs from the driverÕs controls, the brake and the engine, as well as output to the throttle. A coordinator subsystem coordinates the activities of more than one control subsystem and may schedule work items to be executed by specific control subsystems. A data collection subsystem acquires external data as from sensors. It may also reformat and store the data, and perform elementary data analysis, such as range checking. A data analysis subsystem may be combined with data collection subsystems. It analyzes the acquired data. Additional functions may include data visualization, report generation and automatic notification if certain parameters exceed nominal values. A server subsystem responds to other subsystemsÕ requests for service. It may provide access to data repositories or I/O devices. A user interface subsystem provides an operator interface to other subsystems. It formats and displays data and handles certain input functions, such as parameter changes, starting and stopping certain processes, etc. It also handles logon and security functions.
Once the subsystems have been identified, their individual design follows. In step 2, you design classes and produce communication diagrams for each subsystem and for the whole system. This is done without yet introducing threads. 4.2. Step 4: Structuring the threads Given the classes and objects, you identify threads by applying the following structuring criteria to the objects: • I/O criteria map device interfaces onto I/O threads. • Internal criteria identify threads performing internal computations, whether periodic or triggered by asynchronous events. Other internal threads are control threads and user interface threads. A control thread executes a state machine. (This is in contrast to ELMÕs concurrent activities pattern where the state machine is a passive object.) • Priority criteria distinguish between time-critical and non-time-critical threads. • Clustering criteria consolidate two or more threads. • Inversion criteria further reduce the number of threads by merging some of them. Next, three kinds of thread interfaces are defined: message communication, event synchronization and access to passive objects.3 The thread behavior is specified,
3
A passive object is one that does not have its own thread.
ARTICLE IN PRESS B. Sande´n, J. Zalewski / The Journal of Systems and Software xxx (2005) xxx–xxx
including a threadÕs interface, structure, timing characteristics (estimated frequency and execution time), relative priority, event sequencing, and exceptions. 4.3. Step 6: Designing classes At this step, you determine the operations for the classes that were identified during analysis. Suitable operations can be found by analyzing the object interactions as shown in communication or sequence diagrams. Operations are also derived from statecharts and class diagrams. 4.4. Step 7: Detailed design In detailed design, threads and classes are combined. The major issues include: • Relationship and division of responsibilities between threads and classes • Synchronization issues • Interthread communication Threads are activated by internal or external events. Once a thread has been activated, it calls operations on passive objects. Interthread communication relies on message passing and is done through connector objects. COMET distinguishes three types of message communication: • Loosely coupled (asynchronous) message communication • Tightly coupled (synchronous) communication without reply • Tightly coupled communication with reply At this point, the performance of the participating threads can be analyzed using rate-monotonic scheduling (Klein et al., 1993) or some other method. Our brief summary does poor justice to COMETÕs over 30 steps and many diagrams. It is much more involved than the structured-analysis methods from which it stems. On the other hand, with its emphasis on message communication, it relies on the simplest aspects of multithreading only. This can be an advantage because there is a shortage of programmers who understand multithreading well. Another possible advantage is that the dataflow model extends to distributed systems where threads on different computers must rely on asynchronous message communication.
5. The cruise control problem We use the cruise controller for a car with automatic transmission as an example illustrating ELM and
5
COMET. The controller regulates the carÕs speed by adjusting the throttle periodically. Following Gomaa (2000), we assume that the car driver controls the cruise controller by means of a lever, which creates interrupts. The driver can accelerate by moving the lever to Accel. By then moving the lever to Cruise, the driver captures the current speed of the car and defines it as the desired cruising speed. The cruise controller then maintains that cruising speed. When the driver brakes, the controller suspends cruising. When the driver moves the lever to Resume, cruising resumes at the earlier defined cruising speed. This arrangement provides sufficient system safety: Should anything go wrong with the automatic control, the driver can always disable it and reach a safe state by touching the brake pedal. The cruise controller may have some time critical elements, but has no conflicting deadlines that would justify elaborate scheduling as according to the rate-monotonic algorithm (Klein et al., 1993). Throttle adjustment is not deadline dependent. Small variations around the average time between adjustments are more acceptable than adjustments that always occur within a predefined period but with considerable jitter. There are no other complications such as distributed computing or multiprocessing.
6. ELM solution for the cruise controller The behavior of the cruise controller is well understood and can be captured in a statechart without any preliminaries. Fig. 1 shows that the controller enters the Initial state when the ignition is turned on. When the driver selects Accel, the controller enters Accelerating where it normally remains until the driver selects Cruise. At that point, the controller saves the current speed and enters Cruising, where it maintains the speed automatically. If the driver selects Accel, the controller reenters Accelerating. If the driver brakes while the controller is in the superstate Automated control, it transitions to Cruising off. Then, if the driver selects Resume, the controller
Initial entry / Clear desired speed Accel [Brake off]
Off
Brake pressed
Off
Automated control
Cruising off
Accel [Brake off]
Resume [Brake off]
Cruise / Set desired speed Accelerating do / Increase speed
Accel
Fig. 1. Cruise control statechart.
Cruising do / Maintain speed
ARTICLE IN PRESS B. Sande´n, J. Zalewski / The Journal of Systems and Software xxx (2005) xxx–xxx
6
reenters Cruising, unless the brake is being operated. When the driver turns cruising off, the controller returns to Initial. The ELM recipe for a problem that is defined by a state machine and its activities is the concurrent activities pattern. The statechart is implemented as a statemachine synchronized object. The two activities in the statechart, Increase speed and Maintain speed, are combined in the activity thread throttle. Further classes can be derived from the statechart by identifying things that the cruise controller must interface to or maintain data about. The UML communication diagram in Fig. 2 shows the interactions between the following objects, which are all singleton instances of their respective classes: • The synchronized object cruiseControl has a variable state with a unique value for each state in Fig. 1 (except the superstate). The variable desiredSpeed contains the current desired cruising speed. Each of the event reporting operations accel, resume, cruise, off and brakePressed corresponds to an event in the cruise control problem environment. The state query getCruisingState returns one of the values Initial, Accelerating, Cruising and Cruising Off. The parameter query getDesiredSpeed returns the desired speed. • The synchronized object brake hides the interface to the physical brake and has a state variable that indicates whether the brake is currently pressed. The event reporting operation press takes brake from Unpressed to Pressed and calls cruiseControl.brakePressed. The operation release takes the object back to state Unpressed. We assume that brake.press and brake.release are called by an interrupt handler. The Boolean state query brakeOff returns true if the brake is currently in the Unpressed state, and false otherwise. • The object speed hides the way the current speed is determined and provides the operation getSpeed. • The thread object throttle is an instance of a class that inherits from Thread (Appendix A). Its run method defines a periodic thread, which queries the state of cruising once per period by calling cruiseControl.getlever interrupts
getCrusingState getDesiredSpeed cruiseControl
throttle throttle adjustment
getSpeed
getSpeed
brakePressed
brakeOff brake interrupts brake
speed
speed input
Fig. 2. Communication diagram for the Cruise Controller in ELM. Note the symbol used to indicate that throttle is a thread object.
CruisingState. While in Cruising, it calls cruiseControl.getDesiredSpeed and speed.getSpeed, and adjusts the throttle to their difference. In Accelerating, it maintains constant acceleration. The software is structured with a single thread and a few objects. Estimating the concurrency level of the problem, it is clear that a throttle adjustment could co-occur with a lever movement and also with a braking event. But the lever and brake generate interrupts and do not require threads. If the input required sampling, there would be additional threads. The cruise controller is certainly a simple system and has a simple ELM solution. Instead of using the concurrent activities pattern, you can easily solve it with thread identification: • One thread of events consists of the periodic throttle adjustments while cruising is on. This gives rise to one control thread, throttle, in the software. • The other events have to do with the driverÕs inputs and occur considerably less frequently. Depending on the hardware solution in various cases, they can be interrupts, or you can have additional sampler threads. A more complex system has more concurrent activities, which furthermore may need access to shared resources.
7. COMET solution of the cruise controller We compare the ELM design of the cruise controller with a COMET design from (Gomaa, 2000). The original design also includes monitoring functions, which we exclude here. The requirements phase identifies a number of use cases. The analysis phase results in a class diagram and a statechart, similar to Fig. 1. The subsystem structuring design phase results in the communication diagram in Fig. 3. In addition to objects interfacing to external devices, Fig. 3 shows three internal objects: • CruiseControl, which encapsulates the state machine • Algorithms for acceleration and constant speed • DesiredSpeed, which stores the desired speed Next, the system as shown in Fig. 3 is structured into threads. The engine and brake related activities have similar characteristics, and are combined in the clustered periodic input thread BrkEngnInterface. All other devices are handled by their corresponding threads: • The asynchronous device interface thread LeverInterface handles the lever interrupts
ARTICLE IN PRESS B. Sande´n, J. Zalewski / The Journal of Systems and Software xxx (2005) xxx–xxx Lever input
Brake input
Engine Interface
Brake Interface
cruiseReq cruiseReq
DesiredSpeed
Read
Algorithms Throttle value
Current speed
Desired Speed
cruising controlCmd()
DSpeed Read
select() clear()
CruiseControl
Select/ Clear
Enable/ Disable
Throttle position
read(engInp)
BrakeEngnInterface
Lever Interface
On / Off
Pressed/ Released
CruiseControl
Throttle Interface
read(brkInp)
timeEvent
Lever Interface Accel/ Cruise/ Resume /Off
leverInterrupt()
Engine input
7
read(currSpeed) read(dS)
Read
Current speed
SpeedSensor Interface Speed Input
Throttle Interface
throttle Position
write (throttlevalue)
SpeedAdjust
read (currSpeed)
Speed SensorInterface
timeEvent
read(speedInp)
Fig. 4. Thread architecture for the Cruise Controller in COMET (adapted from Gomaa (2000)).
Fig. 3. Communication diagram for the Cruise Controller in COMET (adapted from Gomaa (2000)).
• The periodic SpeedSensorInterface thread supplies periodic readouts of the speed measuring device • The periodic output device interface thread ThrottleInterface actuates the throttle In addition, two data processing threads are introduced: • CruiseControl for state dependent control. It implements the state machine with the events being signaled by messages from other threads. • SpeedAdjust for executing the speed-maintaining algorithm. Loosely and tightly coupled message interfaces are designed next, and connector objects are introduced. The CruiseControl thread has a FIFO queue for event messages from LeverInterface, BrkEngineInterface and SpeedAdjust. All other interfaces are tightly coupled. Fig. 4 is the final thread architecture. (Connector objects are not shown.)
8. Discussion ELM differs from a method such as COMET both in terms of approach and solution. 8.1. Approach ELM goes straight for the heart of the problem, without unnecessary preliminaries. So, the behavior of the cruise controller is captured in a statechart as part of the requirements elicitation. When you elicit requirements from non-technical users, you normally capture use case flows as bulleted lists or in UML activity
diagrams (Fowler, 2004). This is also the approach in COMET. Those simple notations often capture the interactive aspects of a system well enough. But the cruise controller is not a typical interactive system with a few discrete use cases. Also, its requirements are probably specified by engineers who are used to abstraction. In that situation, a single statechart with its precise and expressive notation can describe several interdependent use case flows. In the cruise controller, all states and events are visible to the car driver, so the statechart captures the user interface. In other cases, statecharts describe the inner workings of the system and are constructed during analysis. In either case, you can apply the concurrent activities pattern, and the threads follow from the activities defined in the statechart. State diagramming also leads you naturally to classes related to the events, actions and conditions. Many control systems donÕt have complex class structures. Often, as in the cruise controller, there are a number of singleton objects. COMET starts by capturing requirements in the form of use cases, whose flows are later translated into statecharts in a separate step in the analysis phase. In the analysis, class diagramming precedes state diagramming. This is the conventional order for describing the different models in UML, but not an ideal approach to analyzing a system such as the cruise controller. The class diagram doesnÕt lead naturally to statecharts. You may not even identify those classes that need statecharts. By locking yourself into a somewhat arbitrary class diagram at an early stage, you may limit your options when it comes to more essential design decisions. A ‘‘high ceremony’’ process such as COMET with its many detailed steps gives the inexperienced a sense of security, but does not guarantee a satisfactory solution in a given case. Its long-windedness and complexity
ARTICLE IN PRESS 8
B. Sande´n, J. Zalewski / The Journal of Systems and Software xxx (2005) xxx–xxx
are themselves drawbacks. You may lose your way in the many steps and diagrams, and confuse different notations. If you arrive at threads through a tortuous process, the purpose of each one may not be clear. ELM gives the designer a feel for the possible thread architectures early on. This is possible since the threads are based on features of the problem domain. 8.2. Software solution The COMET solution consists of threads communicating via messages. For example, the thread BrkEngnInterface creates a message and enqueues it for CruiseControl. (The queue is implemented in a connector object.) After a context switch, the CruiseControl thread finds the message, performs any state transition and actions and may enqueue a message for the thread SpeedAdjust, which in turn may enqueue a message for ThrottleInterface. Unlike processes on single or distributed computers, threads operate in a common address space. So, they can communicate asynchronously via a variety of shared objects. ELM takes full advantage of this. A shared object can certainly be a connector as in COMET and contain a message queue, but this is rare with ELM because other solutions are usually more flexible and efficient. The synchronized object cruiseControl in the ELM solution replaces the thread CruiseControl and two message queues in the COMET solution. It works as follows: 1. An interrupt handler (or sampler thread) that detects an event occurrence calls an event-reporting operation on cruiseControl, which changes the state variable value and takes any necessary actions. 2. The activity thread throttle periodically queries the state and adjusts to the new state. This way, ELM avoids most message queues, which are error prone. A queue is a dynamic data structure of indefinite size. A tester must ensure that no queue can ever overflow. To guard against memory leakage, the tester must ensure that every message object is ultimately deallocated. In certain kinds of systems, message queues can even create a security risk: A malicious user may be able to flood the queue in order to stage a denialof-service attack. Clearly, the ELM solution (Fig. 2) contains fewer threads than the COMET solution (Fig. 4). Each thread incurs overhead in the form of stack space, etc., and more threads make the system less deterministic and harder to test. ELM uses the concurrency level of a problem as an indicator of the reasonable number of threads. The cruise controller is clearly manageable whether you have one thread or six, but were we to scale up the problem, the performance difference between,
say, 2000 and 12,000 threads would no doubt be significant.
9. Conclusion The idea that a software system should be modeled on some reality is not new and has been popularized by the object-oriented methods. ELM extends this idea to multithreading: A thread is no longer some pragmatic engineering device but models an event sequence in the problem domain. Many find the modeling idea elegant. As we have shown it also leads to certain advantages over methods such as COMET. • The concurrency level of a problem can be used as a guideline for the number of control threads in the solution. This means that an ELM solution tends to have fewer threads and be less complex than a COMET solution. • By basing the threads on the problem environment you arrive directly at a thread model. This makes for more agile development, which can be helpful as development cycles become more compressed. • ELM lets threads communicate via different types of shared objects, which makes for a more efficient and safer solution than message passing only. ELM is intended for designers reasonably familiar with concurrency, while COMET targets a different audience. If ELM is for the aspiring chef who wants to create a dish, COMET gives a cook a recipe to follow. It leads you to a solution step by step. You can use it without a deep understanding of multithreading. The price is an involved analysis and design process and a solution that may be far from the simplest and most efficient one. Some developers may well find the COMET approach so involved that itÕs barely usable. On the other hand, if your background is in dataflow threading, the transition to ELM is not subtle. Designers who are not steeped in earlier approaches may take more naturally to it.
Acknowledgment Dr. Anhtuan Dinh gave us the idea for this article. The anonymous reviewers provided many suggestions that made the text clearer.
Appendix A. Java threads and synchronized objects The Java threading model distinguishes between two kinds of entities, threads and synchronized objects (Gosling et al., 2005; Sande´n, 2004). Threads compete for the
ARTICLE IN PRESS B. Sande´n, J. Zalewski / The Journal of Systems and Software xxx (2005) xxx–xxx e4
W S
e1 / a1
e2 / a2
T
e3 [cond] / a5
entry/a3 exit/a4 do/c1
v sec.
U
Fig. 5. Simple statechart.
processors; each processor runs at most one thread at any one time.4 Java provides the abstract class Thread with the method run. A standard way to create threads is to declare a new class, B, say, that extends Thread and overrides run with appropriate processing. Each instance, bo, say, of B has its own thread, which executes BÕs run method and has access to boÕs data. An object that is accessed by more than one thread must be synchronized to guard against data inconsistency. Every Java object has a lock variable, which is not directly accessible to the programmer. Any Java method can be designated synchronized, meaning that it is bracketed implicitly by acquire and release operations on the lock variable. If a class has a number of synchronized methods, only one thread at a time can call one of those methods on a given instance of the class.
Appendix B. Statecharts Fig. 5 shows a statechart in the UML 2.0 notation (Fowler, 2004). It describes the behavior over time of some device. At each instant, the device exists in exactly one of the states S, T and U. The arrow with a bullet on its tail indicates that S is the initial state. The arrow from S to T shows that an event, e1, occurring in S, causes a transition to T. The notation e1/a1 shows that e1 also causes the action a1. If event e2 occurs in S, there is no state change but the action a2 is taken. An event is an external impulse and the action is the objectÕs reaction to it. Event e3 causes a transition from T to U if the condition, cond, shown in brackets, is met. v sec is a time event; after v seconds in state U, the device transitions to T. State T has entry and exit actions specified. The entry action (a3) is taken upon any transition to T no matter what causes it. When event e1 causes a transition from S to T, actions a1 and a3 are both taken. Similarly, the exit action (a4) is taken upon any transition from T. Events and actions are instantaneous. An operation that takes time is called an activity and continues throughout a state. T has the activity c1 as indicated
4 In symmetric multiprocessing, multiple processors have access to a common memory.
9
by the notation do/c1. The periodic adjustment of the throttle in a cruise controller is an example of an activity. A lengthy computation is also an activity. W is a superstate. It represents a group of states called substates, in this case T and U. A superstate is not an additional state. The device cannot exist in W without being in either T or U. Superstates come with certain notational conventions. The arrow marked e4 from WÕs border applies to each substate T and U. That is, if the device exists in T or U and event e4 happens it transitions to S. Superstates can overlap. This means that you can define a superstate for any group of states. You could, for example, enclose states T and S in a superstate X.
References Bollella, G., Gosling, J., 2000. The real-time specification for Java. IEEE Computer 33 (6), 47–54. Burns, A., Wellings, A., 1998. Concurrency in Ada. Cambridge University Press. Fowler, M., 2004. UML Distilled, third ed. Addison-Wesley. Gamma, E., Helm, R., Johnson, R., Vlissides, J., 1995. Design Patterns. Elements of Reusable Object-oriented Software. Addison-Wesley. Gomaa, H., 2000. Designing Concurrent, Distributed, and Real-time Applications with UML. Addison-Wesley. Gosling, J., Joy, B., Steele, G., Bracha, G., 2005. JavaTM Language Specification, third ed. Addison-Wesley. Harel, D., Pnueli, A., 1985. On the development of reactive systems. In: Apt, K.R. (Ed.), Logics and Models of Concurrent Systems. Springer, pp. 477–498. Klein, M.H. et al., 1993. A PractitionerÕs Handbook for Real-Time Analysis: Guide to Rate Monotonic Analysis for Real-Time Systems, Kluwer Academic Publishers. Sande´n, B.I., 1997. Modeling concurrent software. IEEE Software 14 (5), 93–100. Sande´n, B.I., 2001. A design pattern for state machines and concurrent activities. In: Dirk Craeynest, D., Strohmeier, A. (Eds.), Proc. 6th International Conference on Reliable Software Technologies-Ada-Europe 2001, Leuven, Belgium, May 14–18, 2001, Lecture Notes in Computer Science, vol. 2043, Springer-Verlag, pp. 203–214. Sande´n, B.I., 2003. Entity-life modeling: modeling a thread architecture on the problem environment. IEEE Software 20 (3), 70–78. Sande´n, B.I., 2004. Coping with Java threads. IEEE Computer 37 (4), 20–27. Sanz, R., Zalewski, J., 2003. Control systems engineering using design patterns. IEEE Control Systems 23 (3), 43–60. Bo I. Sande´n is a Professor of Computer Science at Colorado Technical University. He received an M.S. in Engineering Physics from Lund Institute of Technology in 1970 and a Ph.D. in Computer Science from the Royal Institute of Technology in Stockholm in 1978. From 1971 to 1986 he held positions as a software developer and project manager with UNIVAC and Philips Electronics in Sweden and gained rich experience in the design of multithreaded software. He was a visiting Associate Professor at the Wang Institute in Tyngsboro, MA, 1986–1987 and an Associate Professor of Software Engineering at George Mason University in Fairfax, VA, 1987–1996.
ARTICLE IN PRESS 10
B. Sande´n, J. Zalewski / The Journal of Systems and Software xxx (2005) xxx–xxx
In 1996 he joined Colorado Technical University in Colorado Springs, where he teaches at the undergraduate, graduate and doctoral levels. His research and teaching interests include software engineering, especially the design of multithreaded software, object-oriented analysis and design, and state diagramming. He is the author of two books on software design and numerous articles and serves as a reviewer for various journals and conferences. Home page: http://home.earthlink.net/~bosanden Janusz Zalewski is an Associate Professor of Computer Science at Florida Gulf Coast University. Previously he has been on the faculty in Software Engineering programs at the University of Central Florida in Orlando, and at Embry-Riddle Aeronautical University in Daytona Beach. Before taking a university position, he worked for various nuclear research institutions, including the Superconducting Super
Collider Lab in Dallas, Texas, where he was a member of the Data Acquisition Group, and Lawrence Livermore National Laboratory in Livermore, California, where he worked for the Computer Safety and Reliability Center. He also worked on projects and consulted for a number of private companies including Lockheed Martin, Harris and Boeing. He received an M.Sc. in electronic engineering and a Ph.D. in computer science from Warsaw University of Technology, Poland, in 1973 and 1979, respectively. He was a Chairman of IFIP Working Group 5.4 on Industrial Software Quality and of an IFAC Technical Committee on Safety of Computer Control Systems. He serves on editorial boards of Annual Reviews in Control and Parallel and Distributed Computing Practices. His major areas of research interests include: safety-related computer systems, real-time multiprocessor and distributed systems, and software engineering education. Home page: http://www.fgcu.edu/zalewski/