stuff
This commit is contained in:
12
Examples/concurrency/Makefile
Normal file
12
Examples/concurrency/Makefile
Normal 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 $@ $<
|
||||
57
Examples/concurrency/README.md
Normal file
57
Examples/concurrency/README.md
Normal 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
|
||||
75
Examples/concurrency/con1.c
Normal file
75
Examples/concurrency/con1.c
Normal 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;
|
||||
}
|
||||
77
Examples/concurrency/con2.c
Normal file
77
Examples/concurrency/con2.c
Normal 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;
|
||||
}
|
||||
78
Examples/concurrency/con3.c
Normal file
78
Examples/concurrency/con3.c
Normal 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;
|
||||
}
|
||||
84
Examples/concurrency/con4.c
Normal file
84
Examples/concurrency/con4.c
Normal 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;
|
||||
}
|
||||
91
Examples/concurrency/con_dekkers.c
Normal file
91
Examples/concurrency/con_dekkers.c
Normal 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;
|
||||
}
|
||||
77
Examples/concurrency/mutex1.c
Normal file
77
Examples/concurrency/mutex1.c
Normal 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;
|
||||
}
|
||||
82
Examples/concurrency/mutex2.c
Normal file
82
Examples/concurrency/mutex2.c
Normal 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;
|
||||
}
|
||||
82
Examples/concurrency/mutex3.c
Normal file
82
Examples/concurrency/mutex3.c
Normal 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;
|
||||
}
|
||||
114
Examples/concurrency/prod_cons1.c
Normal file
114
Examples/concurrency/prod_cons1.c
Normal 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;
|
||||
}
|
||||
136
Examples/concurrency/prod_cons2.c
Normal file
136
Examples/concurrency/prod_cons2.c
Normal 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;
|
||||
}
|
||||
68
Examples/concurrency/race.c
Normal file
68
Examples/concurrency/race.c
Normal 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;
|
||||
}
|
||||
|
||||
82
Examples/concurrency/sem1.c
Normal file
82
Examples/concurrency/sem1.c
Normal 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;
|
||||
}
|
||||
85
Examples/concurrency/sem2.c
Normal file
85
Examples/concurrency/sem2.c
Normal 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;
|
||||
}
|
||||
90
Examples/concurrency/sem_list1.c
Normal file
90
Examples/concurrency/sem_list1.c
Normal 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;
|
||||
}
|
||||
99
Examples/concurrency/sem_list2.c
Normal file
99
Examples/concurrency/sem_list2.c
Normal 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;
|
||||
}
|
||||
12
Examples/conditions/Makefile
Normal file
12
Examples/conditions/Makefile
Normal 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 $@ $<
|
||||
38
Examples/conditions/README.md
Normal file
38
Examples/conditions/README.md
Normal 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.
|
||||
95
Examples/conditions/prod_con1.c
Normal file
95
Examples/conditions/prod_con1.c
Normal 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;
|
||||
}
|
||||
108
Examples/conditions/prod_con2.c
Normal file
108
Examples/conditions/prod_con2.c
Normal 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;
|
||||
}
|
||||
116
Examples/conditions/prod_con3.c
Normal file
116
Examples/conditions/prod_con3.c
Normal 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;
|
||||
}
|
||||
119
Examples/conditions/prod_con4.c
Normal file
119
Examples/conditions/prod_con4.c
Normal 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;
|
||||
}
|
||||
123
Examples/conditions/prod_con5.c
Normal file
123
Examples/conditions/prod_con5.c
Normal 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;
|
||||
}
|
||||
127
Examples/conditions/prod_con6.c
Normal file
127
Examples/conditions/prod_con6.c
Normal 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;
|
||||
}
|
||||
131
Examples/conditions/prod_con7.c
Normal file
131
Examples/conditions/prod_con7.c
Normal 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;
|
||||
}
|
||||
121
Examples/conditions/prod_con_cond1.c
Normal file
121
Examples/conditions/prod_con_cond1.c
Normal 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;
|
||||
}
|
||||
133
Examples/conditions/prod_con_cond2.c
Normal file
133
Examples/conditions/prod_con_cond2.c
Normal 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.
39
Examples/ipc/ipcmsg_gfg_r.c
Normal file
39
Examples/ipc/ipcmsg_gfg_r.c
Normal 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;
|
||||
}
|
||||
46
Examples/ipc/ipcmsg_gfg_s.c
Normal file
46
Examples/ipc/ipcmsg_gfg_s.c
Normal 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;
|
||||
}
|
||||
@@ -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
BIN
Examples/ipc/rec
Executable file
Binary file not shown.
BIN
Examples/ipc/send
Executable file
BIN
Examples/ipc/send
Executable file
Binary file not shown.
12
Examples/pthreads/Makefile
Normal file
12
Examples/pthreads/Makefile
Normal 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 $@ $<
|
||||
27
Examples/pthreads/README.md
Normal file
27
Examples/pthreads/README.md
Normal 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
|
||||
48
Examples/pthreads/pthreads1.c
Normal file
48
Examples/pthreads/pthreads1.c
Normal 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;
|
||||
}
|
||||
52
Examples/pthreads/pthreads2.c
Normal file
52
Examples/pthreads/pthreads2.c
Normal 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;
|
||||
}
|
||||
60
Examples/pthreads/pthreads3.c
Normal file
60
Examples/pthreads/pthreads3.c
Normal 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;
|
||||
}
|
||||
72
Examples/pthreads/pthreads4.c
Normal file
72
Examples/pthreads/pthreads4.c
Normal 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;
|
||||
}
|
||||
66
Examples/pthreads/pthreads5.c
Normal file
66
Examples/pthreads/pthreads5.c
Normal 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;
|
||||
}
|
||||
68
Examples/pthreads/pthreads6.c
Normal file
68
Examples/pthreads/pthreads6.c
Normal 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;
|
||||
}
|
||||
46
Examples/pthreads/pthreads_race.c
Normal file
46
Examples/pthreads/pthreads_race.c
Normal 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;
|
||||
}
|
||||
46
Examples/pthreads/pthreads_race_atomic.c
Normal file
46
Examples/pthreads/pthreads_race_atomic.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user