This commit is contained in:
p-w-rs
2022-09-21 12:04:48 -05:00
parent 9e1ad48346
commit 6d881b3edd
75 changed files with 3681 additions and 113 deletions

View File

@@ -0,0 +1,12 @@
CC=gcc
CFLAGS=-O0 -Wall -Wextra -g -pthread
BINS=$(patsubst %.c,%,$(wildcard *.c))
all: $(BINS)
clean:
rm -f $(BINS)
%: %.c
$(CC) $(CFLAGS) -o $@ $<

View File

@@ -0,0 +1,57 @@
# Concurrency examples
CS3841 examples using concurrency techniques
Examples with file names con\*.c show the progression of problems that can arise by trying to perform mutual exclusion ourselves without OS support.
Other examples show in sem\*.c and mutex\*.c show how the OS can help out by providing concurrency mechanisms.
* race.c - Shows the race condition that can happen then threads try to modify multiple global values at the same time
* con1.c - Concurrency using "taking turns"
Provides mutual exclusion but does not work for more than 2 processes
Also does not work if a process does not want to access the critical section
* con2.c - Concurrency using flag array - version 1
Allows threads to specify desire for critical secgion
Due to race conditions with the flag array, it cannot guarentee mutual exclusion
* con3.c - Concurrency using flag array - version 2
Move setting of the flag before the wait
Allows threads to specify desire for critical secgion
Due to race conditions with the flag array, it is prone to deadlock if both threads set the flag at the same time
* con4.c - Concurrency using flag array - version 3
Sets the critical section flag before the wait
Allows threads to specify desire for critical secgion
Adds deadlock detection and relase of the flag if both threads have their flag set
Prone to livelock since threads might be constantly releasing their flag
* con\_dekkers.c - Concurrency algorithm proposed by Theodorus Jozef Dekker
Combines the deadlock detection of cons4 with the taking turns algorithm in cons1
Ensures mutual exclusion along with deadlock/livelock prevention, however needs to know how many participants there are
Also does not work on modern CPUs that perform out-of-order execution
* sem1.c - Uses semaphores to control access to the critical section
* sem2.c - Shows that semaphores are NOT locks, but a signalling mechanism. Multiple posts to a semaphore incremenet the count regardless of the current value
* sem\_list1.c - Shows what can happen as a result of uncontrolled access to a singly linked list
* sem\_list2.c - Uses a semaphore as a way to control access to a critical section for a singly linked list
* mutex1.c - Uses a mutex lock to control access to a critical section that increments two global variables
* mutex2.c - Shows the undefined behavior that can happen when a thread attempts to unlock a mutex that's already unlocked
* mutex3.c - Shows the deadlock behavior that can happen when a thread attempts to lock a mutex that it has already locked
* prod\_cons1.c - Shows the producer and consumer problem
When access to a limited shared space is not controlled there are chances where a producer thread might attempt to write when the space is full and a consumer might attempt to read when the space is empty
* prod\_cons2.c - Shows the producer and consumer problem
Access is controlled using semaphores
One semaphore counts the number of filled spots
One semaphore counts the number of empty spots
Producers wait for at least one free spot before writing
Consusmers wait for at least one filled spot before reading

View File

@@ -0,0 +1,75 @@
/*
* con1.c - Concurrency using "taking turns"
* Provides mutual exclusion but does not work for more than 2 processes
* Also does not work if a process does not want to access the critical section
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
// shared global
static int counter1 = 0;
static int counter2 = 0;
// start flag
volatile int start = 0;
// turn flag
volatile int turn = 0;
void* thread_routine(void* args)
{
int me = *((int *)args);
int you = me ? 0 : 1;
printf("Worker thread: %d ready, you are %d\n", me, you);
// wait for start from master thread
while(!start);
for (int j = 0; j < 1000; j++)
{
while(turn != me)
{
/* do nothing */
}
// this is the critical section
counter1++;
counter2++;
// leaving critical section
turn = you;
}
printf("Worker thread: %d done\n", me);
return NULL;
}
int main()
{
int val1 = 0;
int val2 = 1;
pthread_t thr1;
pthread_t thr2;
if(pthread_create(&thr1, NULL, thread_routine, (void*)&val1) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
if(pthread_create(&thr2, NULL, thread_routine, (void*)&val2) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
start = 1;
pthread_join(thr1,NULL);
pthread_join(thr2,NULL);
printf("counter1: %d\n",counter1);
printf("counter2: %d\n",counter2);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,77 @@
/*
* con2.c - Concurrency using flag array - version 1
* Allows threads to specify desire for critical secgion
* Due to race conditions with the flag array, it cannot guarentee mutual exclusion
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
// shared global
static int counter1 = 0;
static int counter2 = 0;
// start flag
volatile int start = 0;
// flag array
volatile int flag[2] = {0,0};
void* thread_routine(void* args)
{
int me = *((int *) args);
int you = me ? 0 : 1;
printf("Worker thread: %d ready, you are %d\n",me,you);
// wait for start from master thread
while(!start);
for (int j = 0; j < 100000000; j++)
{
while(flag[you])
{
/* do nothing */
}
flag[me] = 1;
// this is the critical section
counter1++;
counter2++;
// leaving critical section
flag[me] = 0;
}
printf("Worker thread: %d done\n",me);
return NULL;
}
int main()
{
int val1 = 0;
int val2 = 1;
pthread_t thr1;
pthread_t thr2;
if(pthread_create(&thr1, NULL, thread_routine, (void*)&val1) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
if(pthread_create(&thr2, NULL, thread_routine, (void*)&val2) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
start = 1;
pthread_join(thr1,NULL);
pthread_join(thr2,NULL);
printf("counter1: %d\n",counter1);
printf("counter2: %d\n",counter2);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,78 @@
/*
* con3.c - Concurrency using flag array - version 2
* Move setting of the flag before the wait
* Allows threads to specify desire for critical secgion
* Due to race conditions with the flag array, it is prone to deadlock
* if both threads set the flag at the same time
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
// shared global
static int counter1 = 0;
static int counter2 = 0;
// start flag
volatile int start = 0;
// flag array
volatile int flag[2] = {0,0};
void* thread_routine(void* args)
{
int me = *((int *) args);
int you = me ? 0 : 1;
printf("Worker thread: %d ready, you are %d\n",me,you);
// wait for start from master thread
while(!start);
for (int j = 0; j < 100000000; j++)
{
flag[me]=1;
while(flag[you])
{
/* do nothing */
}
// this is the critical section
counter1++;
counter2++;
// leaving critical section
flag[me]=0;
}
printf("Worker thread: %d done\n",me);
return NULL;
}
int main()
{
int val1 = 0;
int val2 = 1;
pthread_t thr1;
pthread_t thr2;
if(pthread_create(&thr1, NULL, thread_routine, (void*)&val1) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
if(pthread_create(&thr2, NULL, thread_routine, (void*)&val2) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
start = 1;
pthread_join(thr1,NULL);
pthread_join(thr2,NULL);
printf("counter1: %d\n",counter1);
printf("counter2: %d\n",counter2);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,84 @@
/*
* con4.c - Concurrency using flag array - version 3
* Sets the critical section flag before the wait
* Allows threads to specify desire for critical secgion
* Adds deadlock detection and relase of the flag if both
* threads have their flag set
* Prone to livelock since threads might be constantly releasing their flag
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
// shared global
static int counter1 = 0;
static int counter2 = 0;
// start flag
volatile int start = 0;
// flag array
volatile int flag[2] = {0,0};
void* thread_routine(void* args)
{
int me = *((int *) args);
int you = me ? 0 : 1;
printf("Worker thread: %d ready, you are %d\n",me,you);
// wait for start from master thread
while(!start);
for (int j = 0; j < 1000000; j++)
{
flag[me] = 1;
while(flag[you])
{
// just in case there is deadlock
flag[me] = 0;
usleep(1); // enought time to allow other thread to run
flag[me] = 1;
}
// this is the critical section
counter1++;
counter2++;
// leaving critical section
flag[me] = 0;
}
printf("Worker thread: %d done\n",me);
return NULL;
}
int main()
{
int val1 = 0;
int val2 = 1;
pthread_t thr1;
pthread_t thr2;
if(pthread_create(&thr1, NULL, thread_routine, (void*)&val1) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
if(pthread_create(&thr2, NULL, thread_routine, (void*)&val2) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
start = 1;
pthread_join(thr1,NULL);
pthread_join(thr2,NULL);
printf("counter1: %d\n",counter1);
printf("counter2: %d\n",counter2);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,91 @@
/*
* con_dekkers.c - Concurrency algorithm proposed by Theodorus Jozef Dekker
* Combines the deadlock detection of cons4 with the
* taking turns algorithm in cons1
* Ensures mutual exclusion along with deadlock/livelock prevention
* However needs to know how many participants there are
* Also does not work on modern CPUs that perform out-of-order execution
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
// shared global
static int counter1 = 0;
static int counter2 = 0;
// start flag
volatile int start = 0;
// turn flag
volatile int turn = 0;
// flag array
volatile int flag[2] = {0,0};
void* thread_routine(void* args)
{
int me = *((int *) args);
int you = me ? 0 : 1;
printf("Worker thread: %d ready, you are %d\n",me,you);
// wait for start from master thread
while(!start);
for (int j = 0; j < 1000000; j++)
{
flag[me] = 1;
while(flag[you])
{
if(turn == you)
{
// just in case there is deadlock
flag[me] = 0;
}
while (turn == you);
flag[me] = 1;
}
// this is the critical section
counter1++;
counter2++;
// leaving critical section
turn = you;
flag[me] = 0;
}
printf("Worker thread: %d done\n",me);
return NULL;
}
int main()
{
int val1 = 0;
int val2 = 1;
pthread_t thr1;
pthread_t thr2;
if(pthread_create(&thr1, NULL, thread_routine, (void*)&val1) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
if(pthread_create(&thr2, NULL, thread_routine, (void*)&val2) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
start = 1;
pthread_join(thr1,NULL);
pthread_join(thr2,NULL);
printf("counter1: %d\n",counter1);
printf("counter2: %d\n",counter2);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,77 @@
/*
* mutex1.c - Uses a mutex lock to control access to a critical section
* that increments two global variables
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
// shared global
static int counter1 = 0;
static int counter2 = 0;
// start flag
volatile int start;
// flag mutex
pthread_mutex_t flag = PTHREAD_MUTEX_INITIALIZER;
// The thread process
void* thread_routine(void* args)
{
int me = *((int *) args);
int you = me ? 0 : 1;
printf("Worker thread: %d ready, you are %d\n",me,you);
// wait for start from master thread
while(!start);
for (int j = 0; j < 1000000; j++)
{
pthread_mutex_lock(&flag);
// this is the critical section
counter1++;
counter2++;
// leaving critical section
pthread_mutex_unlock(&flag);
}
printf("Worker thread: %d done\n",me);
return NULL;
}
// Main process
int main()
{
int val1 = 0;
int val2 = 1;
pthread_t thr1;
pthread_t thr2;
if(pthread_create(&thr1, NULL, thread_routine, (void*)&val1) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
if(pthread_create(&thr2, NULL, thread_routine, (void*)&val2) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
start = 1;
pthread_join(thr1,NULL);
pthread_join(thr2,NULL);
printf("counter1: %d\n",counter1);
printf("counter2: %d\n",counter2);
pthread_mutex_destroy(&flag);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,82 @@
/*
* mutex2.c - Shows the undefined behavior that can happen when
* a thread attempts to unlock a mutex that's already
* unlocked
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
// shared global
static int counter1 = 0;
static int counter2 = 0;
// start flag
volatile int start;
// flag mutex
pthread_mutex_t flag = PTHREAD_MUTEX_INITIALIZER;
// The thread process
void* thread_routine(void* args)
{
int me = *((int *) args);
int you = me ? 0 : 1;
printf("Worker thread: %d ready, you are %d\n",me,you);
// wait for start from master thread
while(!start);
for (int j = 0; j < 1000000; j++)
{
pthread_mutex_lock(&flag);
// this is the critical section
counter1++;
counter2++;
// leaving critical section
pthread_mutex_unlock(&flag);
if(me == 0) {
pthread_mutex_unlock(&flag);
}
}
printf("Worker thread: %d done\n",me);
return NULL;
}
// Main process
int main()
{
int val1 = 0;
int val2 = 1;
pthread_t thr1;
pthread_t thr2;
if(pthread_create(&thr1, NULL, thread_routine, (void*)&val1) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
if(pthread_create(&thr2, NULL, thread_routine, (void*)&val2) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
start = 1;
pthread_join(thr1,NULL);
pthread_join(thr2,NULL);
printf("counter1: %d\n",counter1);
printf("counter2: %d\n",counter2);
pthread_mutex_destroy(&flag);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,82 @@
/*
* mutex3.c - Shows the deadlock behavior that can happen when
* a thread attempts to lock a mutex that it has already
* locked
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
// shared global
static int counter1 = 0;
static int counter2 = 0;
// start flag
volatile int start;
// flag mutex
pthread_mutex_t flag = PTHREAD_MUTEX_INITIALIZER;
// The thread process
void* thread_routine(void* args)
{
int me = *((int *) args);
int you = me ? 0 : 1;
printf("Worker thread: %d ready, you are %d\n",me,you);
// wait for start from master thread
while(!start);
for (int j = 0; j < 1000000; j++)
{
pthread_mutex_lock(&flag);
if(me == 0) {
pthread_mutex_lock(&flag);
}
// this is the critical section
counter1++;
counter2++;
// leaving critical section
pthread_mutex_unlock(&flag);
}
printf("Worker thread: %d done\n",me);
return NULL;
}
// Main process
int main()
{
int val1 = 0;
int val2 = 1;
pthread_t thr1;
pthread_t thr2;
if(pthread_create(&thr1, NULL, thread_routine, (void*)&val1) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
if(pthread_create(&thr2, NULL, thread_routine, (void*)&val2) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
start = 1;
pthread_join(thr1,NULL);
pthread_join(thr2,NULL);
printf("counter1: %d\n",counter1);
printf("counter2: %d\n",counter2);
pthread_mutex_destroy(&flag);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,114 @@
/*
* prod_cons1.c - Shows the producer and consumer problem
* When access to a limited shared space is not
* controlled there are chances where a producer thread
* might attempt to write when the space is full and
* a consumer might attempt to read when the space is empty
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define BUFFER_SIZE 30
#define ELEMENTS 100
// Shared Buffer
typedef struct circular_buffer {
unsigned char values[BUFFER_SIZE];
int out_idx;
int in_idx;
} circular_buffer;
circular_buffer buffer;
void buffer_init(circular_buffer* b)
{
b->out_idx = 0;
b->in_idx = 0;
memset(b->values, 0, BUFFER_SIZE);
}
void buffer_insert(circular_buffer* b, unsigned char value)
{
if(b->values[b->in_idx] != 0) {
printf("ERROR: Inserting into buffer when an element exists. Empty was expected\n");
exit(EXIT_FAILURE);
}
b->values[b->in_idx] = value;
b->in_idx = b->in_idx < BUFFER_SIZE-1 ? b->in_idx + 1 : 0;
}
unsigned char buffer_remove(circular_buffer* b)
{
unsigned char return_value = b->values[b->out_idx];
if(return_value == 0) {
printf("ERROR: Removing from a buffer at an empty element. Something was expected\n");
exit(EXIT_FAILURE);
}
b->values[b->out_idx] = 0;
b->out_idx = b->out_idx < BUFFER_SIZE-1 ? b->out_idx + 1 : 0;
return return_value;
}
// Start flag
volatile int start = 0;
// Producer Routine
void* producer()
{
// wait for start from master thread
while(!start);
for(int i = 0; i < ELEMENTS; i++) {
// Add an element to the buffer
unsigned char value = (i % 100) + 1; // Make sure the value isn't zero
buffer_insert(&buffer, value);
printf("Producer added: %d\n", value);
}
return NULL;
}
// Consumer Routine
void* consumer()
{
// wait for start from master thread
while(!start);
for(int i = 0; i < ELEMENTS; i++) {
// Remove an element from the buffer
printf("Consumer removed: %d\n", buffer_remove(&buffer));
}
return NULL;
}
// Main process
int main()
{
buffer_init(&buffer);
pthread_t prod;
pthread_t cons;
if(pthread_create(&prod, NULL, producer, NULL) == -1) {
printf("COULD NOT CREATE PRODUCER\n");
exit(EXIT_FAILURE);
}
if(pthread_create(&cons, NULL, consumer, NULL) == -1) {
printf("COULD NOT CREATE CONSUMER\n");
exit(EXIT_FAILURE);
}
start = 1;
pthread_join(prod, NULL);
pthread_join(cons, NULL);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,136 @@
/*
* prod_cons1.c - Shows the producer and consumer problem
* Access is controlled using semaphores
* One semaphore counts the number of filled spots
* One semaphore counts the number of empty spots
* Producers wait for at least one free spot before writing
* Consusmers wait for at least one filled spot before reading
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define BUFFER_SIZE 30
#define ELEMENTS 100
// Shared Buffer
typedef struct circular_buffer {
unsigned char values[BUFFER_SIZE];
int out_idx;
int in_idx;
} circular_buffer;
circular_buffer buffer;
void buffer_init(circular_buffer* b)
{
b->out_idx = 0;
b->in_idx = 0;
memset(b->values, 0, BUFFER_SIZE);
}
void buffer_insert(circular_buffer* b, unsigned char value)
{
if(b->values[b->in_idx] != 0) {
printf("ERROR: Inserting into buffer when an element exists. Empty was expected\n");
exit(EXIT_FAILURE);
}
b->values[b->in_idx] = value;
b->in_idx = b->in_idx < BUFFER_SIZE-1 ? b->in_idx + 1 : 0;
}
unsigned char buffer_remove(circular_buffer* b)
{
unsigned char return_value = b->values[b->out_idx];
if(return_value == 0) {
printf("ERROR: Removing from a buffer at an empty element. Something was expected\n");
exit(EXIT_FAILURE);
}
b->values[b->out_idx] = 0;
b->out_idx = b->out_idx < BUFFER_SIZE-1 ? b->out_idx + 1 : 0;
return return_value;
}
// Semaphores for controlling access
sem_t fullCount;
sem_t emptyCount;
// Start flag
volatile int start = 0;
// Producer Routine
void* producer()
{
// wait for start from master thread
while(!start);
for(int i = 0; i < ELEMENTS; i++) {
// Wait for a free spot in the buffer
sem_wait(&emptyCount);
// Add an element to the buffer
unsigned char value = (i % 100) + 1; // Make sure the value isn't zero
buffer_insert(&buffer, value);
printf("Producer added: %d\n", value);
// Signal the consumer that there something to consume
sem_post(&fullCount);
}
return NULL;
}
// Consumer Routine
void* consumer()
{
// wait for start from master thread
while(!start);
for(int i = 0; i < ELEMENTS; i++) {
// Wait for element to consume
sem_wait(&fullCount);
// Remove an element from the buffer
printf("Consumer removed: %d\n", buffer_remove(&buffer));
// Signal the producer that there is a free spot
sem_post(&emptyCount);
}
return NULL;
}
// Main process
int main()
{
sem_init(&fullCount, 0, 0);
sem_init(&emptyCount, 0, BUFFER_SIZE);
buffer_init(&buffer);
pthread_t prod;
pthread_t cons;
if(pthread_create(&prod, NULL, producer, NULL) == -1) {
printf("COULD NOT CREATE PRODUCER\n");
exit(EXIT_FAILURE);
}
if(pthread_create(&cons, NULL, consumer, NULL) == -1) {
printf("COULD NOT CREATE CONSUMER\n");
exit(EXIT_FAILURE);
}
start = 1;
pthread_join(prod, NULL);
pthread_join(cons, NULL);
sem_destroy(&fullCount);
sem_destroy(&emptyCount);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,68 @@
/*
* race.c - Shows the race condition that can happen
* then threads try to modify multiple global
* values at the same time
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
// start flag
volatile int start = 0;
// shared global
static int counter1 = 0;
static int counter2 = 0;
void* thread_routine(void* args)
{
int me = *((int *)args);
int you = me ? 0 : 1;
printf("Worker thread: %d ready, you are %d\n", me, you);
// wait for start from master thread
while(!start);
for (int j = 0; j < 100000000; j++)
{
// this is the critical section
counter1++;
counter2++;
// leaving critical section
}
printf("Worker thread: %d done\n",me);
return NULL;
}
int main()
{
int val1 = 0;
int val2 = 1;
pthread_t thr1;
pthread_t thr2;
if(pthread_create(&thr1, NULL, thread_routine, (void*)&val1) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
if(pthread_create(&thr2, NULL, thread_routine, (void*)&val2) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
start = 1;
pthread_join(thr1,NULL);
pthread_join(thr2,NULL);
printf("counter1: %d\n",counter1);
printf("counter2: %d\n",counter2);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,82 @@
/*
* sem1 - Uses semaphores to control access to the
* critical section
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
// shared global
static int counter1 = 0;
static int counter2 = 0;
// start flag
volatile int start;
// flag semaphore
sem_t flag;
// The thread process
void* thread_routine(void* args)
{
int me = *((int *) args);
int you = me ? 0 : 1;
printf("Worker thread: %d ready, you are %d\n",me,you);
// wait for start from master thread
while(!start);
for (int j = 0; j < 1000000; j++)
{
sem_wait(&flag);
// this is the critical section
counter1++;
counter2++;
// leaving critical section
sem_post(&flag);
}
printf("Worker thread: %d done\n",me);
return NULL;
}
// Main process
int main()
{
int val1 = 0;
int val2 = 1;
// Initialize the semaphore - initial value of 1
sem_init(&flag, 0, 1);
pthread_t thr1;
pthread_t thr2;
if(pthread_create(&thr1, NULL, thread_routine, (void*)&val1) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
if(pthread_create(&thr2, NULL, thread_routine, (void*)&val2) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
start = 1;
pthread_join(thr1,NULL);
pthread_join(thr2,NULL);
printf("counter1: %d\n",counter1);
printf("counter2: %d\n",counter2);
// Destroy the semaphore
sem_destroy(&flag);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,85 @@
/*
* sem2.c - Shows that semaphores are NOT locks, but a signalling
* mechanism. Multiple posts to a semaphore incremenet
* the count regardless of the current value
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
// shared global
static int counter1 = 0;
static int counter2 = 0;
// start flag
volatile int start;
// flag semaphore
sem_t flag;
// The thread process
void* thread_routine(void* args)
{
int me = *((int *) args);
int you = me ? 0 : 1;
printf("Worker thread: %d ready, you are %d\n",me,you);
// wait for start from master thread
while(!start);
for (int j = 0; j < 100000000; j++)
{
sem_wait(&flag);
// this is the critical section
counter1++;
counter2++;
// leaving critical section
sem_post(&flag);
if(me == 0) {
sem_post(&flag);
}
}
printf("Worker thread: %d done\n",me);
return NULL;
}
// Main process
int main()
{
int val1 = 0;
int val2 = 1;
sem_init(&flag, 0, 1);
pthread_t thr1;
pthread_t thr2;
if(pthread_create(&thr1, NULL, thread_routine, (void*)&val1) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
if(pthread_create(&thr2, NULL, thread_routine, (void*)&val2) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
start = 1;
pthread_join(thr1,NULL);
pthread_join(thr2,NULL);
printf("counter1: %d\n",counter1);
printf("counter2: %d\n",counter2);
sem_destroy(&flag);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,90 @@
/*
* sem_list1.c - Shows what can happen as a result of
* uncontrolled access to a singly linked list
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
typedef struct node {
int val;
struct node* next;
} node;
typedef struct list {
node* head;
} list;
// shared global
static list mylist;
// start flag
volatile int start = 0;
// The thread process
void* thread_routine()
{
printf("Worker thread: %lu ready\n", pthread_self());
// wait for start from master thread
while(start == 0);
for (int j = 0; j < 1000000; j++)
{
node* new_node = malloc(sizeof(node));
new_node->val = j;
new_node->next = mylist.head;
mylist.head = new_node;
}
printf("Worker thread: %lu done\n", pthread_self());
return NULL;
}
// Main process
int main()
{
#define THREAD_COUNT 10
pthread_t thr_ids[THREAD_COUNT];
mylist.head = NULL;
// Create the threads
for(int i = 0; i < THREAD_COUNT; i++) {
if(pthread_create(&thr_ids[i], NULL, thread_routine, NULL) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
}
// Signal threads to start
start = 1;
// Wait for all threads to finish
for(int i = 0; i < THREAD_COUNT; i++) {
pthread_join(thr_ids[i],NULL);
}
// Count the elements in the list
int length = 0;
node* itr = mylist.head;
while(itr != NULL) {
length++;
itr = itr->next;
}
printf("List contains %d elements\n", length);
// Free the list
while(mylist.head != NULL) {
node* to_delete = mylist.head;
mylist.head = mylist.head->next;
free(to_delete);
}
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,99 @@
/*
* sem_list2.c - Uses a semaphore as a way to control access
* to a critical section for a singly linked list
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
typedef struct node {
int val;
struct node* next;
} node;
typedef struct list {
node* head;
} list;
// shared global
static list mylist;
// start flag
volatile int start = 0;
// flag semaphore
sem_t flag;
// The thread process
void* thread_routine()
{
printf("Worker thread: %lu ready\n", pthread_self());
// wait for start from master thread
while(start == 0);
for (int j = 0; j < 1000000; j++)
{
sem_wait(&flag);
node* new_node = malloc(sizeof(node));
new_node->val = j;
new_node->next = mylist.head;
mylist.head = new_node;
sem_post(&flag);
}
printf("Worker thread: %lu done\n", pthread_self());
return NULL;
}
// Main process
int main()
{
sem_init(&flag, 0, 1);
#define THREAD_COUNT 10
pthread_t thr_ids[THREAD_COUNT];
mylist.head = NULL;
// Create the threads
for(int i = 0; i < THREAD_COUNT; i++) {
if(pthread_create(&thr_ids[i], NULL, thread_routine, NULL) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
}
// Signal threads to start
start = 1;
// Wait for all threads to finish
for(int i = 0; i < THREAD_COUNT; i++) {
pthread_join(thr_ids[i],NULL);
}
// Count the elements in the list
int length = 0;
node* itr = mylist.head;
while(itr != NULL) {
length++;
itr = itr->next;
}
printf("List contains %d elements\n", length);
// Free the list
while(mylist.head != NULL) {
node* to_delete = mylist.head;
mylist.head = mylist.head->next;
free(to_delete);
}
sem_destroy(&flag);
return EXIT_SUCCESS;
}