CCR 101 - Part 3: Ports
In a message-passing system there must exist some mechanism over which to pass messages. In CCR, ports are that mechanism. They are essentially simple FIFO queues. For your programming convenience, they are declared as the generic type, Port<T>, where T defines the type that can travel on that port.
The following sample puts 10 strings on a port and then dequeues them and writes them to the console:
class Sample0301 {
static void Main(string[] args) {
Port<string> port = new Port<string>();
// Enqueue the items
for (int i = 0; i < 10; ++i)
port.Post(i.ToString());
// Dequeue the items
string item;
while (port.Test(out item))
Console.WriteLine(item);
Console.ReadLine();
}
}
As illustrated, we use the Post method to enqueue items, and the Test method to dequeue them. Test will return false if the port is empty and set the output parameter to its default value.
PortSets
PortSets are just aggregated ports of different types. They are essentially a programming convenience and they use overloads to simplify posting any of the supported types to the single portset. The following example posts 10 numbers to a portset, evens to an int port and odds to a string port.
class Sample0302 {
static void Main(string[] args) {
PortSet<int, string> portSet = new PortSet<int, string>();
// Enqueue the items
for (int i = 0; i < 10; ++i) {
if (i % 2 == 0)
portSet.Post(i);
else
portSet.Post(i.ToString());
}
}
}
There's no equivalent for reliably reading items out of the PortSet, rather you must access the individual ports explicitly. Each sub-port is numbered P0...PN, depending on the number of types. The following examples dumps the contents of each sub-port populated above.
class Sample0303 {
static void Main(string[] args) {
PortSet<int, string> portSet = new PortSet<int, string>();
// Enqueue the items (elided)
// ...
// Output them
int intElem;
while (portSet.P0.Test(out intElem))
Console.WriteLine("int: {0}", intElem.ToString());
string stringElem;
while (portSet.P1.Test(out stringElem))
Console.WriteLine("string: {0}", stringElem);
Console.ReadLine();
}
}
Although the CCR provides generic versions of the PortSet class with ability to be specialized with up to 19 different types, the most commonly used is probably the 2-type version (shown above). The reason for this is that two types is often all it needs to convey the result of an operation i.e. the success result and the failure result. In fact, this is so common the CCR defines a form of this type for you:
namespace Microsoft.Ccr.Core {
// Summary:
// Port collection with ports typed for SuccessResult and Exception
//
// Remarks:
// This port type is appropriate as the result port type for generic request/response
// interactions
public class SuccessFailurePort : PortSet<SuccessResult, Exception> {
public SuccessFailurePort();
}
}
Unfortunately, SuccessResult is really just a wrapper around an int, so if the result of your operation defines more than that, you'll need to define your own PortSet explicitly. If your operation returns more than one type in either scenario, you can use the useful Tuple<T0, ..., TN> class to wrap these types.
Next Steps
So far we've only really covered the very basics of the CCR, and if you're coming to these tutorials completely cold, then you're probably distinctly unimpressed thus far. In the next tutorial, we'll introduce the predominant concept in the CCR, Arbiters.
Conceptually, arbiters stand between ports and messages on one side, and dispatcher queues and tasks on the other. They respond to messages arriving on ports, by determining whether some application-specified condition has been met by that arrival. If it has, it will schedule some specified task to execute on a specified dispatcher queue. Arbiters cover a variety of basic scenario and are (importantly) composable. You can build more complex arbitrations out of simpler ones.