Control Flow


In C#, a {} statement block is a container for a set of program statements. When the statement block executes, the contained program statements execute sequentially until all are complete.

We can further decompose the logic of the OpenSesame class into PrintKey and PrintGreeting program statements that correspond to the other C# program statements in the original Open, Sesame console program. Doing this allows us to order these resumable program statements, along with Read, within a general-purpose program statement block that functions like a resumable version of a C# statement block.

Here are the PrintKey and PrintGreeting program statements:

 [Serializable] public class PrintKey : ProgramStatement {   private string key;   public string Key   {     get { return key; }   }   public override void Run(BookmarkManager mgr)   {     // Print the key     key = DateTime.Now.Millisecond.ToString();     Console.WriteLine("here is your key: " + key);     mgr.Done();   } } [Serializable] public class PrintGreeting : ProgramStatement {   private string key;   public string Key   {     get { return key; }     set { key = value; }   }   private string s;   public string Input   {     get { return s; }     set { s = value; }   }   public override void Run(BookmarkManager mgr)   {     // Print the greeting if the key is provided     if (key.Equals(s))       Console.WriteLine("hello, world");     mgr.Done();   } } 


After each statement is captured as a ProgramStatement, we are ready to implement our resumable ProgramStatementBlock, which simply executes a set of contained program statements one by one, in sequence:

 [Serializable] public class ProgramStatementBlock : ProgramStatement {   int currentIndex;   List<ProgramStatement> statements = new List<ProgramStatement>();   public IList<ProgramStatement> Statements   {     get { return statements; }   }   public override void Run(BookmarkManager mgr)   {     currentIndex = 0;     // Empty statement block     if (statements.Count == 0)       mgr.Done();     else       mgr.RunProgramStatement(statements[0], ContinueAt);   }   public void ContinueAt(Bookmark resumed)   {      BookmarkManager mgr = resumed.BookmarkManager;      // If we've run all the statements, we're done      if (++currentIndex == statements.Count)        mgr.Done();      else // Else, run the next statement        mgr.RunProgramStatement(statements[currentIndex], ContinueAt);   } } 


We can provide any list of ProgramStatement objects to ProgramStatementBlock and, just like in a C# statement block, those statements will be executed one by one, sequentially. The only difference, of course, is that ProgramStatementBlock uses bookmarks, which means that we now have a resumable program statement block with thread and process agility.

A program executed by the mythical runtime is just a ProgramStatement object. This means we can now build Open, Sesame like this:[5]

[5] Expressions and property databinding are outside the scope of Chapter 1.

 ProgramStatementBlock openSesameProgram =   new ProgramStatementBlock(); PrintKey printKey = new PrintKey(); Read read = new Read(); PrintGreeting printGreeting = new PrintGreeting(); printGreeting.Key = ... // bind to printKey.Key printGreeting.Input = ... // bind to read.Text openSesameProgram.Statements.Add(printKey); openSesameProgram.Statements.Add(read); openSesameProgram.Statements.Add(printGreeting); MythicalRuntime runtime = new MythicalRuntime(); ProgramHandle handle = runtime.RunProgram(openSesameProgram); ... 


That's it! The Open, Sesame program, as presented to the mythical runtime, is a now a hierarchy of program statementsa ProgramStatementBlock object that is configured with an appropriate list of contained program statements.

Composite Program Statements

ProgramStatementBlock is a general-purpose program statement used for control flow within a resumable program. Languages like C# include other control flow constructs (such as if, switch, for, while, and foreach) that are indispensable if we want to write programs that are more complicated than Open, Sesame.

Let's see how easy it is to implement other control flow constructs as resumable program statements. As a first step, we can quickly establish a base class for these composite program statements (program statements that contain other program statements):

 public abstract class CompositeProgramStatement : ProgramStatement {   public IList<ProgramStatement> Statements { ... } } 


After we have the base class, we change ProgramStatementBlock to derive from it:

 public class ProgramStatementBlock : CompositeProgramStatement {   // Same as earlier except that the Statements property   // is now inherited from CompositeProgramStatement } 


The CompositeProgramStatement class derives from ProgramStatement and defines an IList<ProgramStatement> property that holds the set of program statements contained by the composite program statement. The set of contained program statements will have their execution managed by the CompositeProgramStatement.

We can now easily develop familiar control flow constructs. Here is a composite program statement that provides branching (we are assuming the existence of a BooleanExpression type that defines an Evaluate method returning a Boolean value):

 public class IfElse : CompositeProgramStatement {   // Property that represents a boolean expression   BooleanExpression Expression { ... }   // Run either the first or second contained program   // statement (more than two should be disallowed)   public override void Run(BookmarkManager mgr)   {     if (Expression.Evaluate())        mgr.RunProgramStatement(Statements[0], ContinueAt);     else if (Statements[1] != null)        mgr.RunProgramStatement(Statements[1], ContinueAt);   }   public void ContinueAt(Bookmark resumed)   {     resumed.BookmarkManager.Done();   } } 


Here is another composite program statement that provides looping:

 public class While : CompositeProgramStatement {   // Property that represents a boolean expression   BooleanExpression Expression { ... }   // Run the contained program statement (more than one   // should be disallowed) as long as the expression   // evaluates to true   public override void Run(BookmarkManager mgr)   {     if (Expression.Evaluate())        mgr.RunProgramStatement(Statements[0], ContinueAt);     else       mgr.Done();   }   public void ContinueAt(Bookmark resumed)   {     BookmarkManager mgr = resumed.BookmarkManager;     // Evaluate boolean expression     if (Expression.Evaluate())        mgr.RunProgramStatement(Statements[0], ContinueAt);     else        mgr.Done();   } } 


Just like that, we have resumable branching and looping constructs that we can use in the resumable programs we build.

Control Flow Robustness

Our ProgramStatementBlock and other composite program statements that provide control flow do work, but we are playing a bit fast and loose with our implementation guarantees.

To really make ProgramStatementBlock correct, we need an assurance that its contained statements execute in the proper order. To be fair, the code of ProgramStatementBlock is doing nothing wrong. But what is stopping some other code that might get a reference to a Read (contained by ProgramStatementBlock) from calling its Run method? And what is stopping a Read (or any other program statement contained by a ProgramStatementBlock) from continuing to do work even after signaling that it is done?

We need the following guarantees:

  • A program statement is run only by the (parent) composite program statement that contains it (in its CompositeProgramStatement.Statements collection).

  • A program statement cannot do more work (has no pending bookmarks) after it signals that it is done.

Another way of stating these requirements is in terms of the automaton introduced earlier to describe the lifecycle of reactive program statements:

  • Only the parent of a program statement can move that program statement from the Latent state into the Running state.

  • The parent of a program statement needs to know when that program statement moves from the Running state into the Done state.

  • After a program statement moves into the Done state, it cannot do any more work (it cannot have pending bookmarks).

As we now see, the automaton applies not only to the reactive program as a whole, but to all individual program statements within that program. Furthermore, the automaton is the basis for establishing a set of constraints on program statement execution, upon which the semantics of composite program statements are built.

The bookmark manager can satisfy the requirements listed previously by throwing exceptions when the rules are violated, as shown in the following code snippets:

 BookmarkManager mgr = resumed.BookmarkManager; // obtain a reference to a program statement that is not // a member of the this.Statements collection ProgramStatement statement = ... // will throw an exception mgr.RunProgramStatement(statement, ContinueAt); ... // will throw an exception if there are pending bookmarks mgr.Done(); ... // will throw an exception if Done has already been called mgr.Add(new Bookmark(...)) 


To further enforce the desired restrictions, the accessibility of the Run method defined by ProgramStatement can be changed from public to protected internal. In this way, only the bookmark manager will be able to invoke the Run method (when requested to do so via BookmarkManager.RunProgramStatement).

These changes give our ProgramStatementBlock, and our other composite program statements, the robustness we expect from resumable versions of the tried and true C# { }, if, and while constructs.

Real-World Control Flow

Given the programming model for bookmarks, it is easy to go beyond the familiar control flow constructs of C#. This will allow us to develop composite program statements that help solve complex modeling problems.

The Interleave class is a composite program statement that runs its contained program statements simultaneously:

 [Serializable] public class Interleave : CompositeProgramStatement {   int numChildStatementsCompleted;   public override void Run(BookmarkManager mgr)   {     numChildStatementsCompleted = 0;     // empty     if (Statements.Count == 0)       mgr.Done();     else     {       foreach (ProgramStatement ps in Statements)         mgr.RunProgramStatement(ps, ContinueAt);     }   }   public void ContinueAt(Bookmark resumed)   {     numChildStatementsCompleted++;     BookmarkManager mgr = resumed.BookmarkManager;     if (Statements.Count == numChildStatementsCompleted)       mgr.Done();   } } 


A program that contains an Interleave program statement can easily have multiple simultaneous pending bookmarks. Each program statement contained by the Interleave can, during its execution, pauseawaiting inputand then resume. This bookmarking can happen any number of times within each program statement contained by the Interleave. Because the required inputs can arrive in any order (the program statements contained by the Interleave are in this sense independent), the execution unfolds in an interleaved fashion.

One can imagine more elaborate models of execution as evolutions of the one just described. For example, we can associate a Boolean expression with Interleave (much as we did for IfElse and While), and have Interleave report its completion as soon as the expression evaluates to true. This lets us model situations such as the assignment of work items to multiple parties, where only a subset of the work items are required to complete in order to consider the goal of the overall program met (perhaps there are two redundant processes for determining a loan applicant's credit rating; as soon as one completes successfully, the other can be abandoned).

We can go further and attach Boolean expressions to each of the program statements contained by Interleave. Evaluation of these expressions determines the order in which the contained program statements run (perhaps Joe does not need to review the document until Don and Chris have completed their reviews, unless there are fewer than 24 hours until the deadline).

The important observation here is that each of these elaborations builds on a common model and mechanism for managing the execution of a set of contained program statements.




Essential Windows Workflow Foundation
Essential Windows Workflow Foundation
ISBN: 0321399838
EAN: 2147483647
Year: 2006
Pages: 97

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net