A tail of woe
From Spivey's Corner
I wrote this several years ago after pupils came to me almost in tears about a Concurrency practical. No doubt some things have improved since then. – Mike
This short report narrates the experiences of our unfortunate guinea-pig in attempting the first practical for the second-year course on Concurrency. And if a guinea-pig doesn't have a tail of woe, who does?
Contents |
Solving the problem
Our trusty rodent began diligently by solving the modelling problem on paper. It took him about ten minutes, but he feels that it might occupy half an hour in a tutorial with some of his less able students. That means the students themselves might spend one or two hours on the paper exercise – and certainly there have been finals questions on modelling with about this complexity. Remember, one hour for a guinea pig is like four or five human hours.
His only mistake was to suppose at first that there would be a process corresponding to each train, but this turns out not to be necessary.
Writing the script
Bravely, our volunteer tried to express his solution in the tortured input syntax of FDR[1]. He didn't really know where to start. The practical instructions contained no information. Should he read the very dry syntax summary in the FDR manual, or should he try to follow the examples that were handed out in lectures? He decided on the latter, but that meant a process of experiment, and the experiment couldn't begin until he was in front of the computer.
Quite by chance, our hero hit on several ideas that turned out to be crucial:
- declaring a channel by
channel forward: id . id . idso that events take the formforward.A.B.C. - writing
forward!i?y!k ->for an event that insists on certain values foriandk, but allows an external choice ofy. - the notation
if B then P else Qfor conditional – he looked long and hard in the on-line manual for the notationP <| B |> Qthat seems to be so important in CSP. - the notation for parallel composition that we need is something like
P [|{forward,reverse}|] Q. All those['s and|'s and{'s (and more) are needed – see later!
Of course, he later found all of these things in the manual, but it was hard to digest it all at once, and the manual was on-line, so he could not prepare anything beforehand.
Fighting the tools – fit the first
Starting FDR was no problem for our hero, once he had worked out that
the UNIX command was fdr2 (again, no hint in the documentation). A
control panel with all sorts of buttons and lights appeared, but all
the windows were empty. The bottom line said
FDR session for trains.csp, but nothing else happened. What could be wrong?
After pushing all the buttons he could see, still nothing. So the
indomitable guinea-pig began to do what he does in French restaurants:
scan the menu for something you don't understand, order it, and see
what arrives. Eventually, Options / Show Status revealed a new
window with the helpful remark
parse error near line 28 of trains.csp.
Our guinea-pig has seen such things before; in fact, he
has written several compilers that give error messages that are no
more helpful, although it's never occurred to him before to hide the
messages away in a secret window.
So what's wrong? Somewhere near line 28 appears the definition
Sheds =
Shed(A) [|{done}|] Shed(B) [|{done}|] Shed(C)
[|{done}|] Shed(D) [|{done}|] Shed(E)
This combines five processes, one for each shed, into a single process
that models all five sheds, insisting that they synchronize on the
done event that indicates the right engine is in the right shed.
Our hero knew that parallel composition was associative, and had seen
an example containing P ||| Q ||| R, so this seemed sensible.
But guinea-pigs who have programmed in assembler, C, APL and BASIC develop a nose for these things, and it soon became obvious that brackets were the thing to try. How would he explain that to his students? "Some things I can tell you; other things you just have to find out for yourself". So now we try
Sheds =
Shed(A) [|{done}|] (Shed(B) [|{done}|] (Shed(C)
[|{done}|] (Shed(D) [|{done}|] Shed(E))))
Getting more readable? Not half[2]. But this time FDR happily digests the file, and a list of processes appears in the window.
With a little more experimentation (no help from the instructions),
our hero composes the claim "Spec trace-refined-by Puzzle". Why trace
refinement? Is that good enough? Or should it be failures-divergences
refinement? The instructions don't help. [In fact, it has to be
trace refinement, because the puzzle refuses to do physically
impossible events that a simple spec permits].
Fighting the tools – fit the second
After "Check" is pressed, things start to happen in the secret window (just as well our friend kept it open ...). After several seconds, and many lines of output, the following appears:
Started: 34 Finished: 29 Transitions: 3700 Started: 34 Finished: 30 Transitions: 3800 Started: 34 Finished: 30 Transitions: 3900 Started: 34 Finished: 31 Transitions: 4000Value is not an event reverse
Error compiling Puzzle Loading... done Server: : failed to compile ISM
Ready
Sorry: some of that is in blue, and some in black. But you get the idea.
Funny, the thing seemed to 'compile' ok this time, and now we get this run-time error. Guinea-pigs can't cry, and that's lucky. What's wrong now? Well, to cut a long story short, our hero had written
Puzzle = Sheds [|{forward,reverse}|] Yards [|{forward,reverse}|] Tracks
as a way of putting the whole thing together – then he'd changed it into
Puzzle = (Sheds [|{forward,reverse}|] Yards) [|{forward,reverse}|] Tracks
because of earlier problems. But it turns out that you need to write
Puzzle =
(Sheds [|{|forward,reverse|}|] Yards) [|{|forward,reverse|}|] Tracks
because those extra |'s mean "take the set of events that can occur on
these channels". Clear? It ought to be, because there's no hint in
the instructions.
And now everything seems to work, and another secret window (can you find it?) lets you see the trace that solves the puzzle.
Giving up
With some sense of satisfaction, our battle-scarred protagonist made some paper tokens and followed out the solution on the picture. It worked! At that point he decided to give up. He had spent almost two hours on the practical, and had learnt nothing new about CSP after the first ten minutes. And remember, an hour in guinea-pig time is the same as three or four hours for ordinary humans ...
Oh, and in case your undergraduates come to you looking a bit red about the eyes, our indomitable friend has included his solution at the end. Fivers gratefully accepted, and cheques payable to the Guinea Pig's Claret Fund, because although our little friends are not freely moved to tears, they do like a drop or two from time to time.
- ↑ FDR is a piece of research software, and I don't mind using research software, and I don't mind my students using research software in labs. But I do mind it when I and they are told, "there's a manual on the web; read it."
- ↑ Of course, the FDR aficionados will object that
Sheds = [|{done}|] i : id @ Shed(i)would be much shorter, but how was our benighted pilgrim to work that syntax out?
[I've omitted the solution here in case the practical is still in use after all this time.]