C++CSP v1.x Migration Guide

C++CSP2 is not fully compatible with C++CSP v1.x.

Code developed for the old 1.x versions of C++CSP will not compile or function the same with C++CSP2. I realise that this is very annoying for those who have already developed programs for C++CSP v1.x, and now have to migrate them. The reasons for this lack of compatibility include:

To help all previous users of C++CSP v1.x to convert their programs for use with C++CSP2, I have prepared this guide. I will go through the various areas of C++CSP2 that have changed. Thankfully, almost all of these changes will result in a compile error. So if you go through each compile error, then with the help of this guide you can convert your program to using C++CSP2 properly. Code blocks are provided that show the OLD code, and the corresponding NEW code.

The Basics (a.k.a. Nuts and Bolts)

The first change is that the C++CSP headers have "moved":
    #include <csp/csp.h> //OLD
    #include <cppcsp/cppcsp.h> //NEW

The functions used to start and stop the C++CSP2 library have had their names changed (for similar reasons to the above):

    Start_CSP(); //OLD
    Start_CSP_NET(); //OLD
    End_CSP(); //OLD
    
    Start_CPPCSP(); //NEW
    End_CPPCSP(); //NEW

Time/Sleep/Yield Functions

The Time data-type and its associated functions such as Seconds or GetSeconds remain the same; in fact, a couple more functions have been added, for your convenience that use references instead of pointers.

The sleep/wait functions used to be members of csp::CSProcess. This caused annoyance if you wanted to call these functions in a class that was not itself a process. So these functions have now been moved to being stand-alone:

    class SomeProcess : public CSProcess
    {
    protected:
        void run()
        {
            sleepFor(Seconds(1)); //OLD, sleepFor was a member of CSProcess
            SleepFor(Seconds(1)); //NEW, SleepFor is a function in namespace csp
        
            Time t;
            CurrentTime(&t);
            t += Seconds(1);
            sleepTo(t); //OLD, sleepTo is a member of CSProcess
            SleepUntil(t); //NEW, SleepUntil is a function in namespace csp
        }
    };

Similarly, yield used to be a member function of csp::CSProcess. There are now two stand-alone yield functions in C++CSP2; csp::CPPCSP_Yield() yields to other user-threads in the same thread, and csp::Thread_Yield() yields to other kernel-threads. CPPCSP_Yield is useful if you are not doing any channel communications or synchronisation and want to allow other user-threads to run. Thread_Yield is perhaps less useful; your thread will be switched out at the end of its time-slice anyway.

Processes, and Running Processes

One change to processes is described in the previous section -- its helper functions have been moved out of the class. There are two other major changes to processes themselves.

There are now two process classes; csp::CSProcess and csp::ThreadCSProcess. Usually you will want to use CSProcess as before; if you do not change any of your existing classes from inheriting from CSProcess, everything will work fine as before. For an explanation of the purpose of csp::ThreadCSProcess, click on its link to read the documentation.

The constructor of csp::CSProcess used to have two parameters, one which was a mandatory stack size and one which was a mysterious reserve size. This constructor now takes one optional argument (the stack size). The reason for it becoming optional is not that I have added a magical way of determining the right stack size (alas!), but because there is an arbitrary high default value of 1 megabyte. All your existing processes that specify a stack size should be fine as before -- although if you are moving to 64-bit, remember that some of your data types will double in size. You can no longer specify a reserve size -- if you have any code that did specify the second parameter (which is unlikely), simply remove the second parameter.

One of the major changes to the library has been in the area of running processes. The new mechanism is explained in detail on the Running Processes page. Below are examples of how to convert your existing code to the new system. It can either be converted as-is, to continue using user-threads, or you can change to using kernel-threads:

    CSProcess* processA = new Widget;
    CSProcess* processB = new OtherWidget;
    
    Parallel(processA,processB,NULL); //OLD
    
    RunInThisThread(InParallelOneThread (processA) (processB) ); //NEW, using user-threads
    //or:
    Run(InParallelOneThread (processA) (processB) ); //NEW, using kernel-threads (processA and processB together in a single new thread)
    //or:
    Run(InParallel (processA) (processB) ); //NEW, using kernel-threads (processA and processB together in separate new threads)
    
    
    
    Sequential(processA,processB,NULL); //OLD
    
    RunInThisThread(InSequenceOneThread (processA) (processB) ); //NEW, using user-threads
    //or:
    Run(InSequenceOneThread (processA) (processB) ); //NEW, using kernel-threads (processA and processB together in a single new thread)
    //or:
    Run(InSequence (processA) (processB) ); //NEW, using kernel-threads (processA and processB together in separate new threads)

You will notice that the syntax has changed. Gone is the annoying C-style NULL-terminated vararg list, replaced by a new C++ mechanism. Each parameter is bracketed separately -- no commas! There is a new csp::Run method that can take sequential or parallel lists. You can also easily compose parallel sequences or sequenced parallels. All the detail is on the Running Processes page.

The mechanism for forking has also changed. The old mechanism involved spawning a process with a given barrier to synchronise on when it was done. The new mechanism uses the Scoped Classes idea.

    CSProcess* processA = new Widget;
    
    //begin OLD
    
    Barrier barrier;
    
    spawnProcess(processA,&barrier); //spawnProcess was a static method of CSProcess
    
    //... Do something ...
    
    barrier.sync(); //wait for processA to finish
    
    //end OLD
    
    //begin NEW
    
    {
        ScopedForking forking;
        
        forking.forkInThisThread(processA); //using user-threads, processA remains in this thread
        //or:
        forking.fork(processA); //using kernel-threads, processA is in a new thread
        
        //... Do something ...
    
    } //When forking goes out of scope, it waits for all the forked processes to finish
    
    //end NEW

The new csp::ScopedForking class is a little different, but ultimately it is easier and nicer than the old mechanism. This way you can't forget to synchronise on the barrier and wait for the processes, even if an exception is thrown.

Channel Ends

As in C++CSP v1.x, channels are still used via their ends. Chanout is much the same as it used to be, except that the ParallelComm functionality is gone (see the next section), and instead of the old noPoisonClone() member function, there is a stand-alone csp::NoPoison method that does the same thing. These changes also apply to the channel input ends.

C++CSP v1.x had a single channel input-end type; Chanin. Unbuffered unshared channels supported all its functions, but buffered channels did not support extended input and channels with shared reading ends did not support alting. If you tried to use unsupported functionality, a FunctionNotSupportedException was thrown. This has now been mixed through two major changes:

If you use a channel-end in an alt, you will need to change the type from Chanin to AltChanin. You will get compile errors otherwise -- complaining about the lack of an inputGuard() method (although this method itself has now changed -- see the section on Alting below).

ParallelComm

The ParallelComm system in C++CSP v1.x was an efficient way of performing parallel communications without starting new processes, that took advantage of an easy way of implementing it in the user-threaded C++CSP v1.x run-time.

The changes made to C++CSP to support kernel-threads in C++CSP2 meant that this system could no longer be retained. Although this does help make the library similar, it will make simple parallel communications more cumbersome. The best way to mimic the functionality is to use the csp::common::WriteOnceProcess and csp::common::ReadOnceProcess processes:

    //in and out are Chanin<int> and Chanout<int> respectively
    int inInt,outInt;
    outInt = 9;
    
    //begin OLD:
    ParallelComm pc(in.parIn(&inInt),out.parOut(&outInt));
    pc.communicate();
    //end OLD
    
    //begin NEW
    Run(InParallelThisThread
        (new ReadOnceProcess<int>(in,&inInt))
        (new WriteOnceProcess<int>(out,&outInt))
    );
    in.checkPoison();
    out.checkPoison();
    //end NEW

The checkPoison() calls are important. If the old ParallelComm encountered poison, it would throw a PoisonException. Running the sub-processes as above will not detect poison; if the processes do attempt to use poisoned channels, they will fail and stop. To find out if the channels are now poisoned, you must check the poison manually. Even then, you will not know using the above code whether or not the channel communications succeeded before the poison or not.

There are other ways of implementing the same functionality as ParallelComm. One option would be to perform the communications sequentially. This would make things much easier, but it is not always possible (this could lead to deadlock). Another option, if you do not mind some buffering, is to use buffered channels sequentially, or to use a csp::common::Id process to achieve a similar effect.

Channels

The channels themselves are broadly similar to C++CSP v1.x. The return types of the reader() methods have changed to AltChanin in some places (as a consequence of the changes to channel-ends mentioned previously). The noPoisonReader()/noPoisonWriter() methods have also gone:
    One2OneChannel<int> c;
    
    Chanin<int> in = c.noPoisonReader(); //OLD
    
    Chanin<int> in = NoPoison(c.reader()); //NEW

The names of the buffered channels have changed, from One2OneChannelX to BufferedOne2OneChannel. The original names were actually the result of a mix-up but were never changed, until now. Other changes to the buffered channels are detailed in the Buffered Channels section below.

Buffered Channels

As mentioned above, the names of the buffered channels have changed. The channels are now:

The names of the channel buffers have always changed. Previously - in line with JCSP - the channel buffers were derived from the ChannelDataStore class. Copies of buffers were obtained by calling the clone() method of the buffer.

In C++CSP2, channel buffers now derive from csp::ChannelBuffer. The interface has changed to be more flexible and support new features - this is only of concern if you previously implemented your own buffers. Instead of a clone() method, copies of buffers are obtained from a channel-buffer factory.

    //begin OLD:
    Buffer<int> buffer;
    One2OneChannelX c(buffer),d(buffer);
    //end OLD
    
    //begin NEW:
    FIFOBuffer<int>::Factory bufferFactory;
    BufferedOne2OneChannel c(bufferFactory), d(bufferFactory);
    //end OLD

You will note that the buffer names have also changed. Buffer has become csp::FIFOBuffer and InfiniteBuffer is now csp::InfiniteFIFOBuffer. ZeroBuffer has gone (use an unbuffered channel instead) and csp::OverwritingBuffer has been added.

The other major change to buffered channels is that extended input on buffered channels is now supported. The behaviour is roughly intuitive - in the non-overwriting FIFO buffers, the item of data is read from the buffer at the beginning of the extended input but not removed until the end of the extended input. However for the overwriting buffer (to prevent the writer ever blocking), the data is effectively removed at the beginning of the extended input.

Extended Input

As noted above, extended input is now available on all channels - both buffered and unbuffered. The mechanism for using it has also changed. Previously, an awkward extInput() function was offered for performing extended input. This has been changed to use a new scoped class:
    //begin OLD:
    class MyProcess
    {
        Chanin<int> in;
        Chanout<int> out;
        int n;
    public:
        void extendedAction()
        {
            out << n;
        }
    protected:
        void run()
        {
            while(true)
            {
                in.extInput(&n,this,&MyProcess::extendedAction);
            }
        }
    };
    //end OLD
    
    //begin NEW:
    class MyProcess
    {
        Chanin<int> in;
        Chanout<int> out;       
    protected:
        void run()
        {
            while(true)
            {
                int n;  
                {
                    ScopedExtInput<int> extInput(in,&n);
                    out << n;
                } //extended input finishes at end of scope
            }
        }
    };
    //end NEW

Anyone who had to use the old method will appreciate the comparative ease of this new version. No more awkward member functions or static functions; the code is inline where you would want it to be. This method is also exception-safe; in the case of an exception being thrown during an extended input (for example, poison), the extended input is still ended safely. More details are available on the csp::ScopedExtInput page.

Barriers and Buckets

Buckets are effectively identical to their C++CSP v1.x version. Barriers, however, have changed significantly. Previously barriers were used like buckets; processes wanting to use the barrier held a pointer to it, and used it accordingly. C++CSP2 introduces the idea of barrier-ends, which aids safe usage of barriers.

Barrier-ends (csp::BarrierEnd) are always "encased" in a csp::Mobile wrapper:

    //begin OLD:
    Barrier barrier;
    barrier.enroll();
    barrier.sync();
    barrier.resign();
    //end OLD
    
    //begin NEW:
    Barrier barrier;
    Mobile<BarrierEnd> barrierEnd(barrier.end());
    barrierEnd.enroll();
    barrierEnd.sync();
    barrierEnd.resign();
    //end NEW
You may also want to read about the csp::ScopedBarrierEnd, which offers functionality in a similar way to csp::ScopedForking and csp::ScopedExtInput. Barrier-ends can be constructed as non-enrolled (csp::Barrier::end()) or enrolled (csp::Barrier::enrolledEnd()). If you destroy an enrolled barrier-end, try to sync on a non-enrolled barrier end or a barrier is destroyed while processes are still enrolled a csp::BarrierError will be thrown. This is not just to annoy the programmer - these uses of barriers mean that your program has a bug in it!

Processes that used to take a Barrier* parameter to their constructor should now take a Mobile<BarrierEnd> parameter.

Alting

Alting has also changed in a small but very significant way since C++CSP v1.x. In JCSP, you use channel guards in an alternative, and when the index of the chosen guard is returned, it is the programmer's responsibility to perform the channel input. This decision was reversed in C++CSP v1.x to make the channel guards perform the input automatically on the chosen guard. This only served to confuse users of the library and also made using an extended guard particularly annoying.

I have now reversed the situation in C++CSP2, returning to the JCSP method. I realise that this will be very confusing to existing C++CSP v1.x users, but I think this tough transition is for the best. It will be less confusing for new users and allows extended inputs to be done in a nice way. Existing alting code will break because the inputGuard method no longer takes a parameter, so you will be able to see where the code needs changing. You must removed the parameter, and instead manually perform the input inside the switch statement.

For example:

    //begin OLD:    
    class MyProcess
    {
        Chanin<int> in0,in1;
        Chanout<int> out;
        int n0,n1;
    public:
        static void extInput(CSProcess* proc)
        {
            MyProcess* myproc = (MyProcess*)proc;
            myproc->out << myproc->n1; 
        }
    protected:
        void run()
        {           
            Alternative alt(
                in0.inputGuard(&n0),
                in1.extInputGuard(&extInput,&n1),
                new RelTimeoutGuard(csp::Seconds(1)),
            NULL);
            
            while (true)
            {
                switch (alt.priSelect())
                {
                    case 0: //no need to perform input
                        out << n0;
                        break;
                    case 1: //no need to perform input
                        //The real code was in the extended action
                        break;
                    case 2: //timeout
                        out << -1;
                        break;
                }
            }
        }
    };
    //end OLD
    
    //begin NEW:
    class MyProcess
    {
        AltChanin<int> in0,in1; //Now AltChanin instead of Chanin
        Chanout<int> out;       
    protected:
        void run()
        {
            int n;
            Alternative alt(  boost::assign::list_of<Guard*>
                (in0.inputGuard()) 
                (in1.inputGuard()) 
                (new RelTimeoutGuard(csp::Seconds(1)))  
            );
            
            while (true)
            {
                switch (alt.priSelect())
                {
                    case 0: //must perform input:
                        in0 >> n;
                        out << n;
                        break;
                    case 1: //must perform extended input:
                        {
                            ScopedExtInput<int> extInput(in1,&n);
                            out << n;
                        }
                        break;
                    case 2: //timeout
                        out << -1;
                        break;
                }
            }
        }
    };
    //end NEW

You will notice a few of the other changes; AltChanin must be used for alting channel-ends, instead of Chanin. The guard list that is passed to the Alternative is no longer the clumsy NULL-terminated vararg list, but instead uses the cleaner boost::assign library. Guards for extended inputs are now identical to normal input guards - you simply choose in the switch statement whether to perform an input or an extended input. You can even vary it each time you perform an alt, if you wanted. As before, the Alternative takes care of deleting the guards itself.

Networking

C++CSP v1.3.x had built-in networking support. This is currently being reconsidered/redeveloped for C++CSP2, but is not yet available.

Miscellaneous Other Features

C++CSP v1.x had a few other miscellaneous features, in particular logging and channel bundles. Logging, which was always a rather strange and useless feature, has now been removed from the library. I am not aware that anyone had much use for it. Channel bundles used some flashy templating but I do not know if anyone used them much. If you did use them, and would like them re-added to C++CSP2, please let me know. However, for the time being, I have omitted them in the interests of keeping the library simple.
Generated on Mon Aug 20 12:24:28 2007 for C++CSP2 by  doxygen 1.4.7