CPL - Channel-based Concurrency Module
Lab #2 – Mini-Project
To submit your answers, simply push your files onto the repository. The problem will be graded.
Concurrent Bounded Buffer
Your task is to implement a bounded buffer that obeys a FIFO discipline. The buffer has a very simple interface:
New(capacity int) BoundedBuffer
- A function that returns a new empty buffer with the given capacity.
Add(elem BufferElem)
- A (blocking) method which adds a new element to the buffer.
Remove() BufferElem
- A (blocking) method which removes and returns the last inserted element.
You can define the type BufferElem
as interface{}
using go’s
type definition mechanism (look it up!). This will allow the buffer to
take elements of any type.
Your task is to implement a buffer that adheres to the API above,
backed by an array slice (your code should not use
any functionality from Go’s atomic
or sync
packages):
When you create the buffer use persistent auxiliary workers to handle the insertions and removals (2 per operation). Submit this file as
boundedbuffer/buffer.go
.Regardless of how you implemented the buffer, the proposed API can easily give rise to deadlocks if consumers are not careful. Write a small test program (
boundedbuffer/buffer_test.go
) that uses a buffer with non-zero capacity and two clients that results in a global deadlock (i.e. both clients and the workers are stuck). Name this testTestGlobalDeadlock
, following Go’s testing API. Also write aTestBuffer
function that uses more than one client and exercises the buffer API in a reasonable way.The workers are reading and writing to a shared data structure concurrently. Are these concurrents reads and writes safe? If so, comment your code with an explanation of how this is achieved (don’t use any “weird” packages for this!). If not, give an example workload (put it in the
buffer_test.go
file, asTestRace
) under which a “problem” can arise, exhibiting the appropriate output of the go race detector tool (see here).If your implementation was indeed racy, modify it to ensure that the two workers access the underlying array in mutual exclusion. Call it
boundedbuffer/betterbuffer.go
. Hint: Use channels linking the workers.If your implementation was not racy, congratulations. Now, break it! Place the broken implementation in
boundedbuffer/buffer_racy.go
and don’t forget to add the appropriate test as requested in point (3) above.Does the buffer ensure freedom from starvation to its clients? Explain (write your answer in a file
boundedbuffer/starvation.txt
).Since the buffer is backed by an array slice, there is still a lot of shared-memory concurrency in your code. Lets now try to write a bounded buffer in a way that is more aligned with Go’s concurrency philosophy: Write an implementation (
boundedbuffer/idiomaticbuffer.go
) for the bounded buffer API, with two writing and two reading workers, preserving the good properties of the previous implementation, that is not backed by an array or an array slice (but without using anyimport
ed concurrent data structure). Revisit question 6 for this buffer, leaving your answer as comments in the code.
That’s it! Don’t forget to push!