COMP60621 Lab Exercise 5 Aim: This exercise is designed to illustrate the use of Promela-based modelling in the design phase of a software project. The exercise focuses on issues related to correct termination in concurrent systems. The source code and other files required for this exercise may be found in directory ~griley/COMP60621/source/labs/lab5. Make a new directory for this lab, and copy these files into it. The recommended form for submission of answers is by email to graham.riley@manchester.ac.uk (the submitted file must be a pdf file - no other electronic format is acceptable). Reports should have a maximum length of three pages. Supporting material, such as fragments of code and/or Promela, can be included in a brief appendix. Submission deadline: 9.00am Tuesday 4th December 2012. 1. A Supervisor-Worker example implemented using a Tuple Space. A tuple space is an implementation of an associative memory for parallel and distributed systems. A repository of tuples is maintained and the repository supports concurrent access. A tuple is essentially a tagged data record and the tag identifies the class of data in the tuple. Tuple spaces were the theoretical underpinning of the Linda language developed by David Gelernter and Nicholas Carriero at Yale in the late 1980s. This example illustrates a very basic tuple space that supports the following simple operations: i. out(“tag”, value) to place a tuple of type ‘tag’ in the tuplespace. ii. in(“tag”, data) to retrieve (and delete) a tuple from the tuplespace. This operation ‘blocks’ until a tuple of the requested type is available. iii. rd(“tag”, data) to retrieve, without deleting, a tuple from the tuplespace. This is also a blocking operation. iv. inp() and rdp() are polling (i.e. non-blocking) versions of in() and rd() operations. They return true, along with the tuple, if a matching tuple is found and false otherwise. (In a full implementation of a tuple space, the matching process involved in ‘in’ and ‘rd’ operations is much more sophisticated. In general, the ‘data’ in a tuple is allowed to be a list of items. Some of the items can be explicit values specified by the caller that must match the content of any tuple to be returned to the caller, whereas the other items represent variables to contain the ‘return’ values of data items not specified by the caller.) The application implemented in the supervisor-worker example code is a simple numerical integration of a mathematical function. An approximate value of the area under a curve is generated using the rectangle method. This involves splitting the xaxis into a number of partitions and summing the areas of each of the resulting rectangles under the function curve. The area of a rectangle associated with a given partition along the x-axis is determined from the size of a partition (all partitions are the same size) and the function value at the midpoint of the partition. In the example Promela model we will examine, the supervisor initially places a number of tuples tagged as task tuples into the tuplespace. Each task tuple represents one of the partitions along the x-axis. Workers repeatedly remove task tuples from the tuplespace, process them and return a result tuple to the tuplespace. The supervisor, meanwhile, removes result tuples and adds their value to the final result. Ensuring correct termination of the supervisor and the workers is one of the difficulties in designing and implementing the tuplespace application. The Promela file TestTuple.pml contains a model of a tuple space along with a process that accesses the tuplespace sequentially. You may find it useful to use jSpin to get a feel for how the tuplespace works by, for example, adding and/or modifying statements to access the tuplespace. The Promela file SupervisorWorker_1-full.pml contains models of the tuplespace and of the supervisor and worker processes. A system consisting of a single supervisor and two workers is constructed. This example implements a plausible-looking termination algorithm that is, in fact, flawed. The algorithm implemented in this version is as follows: Supervisor: forall tasks: out(“task”,…) forall results: in(“result”,…) out(“stop”) end Worker: While not rdp(“stop”) do { in(“task”,…) Compute result out(“result”,…) } End In the Promela file, the operations on the tuplespace are implemented in terms of Promela channels. a) Run and Verify the model in jSpin. Note that jSpin finds a deadlock. Consider the program from both the perspective of the operations on the tuplespace (in(), out() and rdp()) and, separately, from the channel-based level used in the implementation of the operations. At each of these levels, describe the origin and nature of this deadlock in terms of the behaviour of the component processes in the model and, hence, informally explain the ‘fault’ in the algorithm. [Hint:You may find it useful to use Guided mode in jSpin to examine the error trail produced by jSpin. You may also find it useful to start at the end of the trail and work backwards to see how the processes arrived at the final, deadlocked state.] The C code for the supervisor-worker example is intended to implement the design contained in the Promela file SupervisorWorker_1-full.pml. In this example, the only operations supported in the C code are in(), out() and rdp(). In the C code, these operations are implemented in terms of Pthreads and pthread condition variables. b) Briefly describe the use of pthread condition variables in implementing the operations of the tuplespace. c) Compile the code (the instructions are contained in the header of the file supervisor_worker.c). Execute the code a few times and confirm that the code does indeed exhibit deadlock. Report approximately how frequently does deadlock occurs. Explain how the deadlock arises in the code in terms of pthread functions. (Hint: focus on the use of pthread condition variables and of the pthread join function in the code) and describe the deadlock situation in the terminology of the ‘four conditions for deadlock’ [Hint: in this case, the shared resources may not be immediately obvious. Once deadlocked, consider what each thread is actually waiting for, and why.] d) Comment briefly on the correspondence between the channel-based implementation of the tuplespace operations in Promela and the pthreadbased implementation of the operations in the C code. Do you think the two implementations provide equivalent behaviour for the tuplespace operations (for our purposes – that is, for investigating termination behaviour)? [Hint: consider how each implementation provides the wait/notify functionality required of the operations.] 2. A correct termination algorithm One relatively simple termination algorithm for problems such as this is known as the ‘poison pill’ algorithm. In one version of this algorithm, the supervisor places ‘poison pill’ task tuples in the tuplespace, one for each worker. A ‘poison pill’ task tuple is a task with a special value. A worker continues taking tasks from the tuplespace until a ‘poison pill’ task is found, in which case the worker terminates. An algorithm for this version might be: Supervisor: forall tasks: out(“task”,…) forall results: in(“result”,…) forall workers out(“task”, stop) end Worker: While true do { in(“task”,…) if value is stop then exit else Compute result out(“result”,…) } end a) Describe the modifications required to the Promela models of the supervisor and workers to implement this version of the ‘poison pill’ algorithm. (Hints: What is a suitable special value for a ‘poison pill’ task? How should the supervisor and worker processes be modified to use this value?) Modify the Promela to include this algorithm and check that jSpin confirms that the improved model is deadlock free. b) Implement the algorithm in the C code. Undertake some testing to check that the code now terminates correctly. (Hint: The supervisor will need to know how many workers there are.) c) An alternative implementation of the ‘poison pill’ algorithm requires the supervisor to output only a single ‘poison pill’ task. For correct termination, when a worker finds this task, it must place a ‘poison pill’ task back into the tuplespace before terminating (why?). Design and describe a version of this algorithm and implement it in both the Promela and the C code. 3. Critical Section Problem and liveness properties This section uses Promela source code that was seen in Lab exercise 3. a) Construct a version of a Promela model that implements attempt 1 of the solution to the Critical Section Problem. Include in your model of one of the processes the code implementing a non-progressing non-critical section, as discussed in the lectures. Use jSpin to verify that this version of the model is not free from starvation. Briefly describe the technique you employed in using jSpin, including any additions you made to the model code and any properties and verification modes you used in jSpin. Also report the resulting output from jSpin. Informally describe how the starvation arises, with reference to the output reported by jSpin. b) Implement the same non progress non-critical section code in a version of the (allegedly correct) Dekker’s algorithm. Report the results of a verification of freedom from starvation in jSpin. Very briefly, describe how Dekker’s algorithm avoids the starvation present in attempt 1.