Wednesday 5 May 2010

Introducing server processes

Much of the recent activity on the python-csp code base has to do with finding useful ways to debug networks of CSP processes. The dynamic analysis of CSP programs has a long history, dating back to Hoare's original work. I'll post more about that another time, but this post details a small change to the core library that enables debugging to run effectively. 

A common idiom in CSP-style code is to have many processes in a process network (connected via channels, or other guards) which all loop forever. If the program needs to be terminated, that is usually done via channel poisoning. I call these processes server processes, because they continually serve values to their output channels (if they have any).

In fact, all of the processes in the csp.builtins library are server processes, and python-csp has quite a few of these. It has most of the processes from the JCSP "plugNplay" package and a distinct process for every built-in unary or binary operator in Python. So, for example, the csp.builtins.Plus processes reads two pieces of data from its input channels, and sends their addition to an output channel. One possible implementation of csp.builtins.Plus would be this:

@process def Plus(inchan1, inchan2, outchan):     while True:         outchan.write(inchan1.read() + inchan2.read()

In reality, this isn't quite how csp.builtins.Plus is implemented, as there are quite a few unary and binary operators in Python, so we make use of metaprogramming to generate most of these builtin processes.

The problem with server processes is that if you want your debugger to deduce useful information about your program then at some point your code needs to terminate so that the debugger can compute a call graph, or some other structure, from which other useful information can be gathered. Ideally, it would also be nice if the debugger could deduce that your server processes are intending to run indefinitely, even though it actually terminates when the debugger executes them.

To achieve this effect, python-csp now has a special class for creating server processes called CSPServer, and a new decorator, called @forever. These work exactly like the usual CSPProcess and @process types that have been included in python-csp since its inception. Apart from using the new constructors, the only change you need to make in your code to use @forever or CSPServer is that a server process should be a generator and, usually, it should yield at the end of each iteration of its internal loop.

As an example, if we were to reimplement csp.builtins.Plus with the new constructors, we can do this in two ways, firstly with the decorator (which is the recommended method) and secondly using the CSPServer class directly. In the code below I am using two other processes from csp.builtinsGenerate which sends an infinite supply of integers down its output channel and Printer which prints anything it reads from its input channel to STDOUT (by default, you can customise this to use any other file type). So now, Plus could be implemented thus:

@forever def Plus(inchan1, inchan2, outchan):     while True:         outchan.write(inchan1.read() + inchan2.read())         yield ... # Silly way to print even numbers in1, in2, out = Channel(), Channel(), Channel() Generate(in1) & Generate(in2) & Plus(in1, in2, out) & Printer(out)

If we decide not to use decorators, then the code is essentially the same, we just need to remember to construct any Plus processes with the CSPServer class:

@forever def Plus(inchan1, inchan2, outchan):     while True:         outchan.write(inchan1.read() + inchan2.read())         yield ... # Silly way to print even numbers in1, in2, out = Channel(), Channel(), Channel() Generate(in1) & Generate(in2) & CSPServer(Plus, in1, in2, out) & Printer(out)

Posted via web from python-csp