A channel is a model for synchronization via message passing. You can think of a channel as a queue/buffer of messages with a fixed maximum size. Messages may be sent over a channel by a thread (Sender) by adding to the queue/buffer and other threads which have a reference to this channel can receive the messages (Receivers) by removing from the queue/buffer. A channel can have multiple senders and receivers referencing it at any point of time.
Channels are used as a primitive to implement various other concurrent programming constructs.
For example, channels are heavily used in Google’s Go programming language and are very useful frameworks for high-level concurrent programming. In this lab you will be writing your own version of a channel, which will be used to communicate among multiple clients. A client can either write onto the channel or read from it. Keep in mind that multiple clients can read and write simultaneously from the channel. You are encouraged to explore the design space creatively and implement a channel that is correct and not exceptionally slow or inefficient. Performance is not the main concern in this assignment (functionality is the main concern), but your implementation should avoid inefficient designs that sleep for any fixed time or unnecessarily waste CPU time.
There are multiple variations to channels, such as whether the send/receive is blocking or non-blocking. In blocking mode, receivers always block until there is data to receive, whereas in non-blocking mode, they simply return. Similarly, with senders, in blocking mode, if the queue/buffer is full, senders wait until some receiver has retrieved a value and there is available space in the queue/buffer whereas in non-blocking mode, they simply leave without sending. In this lab, you will support both blocking and non-blocking send/receive functions.
The only files you will be modifying are channel.c and channel.h and optionally linkedlist.c and linkedlist.h. You should NOT make any changes in any file besides these four files. You will be implementing the following functions, which are described in channel.c and channel.h: –
channel_t* channel_create(size_t size) –
enum channel_status channel_send(channel_t* channel, void* data) –
enum channel_status channel_receive(channel_t* channel, void** data) –
enum channel_status channel_non_blocking_send(channel_t* channel, void* data) –
enum channel_status channel_non_blocking_receive(channel_t* channel, void** data) –
enum channel_status channel_close(channel_t* channel) –
enum channel_status channel_destroy(channel_t* channel) –
enum channel_status channel_select(select_t* channel_list, size_t channel_count, size_t* selected_index)
The enum channel_status is a named enumeration type that is defined in channel.h. Rather than using an int, which can be any number, enumerations are integers that should match one of the defined values. For example, if you want to return that the function succeeded, you would just return SUCCESS.
You are encouraged to define other (static) helper functions, structures, etc. to help structure the code.
The buffer.c and buffer.h files contain the helper constructs for you to create and manage a buffered channel. These functions will help you separate the buffer management from the concurrency issues in your channel code. Please note that these functions are NOT thread-safe. You are welcome to use any of these functions, but you should not change them. – buffer_t* buffer_create(size_t capacity)
enum buffer_status buffer_add(buffer_t* buffer, void* data)
Adds the value into the buffer. Returns BUFFERSUCCESS if the buffer is not full and value was added. Returns BUFFERERROR otherwise.
enum buffer_status buffer_remove(buffer_t* buffer, void** data)
Removes the value from the buffer in FIFO order and stores it in data. Returns
BUFFERSUCCESS if the buffer is not empty and a value was removed. Returns
void buffer_free(buffer_t* buffer)
Frees the memory allocated to the buffer.
size_t buffer_capacity(buffer_t* buffer)
Returns the total capacity of the buffer.
size_t buffer_current_size(buffer_t* buffer)
Returns the current number of elements in the buffer.
We have also provided the optional interface for a linked list in linkedlist.c and linkedlist.h. You are welcome to implement and use this interface in your code, but you are not required to implement it if you don’t want to use it. It is primarily provided to help you structure your code in a clean fashion if you want to use linked lists in your code. Linked lists may NOT be needed depending on your design, so do not try to force it into your solution. You can add/change/remove any of the functions in linkedlist.c and linkedlist.h as you see fit.