CCR 101 - Part 5: Arbitrations (Choice)

Our first 'proper' arbitration is Choice. It takes one or more (normally at least two to be useful) Receiver tasks and schedules the first eligible one. The others will never be scheduled. An example will suffice:

class Sample0501 {
static void Main(string[] args) {
using (DispatcherQueue dq = new DispatcherQueue()) {
Port<int> intPort = new Port<int>();
Port<string> stringPort = new Port<string>();
Port<EmptyValue> emptyPort = new Port<EmptyValue>();
Arbiter.Activate(dq, Arbiter.Choice(
Arbiter.Receive(false, intPort, delegate(int i) {
Console.WriteLine("{0} is an int.", i.ToString());
}),
Arbiter.Receive(false, stringPort, delegate(string s) {
Console.WriteLine("{0} is a string.", s);
}),
Arbiter.Receive(false, emptyPort, delegate(EmptyValue e) {
Console.WriteLine("An empty line.");
})));
Console.Write("Enter a value: ");
string input = Console.ReadLine();
if (input.Length == 0) {
emptyPort.Post(EmptyValue.SharedInstance);
} else {
int intInput;
if (Int32.TryParse(input, out intInput))
intPort.Post(intInput);
else
stringPort.Post(input);
}
Console.ReadLine();
}
}
}

Only the first half of this program is of any interest. First, we create 3 ports. The last port is of type EmptyValue, a CCR type that can be useful for indicating that something happened even if there is no data associated with the message. Then, we create and activate our Choice arbitration. In the above example, we pass three Receiver tasks to the Arbiter.Choice call.


When a message is posted to any one of the ports, the corresponding receiver task becomes eligible for scheduling. However, it is the Choice arbitration that determines whether it will actually be scheduled. The Choice operates a simple first-come, first-served policy. As soon as a single receiver is scheduled, all the others in the Choice are doomed. This can be demonstrated by modifying the example so that each port is posted to at the end anyway. You'll see that the receivers don't fire.

class Sample0502 {
static void Main(string[] args) {
using (DispatcherQueue dq = new DispatcherQueue()) {
Port<int> intPort = new Port<int>();
Port<string> stringPort = new Port<string>();
Port<EmptyValue> emptyPort = new Port<EmptyValue>();
Arbiter.Activate(dq, Arbiter.Choice(
Arbiter.Receive(false, intPort, delegate(int i) {
Console.WriteLine("{0} is an int.", i.ToString());
}),
Arbiter.Receive(false, stringPort, delegate(string s) {
Console.WriteLine("{0} is a string.", s);
}),
Arbiter.Receive(false, emptyPort, delegate(EmptyValue e) {
Console.WriteLine("An empty line.");
})));
Console.Write("Enter a value: ");
string input = Console.ReadLine();
if (input.Length == 0) {
emptyPort.Post(EmptyValue.SharedInstance);
} else {
int intInput;
if (Int32.TryParse(input, out intInput))
intPort.Post(intInput);
else
stringPort.Post(input);
}
intPort.Post(0);
stringPort.Post("Hello World.");
emptyPort.Post(EmptyValue.SharedInstance);
Console.ReadLine();
}
}
}

Note that none of the Receivers is persistent (the first parameter is false in Arbiter.Receive). Choice requires this and will throw a runtime exception if you try and pass a persistent Receive to it. The reason for this is simple: Choice picks one from n Receives and then ignores the rest, so there's actually no point in any of them being persistent.


Choice Arbitrations for determining success/failure.

Perhaps the most common use of Choice is determining whether some asynchronous operation succeeded or failed. In fact, it's so common, the Arbiter class has a particular overload of the Choice method that takes a 2-ary PortSet and has a more direct syntax for declaring the handlers for either outcome. Look at the example below. It attempts to resolve a hostname to an IP address via an asynchronous operation.

class Sample0503 {
static void Main(string[] args) {
using (DispatcherQueue dq = new DispatcherQueue()) {
PortSet<IPAddress[], Exception> result = new PortSet<IPAddress[], Exception>();
Arbiter.Activate(dq, Arbiter.Choice(result,
delegate(IPAddress[] addresses) {
for (int i = 0; i < addresses.Length; ++i) {
Console.WriteLine("{0}. {1}", i.ToString(), addresses[i].ToString());
}
},
delegate(Exception e) {
Console.WriteLine(e.Message);
}));
Console.Write("Enter host: ");
string hostname = Console.ReadLine();
Dns.BeginGetHostAddresses(hostname, delegate(IAsyncResult iar) {
try {
result.Post(Dns.EndGetHostAddresses(iar));
} catch (Exception e) {
result.Post(e);
}
}, null);
Console.ReadLine();
}
}
}

First, we define a PortSet with two types; the first an array of IPAddresses that represents the successful outcome, the second an exception which represents failure. Next, we create and activate our Choice using the more concise syntax available (no explicit Receives).


In the bottom half of the program, we issue the call via the Dns class. Note how I've used an anonymous delegate to implement the operation's callback, posting to the result portset in both the success and failure case. This is a very common implementation pattern when wrapping APM methods with CCR ports.


2 comments:

  1. Anonymous said,

    iodyne.blogspot.com is very informative. The article is very professionally written. I enjoy reading iodyne.blogspot.com every day.
    cash advance loan payday

    on November 25, 2009 at 10:16 AM


  2. Anonymous said,

    Play Casino Games tyuueooru
    http://stonewalljacksoncarnival.org/ - Free Casino Games
    All you need to is a well operating computer and an Internet connection and you?re done with your gambling.
    [url=http://stonewalljacksoncarnival.org/]Best Casino[/url]
    So you can have a wonderful gambling experience with the comfort at your home.
    Online Casino Game
    More casino options The number of online casino is far higher than bricks and mortars based casino all across the world.

    on December 26, 2009 at 2:11 AM