State Modeling, part II

advertisement
Software Architecture and Larger
System Design Issues
Lecture 6: Advanced state modeling/analysis
Topics:
– Modeling/analyzing concurrent behaviors using UML
state diagrams
– Chapter 6 in Blaha and Rumbaugh
– Additional topics not in the Blaha/Rumbaugh book
CSE 335: Software Design
E. Kraemer
Outline of course topics
Foundational OO concepts
Synthetic concepts
Software architecture and larger design issues:
– Strategic design decisions that influence a host of smaller, more tactical
design decisions
• E.g., policy for persistence of data in long-running system
• E.g., allocating functionality to a single, centralized system vs. distributing
functionality among a collection of communicating hosts
–
–
–
–
Often involve a major capital investment
Source of both risk and opportunity
Require lots of a priori modeling and analysis
Focus: Design issues related to concurrent and distributed systems
Software process issues
CSE 335: Software Design
E. Kraemer
Analytical models of behavior
Thus far, the models we have employed have proved
useful for
– documentation/explanation
– “roughing out” a design prior to implementation
Still, they are not very rigorous:
– E.g., sequence diagrams depict only one scenario of
interaction among objects
– Not good for reasoning about space of possible behaviors
Such reasoning requires more formal and complete
models of behavior
CSE 335: Software Design
E. Kraemer
State diagrams
Useful for modeling “space of behaviors” of an
object or a system of interacting objects
Requires:
– Identifying and naming the conceptual “states” that an
object might be in, and
– Conceivable transitions among those states and the
events (and/or conditions) that trigger (and/or guard)
these transitions
Concurrent compositions of state diagrams can be
“executed” to expose anomalous behaviors
CSE 335: Software Design
E. Kraemer
Communication among concurrent
state machines
More interesting applications involve interaction
(explicit communication) among state machines
Examples:
– Active client objects interacting with a shared queue
– Sensor object notifying a software controller
– Perhaps even an object invoking a method on
another
UML provides send actions by which one machine
may signal another
CSE 335: Software Design
E. Kraemer
Simple example: Client using a queue
c1 : Client
q : Queue
System comprises two objects
Each object modeled by a state machine
System state at any time is the pair (state of c1, state of q)
CSE 335: Software Design
E. Kraemer
Modeling method invocations
Given state machines for two objects C and S,
where C is the client and S the supplier of a
method m
Model call and return of m as separate signals
C sends the call signal to S and then enters a
waiting state, which is exited upon reception of a
return signal from S
CSE 335: Software Design
E. Kraemer
Example
Client
... / send S.mCall(this, ...)
Waiting
mRet(...)
Supplier
mCall(caller, ...)
CSE 335: Software Design
Processing
Body of M
... / send caller.mRet(...)
E. Kraemer
Exercise
Develop state models for a simple client and a
queue, assuming:
– class Client does nothing but repeatedly pull items
off of the queue
– class Queue provides an operation pull, which is
implemented by calling empty, back, and pop on
a private data member of type queue<string>
CSE 335: Software Design
E. Kraemer
Example: Client model
Client
Initializing
/ send q.pullCall(this)
Waiting
pullReturn(b,s)
do / processString(b,s)
CSE 335: Software Design
E. Kraemer
Example: Shared queue
Queue
ProcessingPullCall
Idle
pullCall(caller)
Checking
[!q.empty()]
[q.empty()]
/ send caller.pullRet(false,empty)
/ send caller.pullRet(true,s)
CSE 335: Software Design
Empty
NotEmpty
do / s := q.back
E. Kraemer
Recall: Two active clients sharing
a queue
c1 : Client
c2 : Client
q
q
q : Queue
CSE 335: Software Design
E. Kraemer
Question
Do our state diagrams for classes Client and
Queue accurately model the behaviors of
active clients acting on a shared queue?
CSE 335: Software Design
E. Kraemer
Question
Do our state diagrams for classes Client and
Queue accurately model the behaviors of
active clients acting on a shared queue?
Answer really depends upon the “semantics” of
event handling among concurrent state
machines
CSE 335: Software Design
E. Kraemer
Semantics of parallel composition
Multiple interpretations:
– Concurrent regions execute independently
• What happens if transitions in different regions are triggered by
same event?
• Do both execute simultaneously? Does one “consume” the
event to the exclusion of the other? Does each get a separate
“copy” of the event?
– Concurrent regions communicate with one another,
synchronizing on common events
• Regions can only proceed when all are ready to proceed
• Regions transfer data values during a concurrent transition
– Do we distinguish internal and external events?
CSE 335: Software Design
E. Kraemer
UML 2.0 Interpretation: Asynchronous
events run to completion
Run-to-completion semantics:
– State machine processes one event at a time and
finishes all consequences of that event before
processing another event
– Events do not interact with one another during
processing
Event pool:
– Where new events for an object are stored until object
is ready to process them
– No event ordering assumed in the pool
CSE 335: Software Design
E. Kraemer
c1’s pool:
c2’s pool:
Client
Client
/ send q.pullCall(this)
Initializing
/ send q.pullCall(this)
Waiting
Initializing
pullReturn(b,s)
pullReturn(b,s)
do / processString(b,s)
q’s pool:
Waiting
do / processString(b,s)
Queue
ProcessingPullCall
pullCall(caller)
Idle
Checking
[!q.empty()]
[q.empty()]
/ send caller.pullRet(false,empty)
Empty
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
E. Kraemer
c1’s pool:
c2’s pool:
Client
Client
/ send q.pullCall(this)
Initializing
/ send q.pullCall(this)
Waiting
Initializing
pullReturn(b,s)
pullReturn(b,s)
do / processString(b,s)
q’s pool:
Waiting
do / processString(b,s)
Queue
ProcessingPullCall
pullCall(caller)
Idle
Checking
[!q.empty()]
[q.empty()]
/ send caller.pullRet(false,empty)
Empty
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
E. Kraemer
c1’s pool:
c2’s pool:
Client
Client
/ send q.pullCall(this)
Initializing
/ send q.pullCall(this)
Waiting
Initializing
pullReturn(b,s)
pullReturn(b,s)
do / processString(b,s)
q’s pool:
Waiting
do / processString(b,s)
Queue
ProcessingPullCall
pullCall(caller)
Idle
Checking
[!q.empty()]
[q.empty()]
/ send caller.pullRet(false,empty)
Empty
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
E. Kraemer
c1’s pool:
c2’s pool:
Client
Client
/ send q.pullCall(this)
Initializing
/ send q.pullCall(this)
Waiting
Initializing
pullReturn(b,s)
pullReturn(b,s)
do / processString(b,s)
q’s pool:
pullCall(c1)
Waiting
do / processString(b,s)
Queue
ProcessingPullCall
pullCall(caller)
Idle
Checking
[!q.empty()]
[q.empty()]
/ send caller.pullRet(false,empty)
Empty
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
E. Kraemer
c1’s pool:
c2’s pool:
Client
Client
/ send q.pullCall(this)
Initializing
/ send q.pullCall(this)
Waiting
Initializing
pullReturn(b,s)
pullReturn(b,s)
do / processString(b,s)
q’s pool:
Waiting
do / processString(b,s)
Queue
ProcessingPullCall
pullCall(caller)
Idle
Checking
[!q.empty()]
[q.empty()]
/ send caller.pullRet(false,empty)
Empty
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
E. Kraemer
c1’s pool:
c2’s pool:
Client
Client
/ send q.pullCall(this)
Initializing
/ send q.pullCall(this)
Waiting
Initializing
pullReturn(b,s)
pullReturn(b,s)
do / processString(b,s)
q’s pool:
pullCall(c2)
Waiting
do / processString(b,s)
Queue
ProcessingPullCall
pullCall(caller)
Idle
Checking
[!q.empty()]
[q.empty()]
/ send caller.pullRet(false,empty)
Empty
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
E. Kraemer
c1’s pool:
c2’s pool:
Client
Client
/ send q.pullCall(this)
Initializing
/ send q.pullCall(this)
Waiting
Initializing
pullReturn(b,s)
pullReturn(b,s)
do / processString(b,s)
q’s pool:
pullCall(c2)
Waiting
do / processString(b,s)
Queue
ProcessingPullCall
pullCall(caller)
Idle
Checking
[!q.empty()]
[q.empty()]
/ send caller.pullRet(false,empty)
Empty
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
E. Kraemer
c1’s pool:
c2’s pool:
pullRet(false, empty)
Client
Client
/ send q.pullCall(this)
Initializing
/ send q.pullCall(this)
Waiting
Initializing
pullReturn(b,s)
pullReturn(b,s)
do / processString(b,s)
q’s pool:
pullCall(c2)
Waiting
do / processString(b,s)
Queue
ProcessingPullCall
pullCall(caller)
Idle
Checking
[!q.empty()]
[q.empty()]
/ send caller.pullRet(false,empty)
Empty
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
E. Kraemer
Observations
Modeling method invocations as asynchronous
events which are placed in a pool:
– Requests for service on an object:
• “buffered up” on arrival
• dispatched when the object is ready to handle them
– Natural interpretation for how an active object can
be invoked
– Makes passive objects appear to execute with
“monitor semantics”
CSE 335: Software Design
E. Kraemer
Observations (continued)
In real programs, not every passive object is (or
should be) a monitor:
– There is some expense associated with acquiring
more locks than are needed to synchronize threads
– We often want to analyze a system to choose which
passive objects should be monitors.
How could we use state-machine models for this
purpose?
CSE 335: Software Design
E. Kraemer
More precisely…
How could we model the shared queue as a
state machine that admits the behaviors on
the following slide?
CSE 335: Software Design
E. Kraemer
Example
c1 : …
q :Queue
c2 : …
pull
empty
pull
empty
back
pop
back
pop
CSE 335: Software Design
E. Kraemer
Answer
Duplicate an object’s state model with one copy
for each system thread
Note: This will need to be done for each passive
object, and it will potentially cause the
number of states in the system to grow out of
control
CSE 335: Software Design
E. Kraemer
Example: Shared queue
pullCall(caller)
Idle
ProcessingPullCall
pullCall(caller)
[!q.empty()]
Checking
Idle
[q.empty()]
[!q.empty()]
Checking
[q.empty()]
Empty
/ send caller.pullRet(…)
ProcessingPullCall
Empty
/ send caller.pullRet(…)
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
E. Kraemer
Initial state of shared queue
pullCall(caller)
Idle
ProcessingPullCall
pullCall(caller)
[!q.empty()]
Checking
Idle
[q.empty()]
[!q.empty()]
Checking
[q.empty()]
Empty
/ send caller.pullRet(…)
ProcessingPullCall
Empty
/ send caller.pullRet(…)
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
E. Kraemer
Client c1 invokes pull…
pullCall(caller)
Idle
ProcessingPullCall
pullCall(caller)
[!q.empty()]
Checking
Idle
[q.empty()]
[!q.empty()]
Checking
[q.empty()]
Empty
/ send caller.pullRet(…)
ProcessingPullCall
Empty
/ send caller.pullRet(…)
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
E. Kraemer
Queue is not empty…
pullCall(caller)
Idle
ProcessingPullCall
pullCall(caller)
[!q.empty()]
Checking
Idle
[q.empty()]
[!q.empty()]
Checking
[q.empty()]
Empty
/ send caller.pullRet(…)
ProcessingPullCall
Empty
/ send caller.pullRet(…)
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
E. Kraemer
At this point, context switch
and client c2 invokes pull…
pullCall(caller)
Idle
ProcessingPullCall
pullCall(caller)
[!q.empty()]
Checking
Idle
[q.empty()]
[!q.empty()]
Checking
[q.empty()]
Empty
/ send caller.pullRet(…)
ProcessingPullCall
Empty
/ send caller.pullRet(…)
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
E. Kraemer
C1 has yet to pop queue; so
c2’s check for empty fails
pullCall(caller)
Idle
ProcessingPullCall
pullCall(caller)
[!q.empty()]
Checking
Idle
[q.empty()]
[!q.empty()]
Checking
[q.empty()]
Empty
/ send caller.pullRet(…)
ProcessingPullCall
Empty
/ send caller.pullRet(…)
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
E. Kraemer
Bad state! If queue contained
only one element…
pullCall(caller)
Idle
ProcessingPullCall
pullCall(caller)
[!q.empty()]
Checking
Idle
[q.empty()]
[!q.empty()]
Checking
[q.empty()]
Empty
/ send caller.pullRet(…)
ProcessingPullCall
Empty
/ send caller.pullRet(…)
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
CSE 335: Software Design
NotEmpty
do / s := q.back
/ send caller.pullRet(true,s)
E. Kraemer
Question
Assuming this interpretation of passive objects,
how would we model the promotion of shared
queue to a monitor?
CSE 335: Software Design
E. Kraemer
Question
Assuming this interpretation of passive objects,
how would we model the promotion of shared
queue to a monitor?
Answer: Two ways
– Model the lock explicitly as another state machine
– Use only a single state machine model for queue
rather than replicating per thread
CSE 335: Software Design
E. Kraemer
Model checking
Technique for exhaustively and automatically
analyzing finite-state models to find “bad
states”
– Bad states are specified in a temporal logic or
some other declarative notation
Lots of tools that can be used for this purpose:
– FSP, SMV, Spin, etc
If you are designing concurrent software, want
to learn how to use these tools
CSE 335: Software Design
E. Kraemer
Wrap-up: Use of models
In this course, we have now used models for
many purposes:
– Documentation and demonstration of
characteristics of a complex design
– Means for understanding the requirements of a
system to be built
– Analysis for tricky concurrency properties
CSE 335: Software Design
E. Kraemer
Question
What does it mean to transition out of a
concurrent composite state?
CSE 335: Software Design
E. Kraemer
Question
What does it mean to transition out of a
concurrent composite state?
Two possible answers:
– transition awaits completion of all concurrent
activities
– transition acts immediately, terminating all
concurrent activities
CSE 335: Software Design
E. Kraemer
Example: Bridge game
PlayingRubber
ns-game
Not
Vulnerable
Vulnerable
ns-game
N-S wins
rubber
ew-game
Not
Vulnerable
Vulnerable
ew-game
E-W wins
rubber
Note: Transition out of PlayingRubber by one concurrent
activity terminates the other
CSE 335: Software Design
E. Kraemer
Example: Cash dispenser
Emitting
SetUp
ready
do/ dispenseCash
Complete
do/ ejectCard
Note: Will not transition out of Emitting until after
completion of all concurrent activities
CSE 335: Software Design
E. Kraemer
Recall: State explosion problem
Number of states in a system with multiple, orthogonal,
behaviors grows exponentially (product of number of
states of each feature)
Major impediment to understanding:
– Impossible to visualize in any meaningful way
– Requires the use of analysis tools to verify properties
Managing state explosion:
– Concurrent state machines
• Each object in a system modeled as a state machine
• Object state machine runs concurrently with those of other objects
– Concurrent composite states
• Separates one machine can into orthogonal submachines
CSE 335: Software Design
E. Kraemer
Example: Concurrent composite state
Automobile
Temperature control
TempOn
pushAir
Cooling
TempOff
pushTCOff
pushHeat
Rear defroster
pushHeat
pushAir
Heating
Radio control
pushRD
RDOff
pushRad
RDOn
pushRD
CSE 335: Software Design
RadOff
RadOn
pushRad
E. Kraemer
Download