460 likes | 598 Views
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. Outline of course topics.
E N D
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
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
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
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
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
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
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
Example Client ... /send S.mCall(this, ...) mRet(...) Waiting Supplier mCall(caller, ...) ... /send caller.mRet(...) Processing Body of M CSE 335: Software Design
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
Example: Client model Client / send q.pullCall(this) Initializing Waiting pullReturn(b,s) do / processString(b,s) CSE 335: Software Design
Example: Shared queue Queue ProcessingPullCall Idle pullCall(caller) [!q.empty()] Checking [q.empty()] / send caller.pullRet(false,empty) Empty / send caller.pullRet(true,s) NotEmpty do / s := q.back CSE 335: Software Design
c1 : Client c2 : Client Recall: Two active clients sharing a queue q q q : Queue CSE 335: Software Design
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
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
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
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
c1’s pool: c2’s pool: Client Client / send q.pullCall(this) / send q.pullCall(this) Initializing Waiting Initializing Waiting pullReturn(b,s) pullReturn(b,s) do / processString(b,s) do / processString(b,s) Queue q’s pool: ProcessingPullCall pullCall(caller) [!q.empty()] Idle Checking [q.empty()] / send caller.pullRet(false,empty) Empty / send caller.pullRet(true,s) NotEmpty do / s := q.back CSE 335: Software Design
c1’s pool: c2’s pool: Client Client / send q.pullCall(this) / send q.pullCall(this) Initializing Waiting Initializing Waiting pullReturn(b,s) pullReturn(b,s) do / processString(b,s) do / processString(b,s) Queue q’s pool: ProcessingPullCall pullCall(caller) [!q.empty()] Idle Checking [q.empty()] / send caller.pullRet(false,empty) Empty / send caller.pullRet(true,s) NotEmpty do / s := q.back CSE 335: Software Design
c1’s pool: c2’s pool: Client Client / send q.pullCall(this) / send q.pullCall(this) Initializing Waiting Initializing Waiting pullReturn(b,s) pullReturn(b,s) do / processString(b,s) do / processString(b,s) Queue q’s pool: ProcessingPullCall pullCall(caller) [!q.empty()] Idle Checking [q.empty()] / send caller.pullRet(false,empty) Empty / send caller.pullRet(true,s) NotEmpty do / s := q.back CSE 335: Software Design
c1’s pool: c2’s pool: Client Client / send q.pullCall(this) / send q.pullCall(this) Initializing Waiting Initializing Waiting pullReturn(b,s) pullReturn(b,s) do / processString(b,s) do / processString(b,s) Queue q’s pool: pullCall(c1) ProcessingPullCall pullCall(caller) [!q.empty()] Idle Checking [q.empty()] / send caller.pullRet(false,empty) Empty / send caller.pullRet(true,s) NotEmpty do / s := q.back CSE 335: Software Design
c1’s pool: c2’s pool: Client Client / send q.pullCall(this) / send q.pullCall(this) Initializing Waiting Initializing Waiting pullReturn(b,s) pullReturn(b,s) do / processString(b,s) do / processString(b,s) Queue q’s pool: ProcessingPullCall pullCall(caller) [!q.empty()] Idle Checking [q.empty()] / send caller.pullRet(false,empty) Empty / send caller.pullRet(true,s) NotEmpty do / s := q.back CSE 335: Software Design
c1’s pool: c2’s pool: Client Client / send q.pullCall(this) / send q.pullCall(this) Initializing Waiting Initializing Waiting pullReturn(b,s) pullReturn(b,s) do / processString(b,s) do / processString(b,s) Queue q’s pool: pullCall(c2) ProcessingPullCall pullCall(caller) [!q.empty()] Idle Checking [q.empty()] / send caller.pullRet(false,empty) Empty / send caller.pullRet(true,s) NotEmpty do / s := q.back CSE 335: Software Design
c1’s pool: c2’s pool: Client Client / send q.pullCall(this) / send q.pullCall(this) Initializing Waiting Initializing Waiting pullReturn(b,s) pullReturn(b,s) do / processString(b,s) do / processString(b,s) Queue q’s pool: pullCall(c2) ProcessingPullCall pullCall(caller) [!q.empty()] Idle Checking [q.empty()] / send caller.pullRet(false,empty) Empty / send caller.pullRet(true,s) NotEmpty do / s := q.back CSE 335: Software Design
c1’s pool: pullRet(false, empty) c2’s pool: Client Client / send q.pullCall(this) / send q.pullCall(this) Initializing Waiting Initializing Waiting pullReturn(b,s) pullReturn(b,s) do / processString(b,s) do / processString(b,s) Queue q’s pool: pullCall(c2) ProcessingPullCall pullCall(caller) [!q.empty()] Idle Checking [q.empty()] / send caller.pullRet(false,empty) Empty / send caller.pullRet(true,s) NotEmpty do / s := q.back CSE 335: Software Design
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
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
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
Example c1 : … q :Queue c2 : … pull empty pull empty back pop back pop CSE 335: Software Design
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
ProcessingPullCall ProcessingPullCall pullCall(caller) pullCall(caller) [!q.empty()] [!q.empty()] Idle Idle Checking Checking [q.empty()] [q.empty()] Empty Empty / send caller.pullRet(…) / send caller.pullRet(…) NotEmpty do / s := q.back NotEmpty do / s := q.back / send caller.pullRet(true,s) / send caller.pullRet(true,s) Example: Shared queue CSE 335: Software Design
ProcessingPullCall ProcessingPullCall pullCall(caller) pullCall(caller) [!q.empty()] [!q.empty()] Idle Checking Idle Checking [q.empty()] [q.empty()] Empty Empty / send caller.pullRet(…) / send caller.pullRet(…) NotEmpty do / s := q.back NotEmpty do / s := q.back / send caller.pullRet(true,s) / send caller.pullRet(true,s) Initial state of shared queue CSE 335: Software Design
ProcessingPullCall ProcessingPullCall pullCall(caller) pullCall(caller) [!q.empty()] [!q.empty()] Idle Checking Idle Checking [q.empty()] [q.empty()] Empty Empty / send caller.pullRet(…) / send caller.pullRet(…) NotEmpty do / s := q.back NotEmpty do / s := q.back / send caller.pullRet(true,s) / send caller.pullRet(true,s) Client c1 invokes pull… CSE 335: Software Design
ProcessingPullCall pullCall(caller) [!q.empty()] Idle Checking [q.empty()] Empty / send caller.pullRet(…) NotEmpty do / s := q.back / send caller.pullRet(true,s) Queue is not empty… ProcessingPullCall pullCall(caller) [!q.empty()] Idle Checking [q.empty()] Empty / send caller.pullRet(…) NotEmpty do / s := q.back / send caller.pullRet(true,s) CSE 335: Software Design
ProcessingPullCall ProcessingPullCall pullCall(caller) pullCall(caller) [!q.empty()] [!q.empty()] Idle Checking Idle Checking [q.empty()] [q.empty()] Empty Empty / send caller.pullRet(…) / send caller.pullRet(…) NotEmpty do / s := q.back NotEmpty do / s := q.back / send caller.pullRet(true,s) / send caller.pullRet(true,s) At this point, context switch and client c2 invokes pull… CSE 335: Software Design
ProcessingPullCall ProcessingPullCall pullCall(caller) pullCall(caller) [!q.empty()] [!q.empty()] Idle Checking Idle Checking [q.empty()] [q.empty()] Empty Empty / send caller.pullRet(…) / send caller.pullRet(…) NotEmpty do / s := q.back NotEmpty do / s := q.back / send caller.pullRet(true,s) / send caller.pullRet(true,s) C1 has yet to pop queue; so c2’s check for empty fails CSE 335: Software Design
ProcessingPullCall ProcessingPullCall pullCall(caller) pullCall(caller) [!q.empty()] [!q.empty()] Idle Checking Idle Checking [q.empty()] [q.empty()] Empty Empty / send caller.pullRet(…) / send caller.pullRet(…) NotEmpty do / s := q.back NotEmpty do / s := q.back / send caller.pullRet(true,s) / send caller.pullRet(true,s) Bad state! If queue contained only one element… CSE 335: Software Design
Question Assuming this interpretation of passive objects, how would we model the promotion of shared queue to a monitor? CSE 335: Software Design
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
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
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
Question What does it mean to transition out of a concurrent composite state? CSE 335: Software Design
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
Example: Bridge game PlayingRubber Not Vulnerable N-S wins rubber ns-game ns-game Vulnerable Not Vulnerable E-W wins rubber ew-game ew-game Vulnerable Note: Transition out of PlayingRubber by one concurrent activity terminates the other CSE 335: Software Design
Example: Cash dispenser Emitting do/ dispenseCash ready SetUp Complete do/ ejectCard Note: Will not transition out of Emitting until after completion of all concurrent activities CSE 335: Software Design
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
Example: Concurrent composite state Automobile Temperature control TempOn pushAir Cooling pushTCOff TempOff pushHeat pushAir Heating pushHeat Rear defroster Radio control pushRD pushRad RDOff RDOn RadOff RadOn pushRD pushRad CSE 335: Software Design