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;
|
||||
}
|
||||
Reference in New Issue
Block a user