From fcca47f0430f3cff7e4fd195e8056ad658401eff Mon Sep 17 00:00:00 2001 From: Ben Goldsworthy Date: Wed, 24 Jun 2020 13:51:34 +0100 Subject: [PATCH] Initial commit --- .gitignore | 12 +++ PREEMPTIVE/libprethread.a | Bin 0 -> 21298 bytes chan.c | 164 ++++++++++++++++++++++++++++++++++++++ chan.h | 22 +++++ queue.c | 79 ++++++++++++++++++ queue.h | 21 +++++ sem.c | 103 ++++++++++++++++++++++++ sem.h | 21 +++++ 8 files changed, 422 insertions(+) create mode 100644 .gitignore create mode 100755 PREEMPTIVE/libprethread.a create mode 100755 chan.c create mode 100755 chan.h create mode 100755 queue.c create mode 100755 queue.h create mode 100755 sem.c create mode 100755 sem.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07b1505 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +thread.* +APPS/ +PREEMPTIVE/libprethreat.a +PREEMPTIVE/makefile +PREEMPTIVE/README +PREEMPTIVE/sem.c +PREEMPTIVE/sem.h +PREEMPTIVE/thread.c +libthread.a +core +makefile +README diff --git a/PREEMPTIVE/libprethread.a b/PREEMPTIVE/libprethread.a new file mode 100755 index 0000000000000000000000000000000000000000..0a6c89500e13b68ad1bd1e193e6d99e536eabb0f GIT binary patch literal 21298 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 literal 0 HcmV?d00001 diff --git a/chan.c b/chan.c new file mode 100755 index 0000000..bf6a80f --- /dev/null +++ b/chan.c @@ -0,0 +1,164 @@ +/************************************************************************ + * * + * file: chan.c * + * * + * Module implementing message passing IPC for threads. * + * * + * Our implementation assumes that threads calling us are * + * subject to arbitrary preemption. * + * * + ************************************************************************/ + +#include "thread.h" +#include "chan.h" +#include +#include + +Sem* chan_mutex; /* protect chan module from race conditions */ + +/************************************************************************ + * INTERFACE: chan_create() * + * * + * Make a channel and return a pointer to it. Return (Chan *)0 on * + * failure. A channel is a struct which we allocate using malloc(). * + * Internally, the channel consists of 3 semaphores which are created * + * using chan_create(). We initalise the data field of the channel to 0.* + * Failure occurs if either the malloc() fails, or we fail to create * + * some of the semaphores. In case of failure we are careful to * + * free any just-allocated resources to avoid memory leaks. * + ************************************************************************/ +Chan* +chan_create() +{ + Chan* c; + static int firstCall = 1; + + if (firstCall) { + firstCall = 0; + if((chan_mutex = sem_create(1)) == (Sem*)0) + return (Chan*)0; + } + + if ((c = (Chan*)malloc(sizeof(Chan))) == (Chan*)0) + return (Chan*)0; + + if ((c->sblock = sem_create(0)) == (Sem*)0) { + free((void*)c); + return (Chan*)0; + } + + if ((c->rblock = sem_create(0)) == (Sem*)0) { + free((void*)c); + sem_destroy(c->sblock); + return (Chan*)0; + } + + if ((c->send_serialiser = sem_create(1)) == (Sem*)0) { + free((void*)c); + sem_destroy(c->sblock); + sem_destroy(c->rblock); + return (Chan*)0; + } + + c->data = 0; + return c; +} + +/************************************************************************ + * INTERFACE: chan_destroy() * + * * + * Destroy a channel. Must only be allowed to succeed if we are able * + * to successfully destroy *all* the semaphores. Must handle the case * + * where we can destroy some semaphores but not others (in which case * + * we should obviously report failure). If we fail to destroy * + * a semaphore, we need to restore previously-destroyed ones to return * + * the channel to its pre-call state. * + * Return -1 on failure; and 1 on success. * + ************************************************************************/ +int +chan_destroy(Chan* c) +{ + Chan* backup; + + sem_P(chan_mutex); + if ((backup = chan_create()) == (Chan*)0) + return -1; + + if ((sem_destroy(c->rblock)) == -1) { + sem_destroy(backup->rblock); + sem_destroy(backup->sblock); + sem_destroy(backup->send_serialiser); + free((void*)backup); + sem_V(chan_mutex); + return -1; + } + /* So far: successfully destroyed rblock */ + if ((sem_destroy(c->sblock)) == -1) { + c->rblock = backup->rblock; + sem_destroy(backup->sblock); + sem_destroy(backup->send_serialiser); + free((void*)backup); + sem_V(chan_mutex); + return -1; + } + + /* So far: successfully destrotyed rblcok and sblock */ + if ((sem_destroy(c->send_serialiser)) == -1) { + c->rblock = backup->rblock; + c->sblock = backup->sblock; + sem_destroy(backup->send_serialiser); + free((void*)backup); + sem_V(chan_mutex); + return -1; + } + + /* So far: successfully destroyed rblock, sblock and send_serialiser */ + sem_destroy(backup->rblock); + sem_destroy(backup->sblock); + sem_destroy(backup->send_serialiser); + free((void*)backup); + free((void*)c); + sem_V(chan_mutex); + return 1; +} + +/************************************************************************ + * INTERFACE: chan_send() * + * * + * Send a message on a channel. * + * This will involve, first, attaching the given int 'message' to the * + * channel, then telling chan_receive() that we have a message to send * + * (by calling sem_V() on one sem), and then waiting (by calling sem_P()* + * on the other sem) until chan_receive() has taken the message and * + * told us we can proceed. We can use the send_serialiser semaphore to * + * ensure that concurrent calls to chan_send() are serialised. * + ************************************************************************/ +void +chan_send(Chan* c, int sentdata) +{ + sem_P(c->send_serialiser); + c->data = sentdata; + sem_V(c->rblock); + sem_P(c->sblock); + sem_V(c->send_serialiser); +} + +/************************************************************************ + * INTERFACE: chan_receive() * + * * + * Receive a message on a channel. * + * This will involve, first, waiting on one semaphore (using sem_P()) * + * until chan_send() has given us something to receive; then making * + * receiveddata point at the message in the channel; then * + * telling chan_send() that it may proceed (using sem_V() on the other * + * semaphore). * + ************************************************************************/ +void +chan_receive(Chan* c, int* receiveddata) +{ + sem_P(c->rblock); + *receiveddata = c->data; + sem_V(c->sblock); +} + +/* end file: chan.c */ diff --git a/chan.h b/chan.h new file mode 100755 index 0000000..a5b1fd3 --- /dev/null +++ b/chan.h @@ -0,0 +1,22 @@ +/* file: chan.h -- public interface to message passingfunctions. */ + +#ifndef CHAN_DEFINED +#define CHAN_DEFINED + +#include "sem.h" +#include "thread.h" + +typedef struct { + Sem *rblock; /* receivers block on this */ + Sem *sblock; /* senders block on this */ + Sem *send_serialiser; /* to prevent concurrent senders clashing */ + int data; +} Chan; + +Chan *chan_create(void); +int chan_destroy(Chan *chan); +void chan_send(Chan *chan, int sentdata); +void chan_receive(Chan *chan, int *receiveddata); + +#endif +/* end file: chan.h */ diff --git a/queue.c b/queue.c new file mode 100755 index 0000000..62c0e1a --- /dev/null +++ b/queue.c @@ -0,0 +1,79 @@ +/************************************************************************ + * * + * file: queue.c * + * * + * Module implementing queue manipulation functions. The module can * + * hold structs of any type as long as the user of the module * + * does the appropriate casting -- all it assumes is that the first * + * member of the struct is a pointer, 'next', to a struct. * + * * + ************************************************************************/ + +#include "queue.h" +#include + +/************************************************************************ + * queue_init() * + * * + * Initialise a Queue struct. * + * We assume this will never fail - so always return 1 for success. * + ************************************************************************/ +int +queue_init(Queue* q) +{ + q->front = q->back = (Qitem*)0; + return 1; +} + +/************************************************************************ + * queue_empty() * + * * + * Return TRUE (i.e. non-zero) iff queue is empty; else FALSE (i.e. 0). * + ************************************************************************/ +int +queue_empty(Queue* q) +{ + return q->front == (Qitem*)0; +} + +/************************************************************************ + * queue_put() * + * * + * Add a new Qitem to the back of the specified queue. * + * Make sure the next pointer of the new Qitem is 0! * + * We assume the given Qitem can be used directly by us and does not * + * need to be copied. * + ************************************************************************/ +void +queue_put(Queue* q, Qitem* new_item) +{ + new_item->next = (Qitem*)0; + if (queue_empty(q)) + q->front = new_item; + else + q->back->next = new_item; + q->back = new_item; +} + +/************************************************************************ + * queue_get() * + * * + * Remove the front Qitem struct from the specified queue and return * + * a pointer to it. When we remove and return the struct we pass all * + * future responsibility for it to our caller. Return (Qitem *)0 if * + * queue is empty. * + ************************************************************************/ +Qitem* +queue_get(Queue* q) +{ + Qitem* p = q->front; + if (!queue_empty(q)) { + q->front = q->front->next; + if (q->front == (Qitem*)0) q->back = (Qitem*)0; + } + return p; +} + +/* end file: queue.c */ + + diff --git a/queue.h b/queue.h new file mode 100755 index 0000000..fc7021c --- /dev/null +++ b/queue.h @@ -0,0 +1,21 @@ +/* file: queue.h -- definitions for queue manipulation routines. */ + +#ifndef QUEUE_DEFINED +#define QUEUE_DEFINED + +typedef struct qitem { + struct qitem *next; /* this must always be first; can cast to any struct with this first */ +} Qitem; + +typedef struct queue { + Qitem *front, *back; +} Queue; + +int queue_init(Queue *q); +int queue_empty(Queue *q); +void queue_put(Queue *q, Qitem *new_item); +Qitem *queue_get(Queue *q); + +#endif +/* end file: queue.h */ + diff --git a/sem.c b/sem.c new file mode 100755 index 0000000..eee13a2 --- /dev/null +++ b/sem.c @@ -0,0 +1,103 @@ +/************************************************************************ + * * + * file: sem.c * + * * + * Module implementing semaphores. * + * * + * All routines should call thread_yield() where reasonable to encourage* + * fair execution. * + * * + * Note that our implementation assumes that threads calling us are not * + * subject to arbitrary preemption - if they were, we would need to * + * add a protecting spin lock to our semaphore implementation. * + * * + ************************************************************************/ + +#include "sem.h" +#include +#include + +/* External stuff defined in thread.c. + */ +extern Queue ready_queue; +extern int thread_block_and_switch(Queue* q); + +/************************************************************************ + * INTERFACE: sem_create() * + * * + * Make and return a semaphore (i.e. return a pointer to a Sem struct). * + * This will involve allocating a Sem structure using malloc(), * + * creating/initialising a queue for threads blocked on the * + * semaphore and also initialising the semaphore to the value specified * + * by our caller. This value must be validated to be >= 0. The * + * function can only fail if malloc() fails - in this case we return * + * (Sem *)0. * + ************************************************************************/ +Sem* sem_create(int val) +{ + Sem* s; + + if (val < 0 || ((s = (Sem*)malloc(sizeof(Sem))) == (Sem*)0)) + return (Sem*)0; + + s->val = val; + queue_init(&(s->queue)); + thread_yield(); + return s; +} + +/************************************************************************ + * INTERFACE: sem_destroy() * + * * + * Destroy a semaphore. * + * Forbid destruction (return -1) if the semaphore has waiting threads. * + * Otherwise return 1 for success. * + ************************************************************************/ +int +sem_destroy(Sem* s) +{ + if (!queue_empty(&(s->queue))) return -1; + free((void*)s); + return 1; +} + +/************************************************************************ + * INTERFACE: sem_P() * + * * + * Standard semaphore operation. * + * That is, decrement the value of the semaphore and if this takes * + * its value below 0 block the calling thread on the semaphore's queue * + * using thread_block_and_switch(). * + * We know that a semaphore value can't theoretically get less than 0, * + * but we internally (hidden from the semaphore user) use the negative * + * range of val to record how many threads are currently blocked on the * + * semaphore. * + ************************************************************************/ +void +sem_P(Sem* s) +{ + if (--s->val < 0) thread_block_and_switch(&(s->queue)); + else thread_yield(); +} + +/************************************************************************ + * INTERFACE: sem_V() * + * * + * Standard semaphore operation. * + * That is, increment the value of the semaphore. If there were threads * + * blocked on the semaphore (i.e. the value after incrementing the * + * semaphore is still <= 0), we get the first thread from the * + * semaphore's queue and put it on the thread package's ready queue * + * using queue_put(). * + * Because the sem module is solely responsible for managing a sem's * + * queue, we can guarantee that if (s->val <= 0) there is always an * + * item on the sem's queue. * + ************************************************************************/ +void +sem_V(Sem *s) +{ + if (++s->val <= 0) queue_put(&ready_queue, queue_get(&(s->queue))); + thread_yield(); +} + +/* end file: sem.c */ diff --git a/sem.h b/sem.h new file mode 100755 index 0000000..1d616bf --- /dev/null +++ b/sem.h @@ -0,0 +1,21 @@ +/* file: sem.h -- definitions for semaphores. */ + +#ifndef SEM_DEFINED +#define SEM_DEFINED + +/* Include public interface. */ +#include "queue.h" +#include "thread.h" + +typedef struct semtype { + int val; + Queue queue; +} Sem; + +Sem *sem_create(int val); +int sem_destroy(Sem *s); +void sem_P(Sem *s); +void sem_V(Sem *s); + +#endif +/* end file: sem.h */