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;
}

View File

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

View File

@@ -0,0 +1,38 @@
# Producer/Consumer with condition variable examples
CS3841 examples for producer/consumer that motivate the need for condition variables
* prod\_con1.c - Producer and consumer with unlimited sized buffer
Works with a single semaphore to count full slots when only a single producer and consumer is used
* prod\_con2.c - Producer and consumer with unlimited sized buffer
Shows a problem that can arise when multiple consumers are consuming elements from the shared space at the same time
With no controlled access between consumers, the number of elements in the shared space become incorrect
* prod\_con3.c - Producer and consumer with unlimited sized buffer
Fixes the problem when multiple producers and consumers modify a shared space by using a mutex lock to prevent threads from modifying the space at the same time
* prod\_con4.c - Producer and consumer with unlimited sized buffer
Shows a problem that can arise when a consumer wants to consume multiple items from the shared space. Since a mutex lock is used to lock out other threads, it is possible for a consumer to remove elements from an empty shared space
* prod\_con5.c - Producer and consumer with unlimited sized buffer
An attempt to fix the problem of a consumer removing from an empty shared space when more than one item is to be consumed by waiting in a while loop until enough elements are in the space
Unfortunately causes deadlock since the consumer is holding the the mutex lock while waiting for more items
* prod\_con6.c - Producer and consumer with unlimited sized buffer
An attempt to fix the problem of a consumer removing from an empty shared space when more than one item is to be consumed by waiting in a while loop until enough elements are in the space.
Attemps to fix the deadlock provlem in prod\_cons5.c by releasing the lock
Unfortunately the consumer releasing the lock in the while loop causes undefined behavior
* prod\_con7.c - Producer and consumer with unlimited sized buffer
An attempt to fix the problem of a consumer removing from an empty shared space when more than one item is to be consumed by releasing the lock on the shared space and then waiting on a semaphore until there are more items.
Unfortunately, there is know way for the consumer to get back into the "head of the line" after more items are produced. Another consumer can skip ahead
* prod\_con\_cond1.c - Producer and consumer with unlimited sized buffer
Uses a condition variable to ensure proper order of the consumers when they wait for more items to be consumed
Unfortunately, using a single condition variable without additional mutual exculsion does not maintain the consumers order in line
* prod\_con\_cond2.c - Producer and consumer with unlimited sized buffer
Uses a condition variable to ensure proper order of the consumers when they wait for more items to be consumed
Also uses a mutex to control the ordering of consumers
This example provides correct behavior when there are multiple producers and multiple consumers and each consumer orders multiple items.

View File

@@ -0,0 +1,95 @@
/*
* prod_con1.c - Producer and consumer with unlimited sized buffer
* Works with a single semaphore to count full slots
* when only a single producer and consumer is used
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define ELEMENTS 100
int count = 0;
// Semaphores for controlling access
sem_t fullCount;
// 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
count++;
printf("Producer %lu added, count: %d\n", pthread_self(), count);
// Signal the consumer that there something to consume
sem_post(&fullCount);
usleep(200);
}
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
if(count == 0) {
printf("ERROR: Customer %lu removed with no elements\n", pthread_self());
exit(EXIT_FAILURE);
}
count--;
usleep(50);
}
return NULL;
}
// Main thread
int main()
{
sem_init(&fullCount, 0, 0);
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);
printf("Final count is: %d, expected 0\n", count);
sem_destroy(&fullCount);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,108 @@
/*
* prod_con2.c - Producer and consumer with unlimited sized buffer
* Shows a problem that can arise when multiple consumers
* are consuming elements from the shared space at the same time
* With no controlled access between consumers, the number
* of elements in the shared space become incorrect
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define ELEMENTS 100
#define PRODUCER_COUNT 10
#define CONSUMER_COUNT 5
int count = 0;
// Semaphores for controlling access
sem_t fullCount;
// 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
count++;
printf("Producer %lu added, count: %d\n", pthread_self(), count);
// Signal the consumer that there something to consume
sem_post(&fullCount);
usleep(200);
}
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
if(count == 0) {
printf("ERROR: Customer %lu removed with no elements\n", pthread_self());
exit(EXIT_FAILURE);
}
count--;
usleep(50);
}
return NULL;
}
// Main thread
int main()
{
sem_init(&fullCount, 0, 0);
pthread_t prod[PRODUCER_COUNT];
pthread_t cons[CONSUMER_COUNT];
for(int i = 0; i < PRODUCER_COUNT; i++) {
if(pthread_create(&prod[i], NULL, producer, NULL) == -1) {
printf("COULD NOT CREATE PRODUCER\n");
exit(EXIT_FAILURE);
}
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
if(pthread_create(&cons[i], NULL, consumer, NULL) == -1) {
printf("COULD NOT CREATE CONSUMER\n");
exit(EXIT_FAILURE);
}
}
start = 1;
for(int i = 0; i < PRODUCER_COUNT; i++) {
pthread_join(prod[i], NULL);
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
pthread_join(cons[i], NULL);
}
int expected_final_count = PRODUCER_COUNT*ELEMENTS - CONSUMER_COUNT*ELEMENTS;
printf("Final count is: %d, expected %d\n", count, expected_final_count);
sem_destroy(&fullCount);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,116 @@
/*
* prod_con3.c - Producer and consumer with unlimited sized buffer
* Fixes the problem when multiple producers and
* consumers modify a shared space by using
* a mutex lock to prevent threads from modifying
* the space at the same time
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define ELEMENTS 100
#define PRODUCER_COUNT 10
#define CONSUMER_COUNT 5
int count = 0;
// Semaphores for controlling access
sem_t fullCount;
pthread_mutex_t element_mutex = PTHREAD_MUTEX_INITIALIZER;
// 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++) {
pthread_mutex_lock(&element_mutex);
// Add an element
count++;
printf("Producer %lu added, count: %d\n", pthread_self(), count);
// Signal the consumer that there something to consume
sem_post(&fullCount);
pthread_mutex_unlock(&element_mutex);
usleep(200);
}
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
pthread_mutex_lock(&element_mutex);
if(count == 0) {
printf("ERROR: Customer %lu removed with no elements\n", pthread_self());
exit(EXIT_FAILURE);
}
count--;
pthread_mutex_unlock(&element_mutex);
usleep(50);
}
return NULL;
}
// Main thread
int main()
{
sem_init(&fullCount, 0, 0);
pthread_t prod[PRODUCER_COUNT];
pthread_t cons[CONSUMER_COUNT];
for(int i = 0; i < PRODUCER_COUNT; i++) {
if(pthread_create(&prod[i], NULL, producer, NULL) == -1) {
printf("COULD NOT CREATE PRODUCER\n");
exit(EXIT_FAILURE);
}
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
if(pthread_create(&cons[i], NULL, consumer, NULL) == -1) {
printf("COULD NOT CREATE CONSUMER\n");
exit(EXIT_FAILURE);
}
}
start = 1;
for(int i = 0; i < PRODUCER_COUNT; i++) {
pthread_join(prod[i], NULL);
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
pthread_join(cons[i], NULL);
}
int expected_final_count = PRODUCER_COUNT*ELEMENTS - CONSUMER_COUNT*ELEMENTS;
printf("Final count is: %d, expected %d\n", count, expected_final_count);
sem_destroy(&fullCount);
pthread_mutex_destroy(&element_mutex);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,119 @@
/*
* prod_con4.c - Producer and consumer with unlimited sized buffer
* Shows a problem that can arise when a consumer
* wants to consume multiple items from the shared
* space. Since a mutex lock is used to lock out
* other threads, it is possible for a consumer
* to remove elements from an empty shared space
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define ELEMENTS 100
#define PRODUCER_COUNT 10
#define CONSUMER_COUNT 5
#define ORDER_COUNT 2
int count = 0;
// Semaphores for controlling access
sem_t fullCount;
pthread_mutex_t element_mutex = PTHREAD_MUTEX_INITIALIZER;
// 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++) {
pthread_mutex_lock(&element_mutex);
// Add an element
count++;
printf("Producer %lu added, count: %d\n", pthread_self(), count);
// Signal the consumer that there something to consume
sem_post(&fullCount);
pthread_mutex_unlock(&element_mutex);
usleep(200);
}
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);
pthread_mutex_lock(&element_mutex);
for(int j = 0; j < ORDER_COUNT; j++) {
// Remove an element from the buffer
if(count == 0) {
printf("ERROR: Customer %lu removed with no elements\n", pthread_self());
exit(EXIT_FAILURE);
}
count--;
}
pthread_mutex_unlock(&element_mutex);
usleep(50);
}
return NULL;
}
// Main thread
int main()
{
sem_init(&fullCount, 0, 0);
pthread_t prod[PRODUCER_COUNT];
pthread_t cons[CONSUMER_COUNT];
for(int i = 0; i < PRODUCER_COUNT; i++) {
if(pthread_create(&prod[i], NULL, producer, NULL) == -1) {
printf("COULD NOT CREATE PRODUCER\n");
exit(EXIT_FAILURE);
}
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
if(pthread_create(&cons[i], NULL, consumer, NULL) == -1) {
printf("COULD NOT CREATE CONSUMER\n");
exit(EXIT_FAILURE);
}
}
start = 1;
for(int i = 0; i < PRODUCER_COUNT; i++) {
pthread_join(prod[i], NULL);
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
pthread_join(cons[i], NULL);
}
int expected_final_count = PRODUCER_COUNT*ELEMENTS - CONSUMER_COUNT*ELEMENTS*ORDER_COUNT;
printf("Final count is: %d, expected %d\n", count, expected_final_count);
sem_destroy(&fullCount);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,123 @@
/*
* prod_con5.c - Producer and consumer with unlimited sized buffer
* An attempt to fix the problem of a consumer removing
* from an empty shared space when more than one item
* is to be consumed by waiting in a while loop until
* enough elements are in the space
* Unfortunately causes deadlock since the consumer is
* holding the the mutex lock while waiting for more items
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define ELEMENTS 100
#define PRODUCER_COUNT 10
#define CONSUMER_COUNT 5
#define ORDER_COUNT 2
int count = 0;
// Semaphores for controlling access
sem_t fullCount;
pthread_mutex_t element_mutex = PTHREAD_MUTEX_INITIALIZER;
// 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++) {
pthread_mutex_lock(&element_mutex);
// Add an element
count++;
printf("Producer %lu added, count: %d\n", pthread_self(), count);
// Signal the consumer that there something to consume
sem_post(&fullCount);
pthread_mutex_unlock(&element_mutex);
usleep(200);
}
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);
pthread_mutex_lock(&element_mutex);
while(count < ORDER_COUNT);
for(int j = 0; j < ORDER_COUNT; j++) {
// Remove an element from the buffer
if(count == 0) {
printf("ERROR: Customer %lu removed with no elements\n", pthread_self());
exit(EXIT_FAILURE);
}
count--;
}
pthread_mutex_unlock(&element_mutex);
usleep(50);
}
return NULL;
}
// Main thread
int main()
{
sem_init(&fullCount, 0, 0);
pthread_t prod[PRODUCER_COUNT];
pthread_t cons[CONSUMER_COUNT];
for(int i = 0; i < PRODUCER_COUNT; i++) {
if(pthread_create(&prod[i], NULL, producer, NULL) == -1) {
printf("COULD NOT CREATE PRODUCER\n");
exit(EXIT_FAILURE);
}
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
if(pthread_create(&cons[i], NULL, consumer, NULL) == -1) {
printf("COULD NOT CREATE CONSUMER\n");
exit(EXIT_FAILURE);
}
}
start = 1;
for(int i = 0; i < PRODUCER_COUNT; i++) {
pthread_join(prod[i], NULL);
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
pthread_join(cons[i], NULL);
}
int expected_final_count = PRODUCER_COUNT*ELEMENTS - CONSUMER_COUNT*ELEMENTS;
printf("Final count is: %d, expected %d\n", count, expected_final_count);
sem_destroy(&fullCount);
pthread_mutex_destroy(&element_mutex);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,127 @@
/*
* prod_con6.c - Producer and consumer with unlimited sized buffer
* An attempt to fix the problem of a consumer removing
* from an empty shared space when more than one item
* is to be consumed by waiting in a while loop until
* enough elements are in the space. Attemps to fix the
* deadlock provlem in prod_cons5.c by releasing the lock
* Unfortunately the consumer releasing the lock in the
* while loop causes undefined behavior
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define ELEMENTS 100
#define PRODUCER_COUNT 10
#define CONSUMER_COUNT 5
#define ORDER_COUNT 2
int count = 0;
// Semaphores for controlling access
sem_t fullCount;
pthread_mutex_t element_mutex = PTHREAD_MUTEX_INITIALIZER;
// 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++) {
pthread_mutex_lock(&element_mutex);
// Add an element
count++;
printf("Producer %lu added, count: %d\n", pthread_self(), count);
// Signal the consumer that there something to consume
sem_post(&fullCount);
pthread_mutex_unlock(&element_mutex);
usleep(200);
}
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);
pthread_mutex_lock(&element_mutex);
while(count < ORDER_COUNT) {
pthread_mutex_unlock(&element_mutex);
}
pthread_mutex_lock(&element_mutex);
for(int j = 0; j < ORDER_COUNT; j++) {
// Remove an element from the buffer
if(count == 0) {
printf("ERROR: Customer %lu removed with no elements\n", pthread_self());
exit(EXIT_FAILURE);
}
count--;
}
pthread_mutex_unlock(&element_mutex);
usleep(50);
}
return NULL;
}
// Main thread
int main()
{
sem_init(&fullCount, 0, 0);
pthread_t prod[PRODUCER_COUNT];
pthread_t cons[CONSUMER_COUNT];
for(int i = 0; i < PRODUCER_COUNT; i++) {
if(pthread_create(&prod[i], NULL, producer, NULL) == -1) {
printf("COULD NOT CREATE PRODUCER\n");
exit(EXIT_FAILURE);
}
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
if(pthread_create(&cons[i], NULL, consumer, NULL) == -1) {
printf("COULD NOT CREATE CONSUMER\n");
exit(EXIT_FAILURE);
}
}
start = 1;
for(int i = 0; i < PRODUCER_COUNT; i++) {
pthread_join(prod[i], NULL);
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
pthread_join(cons[i], NULL);
}
int expected_final_count = PRODUCER_COUNT*ELEMENTS - CONSUMER_COUNT*ELEMENTS;
printf("Final count is: %d, expected %d\n", count, expected_final_count);
sem_destroy(&fullCount);
pthread_mutex_destroy(&element_mutex);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,131 @@
/*
* prod_con7.c - Producer and consumer with unlimited sized buffer
* An attempt to fix the problem of a consumer removing
* from an empty shared space when more than one item
* is to be consumed by releasing the lock on the shared
* space and then waiting on a semaphore until there
* are more items.
* Unfortunately, there is know way for the consumer
* to get back into the "head of the line" after more
* items are produced. Another consumer can skip ahead
* and take the items.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#define ELEMENTS 100
#define PRODUCER_COUNT 10
#define CONSUMER_COUNT 5
#define ORDER_COUNT 2
int count = 0;
// Semaphores for controlling access
sem_t fullCount;
pthread_mutex_t element_mutex = PTHREAD_MUTEX_INITIALIZER;
// 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++) {
pthread_mutex_lock(&element_mutex);
// Add an element
count++;
printf("Producer %lu added, count: %d\n", pthread_self(), count);
// Signal the consumer that there something to consume
sem_post(&fullCount);
pthread_mutex_unlock(&element_mutex);
usleep(200);
}
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);
pthread_mutex_lock(&element_mutex);
for(int j = 0; j < ORDER_COUNT; j++) {
if(count == 0) {
pthread_mutex_unlock(&element_mutex);
sem_wait(&fullCount);
pthread_mutex_lock(&element_mutex);
}
// Remove an element from the buffer
if(count == 0) {
printf("ERROR: Customer %lu removed with no elements\n", pthread_self());
exit(EXIT_FAILURE);
}
count--;
}
pthread_mutex_unlock(&element_mutex);
usleep(50);
}
return NULL;
}
// Main thread
int main()
{
sem_init(&fullCount, 0, 0);
pthread_t prod[PRODUCER_COUNT];
pthread_t cons[CONSUMER_COUNT];
for(int i = 0; i < PRODUCER_COUNT; i++) {
if(pthread_create(&prod[i], NULL, producer, NULL) == -1) {
printf("COULD NOT CREATE PRODUCER\n");
exit(EXIT_FAILURE);
}
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
if(pthread_create(&cons[i], NULL, consumer, NULL) == -1) {
printf("COULD NOT CREATE CONSUMER\n");
exit(EXIT_FAILURE);
}
}
start = 1;
for(int i = 0; i < PRODUCER_COUNT; i++) {
pthread_join(prod[i], NULL);
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
pthread_join(cons[i], NULL);
}
int expected_final_count = PRODUCER_COUNT*ELEMENTS - CONSUMER_COUNT*ELEMENTS;
printf("Final count is: %d, expected %d\n", count, expected_final_count);
sem_destroy(&fullCount);
pthread_mutex_destroy(&element_mutex);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,121 @@
/*
* prod_con_cond1.c - Producer and consumer with unlimited sized buffer
* Uses a condition variable to ensure proper
* order of the consumers when they wait for more
* items to be consumed.
* Unfortunately, using a single condition variable
* without additional mutual exculsion does not maintain
* the consumers order in line
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define ELEMENTS 100
#define PRODUCER_COUNT 10
#define CONSUMER_COUNT 5
#define ORDER_COUNT 2
int count = 0;
pthread_mutex_t element_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_elements = PTHREAD_COND_INITIALIZER;
// 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++) {
pthread_mutex_lock(&element_mutex);
// Add an element
count++;
printf("Producer %lu added, count: %d\n", pthread_self(), count);
// Signal the consumer that there is something to consume
pthread_cond_signal(&has_elements);
pthread_mutex_unlock(&element_mutex);
usleep(200);
}
return NULL;
}
// Consumer Routine
void* consumer()
{
// wait for start from master thread
while(!start);
for(int i = 0; i < ELEMENTS; i++) {
pthread_mutex_lock(&element_mutex);
for(int j = 0; j < ORDER_COUNT; j++) {
// Wait for element to consume
if(count == 0) {
pthread_cond_wait(&has_elements, &element_mutex);
}
// Remove an element
if(count == 0) {
printf("ERROR: Customer %lu removed with no elements\n", pthread_self());
exit(EXIT_FAILURE);
}
count--;
}
pthread_mutex_unlock(&element_mutex);
usleep(50);
}
return NULL;
}
// Main thread
int main()
{
pthread_t prod[PRODUCER_COUNT];
pthread_t cons[CONSUMER_COUNT];
for(int i = 0; i < PRODUCER_COUNT; i++) {
if(pthread_create(&prod[i], NULL, producer, NULL) == -1) {
printf("COULD NOT CREATE PRODUCER\n");
exit(EXIT_FAILURE);
}
}
for(int i = 0; i < 5; i++) {
if(pthread_create(&cons[i], NULL, consumer, NULL) == -1) {
printf("COULD NOT CREATE CONSUMER\n");
exit(EXIT_FAILURE);
}
}
start = 1;
for(int i = 0; i < PRODUCER_COUNT; i++) {
pthread_join(prod[i], NULL);
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
pthread_join(cons[i], NULL);
}
printf("Final count is: %d\n", count);
pthread_cond_destroy(&has_elements);
pthread_mutex_destroy(&element_mutex);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,133 @@
/*
* prod_con_cond2.c - Producer and consumer with unlimited sized buffer
* Uses a condition variable to ensure proper
* order of the consumers when they wait for more
* items to be consumed.
* Also uses a mutex to control the ordering of consumers
* This example provides correct behavior when there
* are multiple producers and multiple consumers and each
* consumer orders multiple items.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define ELEMENTS 100
#define PRODUCER_COUNT 10
#define CONSUMER_COUNT 5
#define ORDER_COUNT 2
int count = 0;
pthread_mutex_t consumer_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t element_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t has_elements = PTHREAD_COND_INITIALIZER;
// 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++) {
pthread_mutex_lock(&element_mutex);
// Add an element
count++;
printf("Producer %lu added, count: %d\n", pthread_self(), count);
// Signal the consumer that there is something to consume
pthread_cond_signal(&has_elements);
pthread_mutex_unlock(&element_mutex);
usleep(200);
}
return NULL;
}
// Consumer Routine
void* consumer()
{
// wait for start from master thread
while(!start);
for(int i = 0; i < ELEMENTS; i++) {
// All consumers line up in order
pthread_mutex_lock(&consumer_mutex);
// Only one consumer gets to be at the head of the line
pthread_mutex_lock(&element_mutex);
for(int j = 0; j < ORDER_COUNT; j++) {
// Wait for element to consume
if(count == 0) {
pthread_cond_wait(&has_elements, &element_mutex);
}
// Remove an element
if(count == 0) {
printf("ERROR: Customer %lu removed with no elements\n", pthread_self());
exit(EXIT_FAILURE);
}
count--;
}
// Let more consumers and producers run
pthread_mutex_unlock(&element_mutex);
// Let the next consumer at the head of the line order items
pthread_mutex_unlock(&consumer_mutex);
usleep(50);
}
return NULL;
}
// Main thread
int main()
{
pthread_t prod[PRODUCER_COUNT];
pthread_t cons[CONSUMER_COUNT];
for(int i = 0; i < PRODUCER_COUNT; i++) {
if(pthread_create(&prod[i], NULL, producer, NULL) == -1) {
printf("COULD NOT CREATE PRODUCER\n");
exit(EXIT_FAILURE);
}
}
for(int i = 0; i < 5; i++) {
if(pthread_create(&cons[i], NULL, consumer, NULL) == -1) {
printf("COULD NOT CREATE CONSUMER\n");
exit(EXIT_FAILURE);
}
}
start = 1;
for(int i = 0; i < PRODUCER_COUNT; i++) {
pthread_join(prod[i], NULL);
}
for(int i = 0; i < CONSUMER_COUNT; i++) {
pthread_join(cons[i], NULL);
}
printf("Final count is: %d\n", count);
pthread_cond_destroy(&has_elements);
pthread_mutex_destroy(&element_mutex);
pthread_mutex_destroy(&consumer_mutex);
return EXIT_SUCCESS;
}

Binary file not shown.

View File

@@ -0,0 +1,39 @@
// C Program for Message Queue (Reader Process)
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
// structure for message queue
struct mesg_buffer
{
long mesg_type;
char mesg_text[100];
} message;
int main()
{
key_t key;
int msgid;
// ftok to generate unique key
key = ftok("progfile", 65);
// msgget creates a message queue
// and returns identifier
msgid = msgget(key, 0666 | IPC_CREAT);
// msgrcv to receive message
msgrcv(msgid, &message, sizeof(message), 0, 0);
printf("Data Received is : %s \n", message.mesg_text);
msgrcv(msgid, &message, sizeof(message), 0, 0);
printf("Data Received is : %s \n", message.mesg_text);
msgrcv(msgid, &message, sizeof(message), 0, 0);
printf("Data Received is : %s \n", message.mesg_text);
msgrcv(msgid, &message, sizeof(message), 0, 0);
printf("Data Received is : %s \n", message.mesg_text);
// to destroy the message queue
msgctl(msgid, IPC_RMID, NULL);
return 0;
}

View File

@@ -0,0 +1,46 @@
// C Program for Message Queue (Writer Process)
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX 10
// structure for message queue
struct mesg_buffer
{
long mesg_type;
char mesg_text[100];
} m1, m2, m3, m4;
int main()
{
key_t key;
int msgid;
// ftok to generate unique key
key = ftok("progfile", 65);
// msgget creates a message queue
// and returns identifier
msgid = msgget(key, 0666 | IPC_CREAT);
m1.mesg_type = 1;
m2.mesg_type = 2;
m3.mesg_type = 3;
m4.mesg_type = 4;
printf("Write Data : ");
fgets(m1.mesg_text, MAX, stdin);
fgets(m2.mesg_text, MAX, stdin);
fgets(m3.mesg_text, MAX, stdin);
fgets(m4.mesg_text, MAX, stdin);
// msgsnd to send message
msgsnd(msgid, &m1, sizeof(m1), 0);
msgsnd(msgid, &m2, sizeof(m2), 0);
msgsnd(msgid, &m3, sizeof(m3), 0);
msgsnd(msgid, &m4, sizeof(m4), 0);
// display the message
// printf("Data send is : %s \n", message.mesg_text);
return 0;
}

View File

@@ -3,12 +3,12 @@
* to send a signal from one process to another
*/
#include <signal.h> // needed for signal system call
#include <stdio.h> // needed for printf, perror
#include <stdlib.h> // needed for exit
#include <sys/types.h> // needed for pid_t
#include <sys/wait.h> // needed for wait system call
#include <unistd.h> // needed for fork, getpid, getppid, kill system calls
#include <stdlib.h> // needed for exit
#include <signal.h> // needed for signal system call
#include <stdio.h> // needed for printf, perror
void signal_handler(int sig)
{
@@ -23,12 +23,12 @@ int main()
pid_t pid = fork(); // fork into 2 processes
if(pid < 0) // error
if (pid < 0) // error
{
perror("fork");
exit(EXIT_FAILURE);
}
else if(pid == 0) // child
else if (pid == 0) // child
{
// Send a signal to the parent by its PID
pid_t parent_pid = getppid();

BIN
Examples/ipc/rec Executable file

Binary file not shown.

BIN
Examples/ipc/send Executable file

Binary file not shown.

View File

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

View File

@@ -0,0 +1,27 @@
# pthread examples
CS3841 examples using the pthread library to create POSIX threads
* pthreads1.c - Shows how to create and wait for single thread (beyond the main thread)
* pthreads2.c - Shows how to create and wait for multple threads (beyond the main thread)
Threads prints out its thread ID and process ID
Shows that threads share the same process ID but all have their own thread ID
* pthreads3.c - Shows how to pass parameter values to thread routines
* pthreads4.c - Shows how to pass parameter values to thread routines
The threads dereference the pointer passed to the thread routine to demonstrate that memory is shared between all threads
* pthreads5.c - Shows a problem that can happen with parameters passed to thread routines due to the fact that the parameter must be a pointer
The referenced location must be valid for the entire life of the thread
NOTE: Context switches between threads can happen when unexpected
* pthreads6.c - Shows the problems that can happen due to the fact that thread routine parameters are pointers to memory locations
Threads can easily affect other threads memory
NOTE: Context switches between threads can happen when unexpected
* pthreads\_race.c - Shows a "race condition" when multiple threads try to race to modify a global value
Due to unpredictable behavior in context switching the value of global\_value does not result in what is expected
* pthreads\_race\_atomic.c - Uses an atomic add instruction to prevent the race condition that can happen when multiple threads modify a global value at the same time

View File

@@ -0,0 +1,48 @@
/*
* pthreads1.c - Shows how to create and wait for single
* thread (beyond the main thread)
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int global_value = 10;
// thread_routine
// writes to a global variable - shows that
// global data is shared between threads
//
// NOTE: that parameters and return value are pointers
void* thread_routine()
{
for(int i = 0; i < 10; i++) {
printf("THREAD READS GLOBAL VALUE: %d\n", global_value);
global_value++;
sleep(1);
}
return NULL;
}
int main()
{
// Create a thread
pthread_t thr_id;
if(pthread_create(&thr_id, NULL, thread_routine, NULL) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
// Write to global data
for(int i = 0; i < 10; i++) {
printf("PARENT READS GLOBAL: %d\n", global_value);
sleep(1);
}
// Wait for thread to finish
pthread_join(thr_id, NULL);
// Return success
return 0;
}

View File

@@ -0,0 +1,52 @@
/*
* pthreads2.c - Shows how to create and wait for multple
* threads (beyond the main thread)
* Threads prints out its thread ID and process ID.
* Shows that threads share the same process ID
* but all have their own thread ID
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int global_value = 10;
// thread_routine
// writes to a global variable - shows that
// global data is shared between threads
//
// NOTE: that parameters and return value are pointers
void* thread_routine()
{
for(int i = 0; i < 10; i++) {
printf("%lu %d: THREAD READS GLOBAL VALUE: %d\n", pthread_self(), getpid(), global_value);
global_value++;
sleep(1);
}
return NULL;
}
int main()
{
// Create 5 threads
pthread_t thr_id[5];
for(int i = 0; i < 5; i++) {
if(pthread_create(&thr_id[i], NULL, thread_routine, NULL) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
}
// Wait for all threads to finish
for(int i = 0; i < 5; i++) {
pthread_join(thr_id[i], NULL);
}
// Print out final value of global data
printf("%lu %d PARENT READS GLOBAL: %d\n", pthread_self(), getpid(), global_value);
// Return success
return 0;
}

View File

@@ -0,0 +1,60 @@
/*
* pthreads3.c - Shows how to pass parameter values to thread routines
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// Function to check if a number is prime
// Returns 1 if prime, 0 if not prime
int is_prime(int v)
{
for(int i = 2; i < v; i++) {
if(v % i == 0) {
return 0;
}
}
return 1;
}
// Thread routine
// calls is_prime to check if the input argument is prime
//
// NOTE: that parameters and return value are pointers
void* thread_routine(void* args)
{
int* val = (int*)args;
if(is_prime(*val)) {
printf("THREAD %lu FOUND that %d is prime\n", pthread_self(), *val);
} else {
printf("THREAD %lu FOUND that %d is not prime\n", pthread_self(), *val);
}
return NULL;
}
int main()
{
int val1 = 2;
int val2 = 100;
// Create 2 threads, passing a different value to each
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);
}
// Wait for the threads to finish
pthread_join(thr1, NULL);
pthread_join(thr2, NULL);
// Return success
return 0;
}

View File

@@ -0,0 +1,72 @@
/*
* pthreads4.c - Shows how to pass parameter values to thread routines
* The threads dereference the pointer passed to the
* thread routine to demonstrate that memory is shared
* between all threads
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// Function to check if a number is prime
// Returns 1 if prime, 0 if not prime
int is_prime(int v)
{
for(int i = 2; i < v; i++) {
if(v % i == 0) {
return 0;
}
}
return 1;
}
// Thread routine
// calls is_prime to check if the input argument is prime
// Modifies the input parameter by dereferencing the pointer
//
// NOTE: that parameters and return value are pointers
void* thread_routine(void* args)
{
int* val = (int*)args;
if(is_prime(*val)) {
printf("THREAD %lu FOUND that %d is prime\n", pthread_self(), *val);
*val = 1;
} else {
printf("THREAD %lu FOUND that %d is not prime\n", pthread_self(), *val);
*val = 0;
}
return NULL;
}
int main()
{
int val1 = 2;
int val2 = 100;
// Print out the values before creating threads
printf("Parent val1=%d val2=%d\n", val1, val2);
// Create 2 threads
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);
}
// Wait for the threads to finish
pthread_join(thr1, NULL);
pthread_join(thr2, NULL);
// Print out the final values
printf("Parent val1=%d val2=%d\n", val1, val2);
// Return success
return 0;
}

View File

@@ -0,0 +1,66 @@
/*
* pthreads5.c - Shows a problem that can happen with parameters
* passed to thread routines due to the fact that
* the parameter must be a pointer. The referenced
* location must be valid for the entire life of the
* thread.
*
* NOTE: Context switches between threads can happen when unexpected
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// Function to check if a number is prime
// Returns 1 if prime, 0 if not prime
int is_prime(int v)
{
for(int i = 2; i < v; i++) {
if(v % i == 0) {
return 0;
}
}
return 1;
}
// Thread routine
// calls is_prime to check if the input argument is prime
//
// NOTE: that parameters and return value are pointers
void* thread_routine(void* args)
{
int* val = (int*)args;
if(is_prime(*val)) {
printf("THREAD %lu FOUND that %d is prime\n", pthread_self(), *val);
} else {
printf("THREAD %lu FOUND that %d is not prime\n", pthread_self(), *val);
}
return NULL;
}
// start_thread
// Creates a new thread and returns the thread ID
//
// NOTE: the parameter to the thread is created on the stack
// the address becomes invalid when the function returns
pthread_t start_thread(int val)
{
pthread_t thr;
printf("Before thread creation val=%d\n", val);
if(pthread_create(&thr, NULL, thread_routine, (void*)&val) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
return thr;
}
int main()
{
pthread_t thr1 = start_thread(2);
pthread_t thr2 = start_thread(100);
pthread_join(thr1, NULL);
pthread_join(thr2, NULL);
return 0;
}

View File

@@ -0,0 +1,68 @@
/*
* pthreads6.c - Shows the problems that can happen due to the fact
* that thread routine parameters are pointers to memory
* locations. Threads can easily affect other threads
* memory.
*
* NOTE: Context switches between threads can happen when unexpected
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// Function to check if a number is prime
// Returns 1 if prime, 0 if not prime
int is_prime(int v)
{
for(int i = 2; i < v; i++) {
if(v % i == 0) {
return 0;
}
}
return 1;
}
// Thread routine
// calls is_prime to check if the input argument is prime
// Modifies the input parameter by dereferencing the pointer
//
// NOTE: that parameters and return value are pointers
void* thread_routine(void* args)
{
int* val = (int*)args;
if(is_prime(*val)) {
printf("THREAD %lu FOUND that %d is prime %p\n", pthread_self(), *val, val);
*val = 1;
} else {
printf("THREAD %lu FOUND that %d is not prime %p\n", pthread_self(), *val, val);
*val = 0;
}
return NULL;
}
int main()
{
int val = 0;
// Create 10 threads
pthread_t thr[10];
for(int i = 0; i < 10; i++) {
// NOTE: there is a single memory location for val which is passed to all threads
val = i + 2;
if(pthread_create(&thr[i], NULL, thread_routine, (void*)&val) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
}
// Wait for threads to finish
for(int i = 0; i < 10; i++) {
pthread_join(thr[i], NULL);
}
// Return success
return 0;
}

View File

@@ -0,0 +1,46 @@
/*
* pthreads_race.c - Shows a "race condition" when multiple threads
* try to race to modify a global value
* Due to unpredictable behavior in context switching
* the value of global_value does not result in what is
* expected
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int global_value = 0;
void* thread_routine()
{
for(int i = 0; i < 100000000; i++) {
global_value++;
}
return NULL;
}
int main()
{
// Create 5 threads
pthread_t thr_id[5];
for(int i = 0; i < 5; i++) {
if(pthread_create(&thr_id[i], NULL, thread_routine, NULL) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
}
// Wait for all threads to finish
for(int i = 0; i < 5; i++) {
pthread_join(thr_id[i], NULL);
}
// Print out final value of global value
// 5 threads * 100000000 iterations - expected value is 500000000
printf("PARENT READS GLOBAL: %d\n", global_value);
// Return success
return 0;
}

View File

@@ -0,0 +1,46 @@
/*
* pthreads_race_atomic.c - Uses an atomic add instruction to prevent
* the race condition that can happen when
* multiple threads modify a global value at the
* same time.
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdatomic.h>
atomic_int global_value = 0;
void* thread_routine()
{
for(int i = 0; i < 100000000; i++) {
atomic_fetch_add(&global_value, 1);
}
return NULL;
}
int main()
{
// Create 5 threads
pthread_t thr_id[5];
for(int i = 0; i < 5; i++) {
if(pthread_create(&thr_id[i], NULL, thread_routine, NULL) == -1) {
printf("COULD NOT CREATE A THREAD\n");
exit(EXIT_FAILURE);
}
}
// Wait for all threads to finish
for(int i = 0; i < 5; i++) {
pthread_join(thr_id[i], NULL);
}
// Print out final value of global value
// 5 threads * 100000000 iterations - expected value is 500000000
printf("PARENT READS GLOBAL: %d\n", global_value);
// Return success
return 0;
}