add threads API code and Makefile and README

This commit is contained in:
Remzi Arpaci-Dusseau 2020-06-08 04:13:03 -05:00
parent 1b24c91885
commit e5198a301a
8 changed files with 244 additions and 0 deletions

23
threads-api/Makefile Normal file
View File

@ -0,0 +1,23 @@
FLAGS = -Wall -pthread -g
all: main-race main-deadlock main-deadlock-global main-signal main-signal-cv
clean:
rm -f main-race main-deadlock main-deadlock-global main-signal main-signal-cv
main-race: main-race.c common_threads.h
gcc -o main-race main-race.c $(FLAGS)
main-deadlock: main-deadlock.c common_threads.h
gcc -o main-deadlock main-deadlock.c $(FLAGS)
main-deadlock-global: main-deadlock-global.c common_threads.h
gcc -o main-deadlock-global main-deadlock-global.c $(FLAGS)
main-signal: main-signal.c common_threads.h
gcc -o main-signal main-signal.c $(FLAGS)
main-signal-cv: main-signal-cv.c common_threads.h
gcc -o main-signal-cv main-signal-cv.c $(FLAGS)

32
threads-api/README.md Normal file
View File

@ -0,0 +1,32 @@
# Overview
In this homework, you'll use a real tool on Linux to find problems in
multi-threaded code. The tool is called `helgrind` (available as part of the
valgrind suite of debugging tools).
See `http://valgrind.org/docs/manual/hg-manual.htm` for details about
the tool, including how to download and install it (if it's not
already on your Linux system).
You'll then look at a number of multi-threaded C programs to see how you can
use the tool to debug problematic threaded code.
First things first: download and install `valgrind` and the related `helgrind` tool.
Then, type `make` to build all the different programs. Examine the `Makefile`
for more details on how that works.
Then, you have a few different C programs to look at:
- `main-race.c`: A simple race condition
- `main-deadlock.c`: A simple deadlock
- `main-deadlock-global.c`: A solution to the deadlock problem
- `main-signal.c`: A simple child/parent signaling example
- `main-signal-cv.c`: A more efficient signaling via condition variables
- `common_threads.h`: Header file with wrappers to make code check errors and be more readable
With these programs, you can now answer the questions in the textbook.

View File

@ -0,0 +1,37 @@
#ifndef __common_threads_h__
#define __common_threads_h__
#include <pthread.h>
#include <assert.h>
#include <sched.h>
#ifdef __linux__
#include <semaphore.h>
#endif
#define Pthread_create(thread, attr, start_routine, arg) assert(pthread_create(thread, attr, start_routine, arg) == 0);
#define Pthread_join(thread, value_ptr) assert(pthread_join(thread, value_ptr) == 0);
#define Pthread_mutex_init(m, v) assert(pthread_mutex_init(m, v) == 0);
#define Pthread_mutex_lock(m) assert(pthread_mutex_lock(m) == 0);
#define Pthread_mutex_unlock(m) assert(pthread_mutex_unlock(m) == 0);
#define Pthread_cond_init(cond, v) assert(pthread_cond_init(cond, v) == 0);
#define Pthread_cond_signal(cond) assert(pthread_cond_signal(cond) == 0);
#define Pthread_cond_wait(cond, mutex) assert(pthread_cond_wait(cond, mutex) == 0);
#define Mutex_init(m) assert(pthread_mutex_init(m, NULL) == 0);
#define Mutex_lock(m) assert(pthread_mutex_lock(m) == 0);
#define Mutex_unlock(m) assert(pthread_mutex_unlock(m) == 0);
#define Cond_init(cond) assert(pthread_cond_init(cond, NULL) == 0);
#define Cond_signal(cond) assert(pthread_cond_signal(cond) == 0);
#define Cond_wait(cond, mutex) assert(pthread_cond_wait(cond, mutex) == 0);
#ifdef __linux__
#define Sem_init(sem, value) assert(sem_init(sem, 0, value) == 0);
#define Sem_wait(sem) assert(sem_wait(sem) == 0);
#define Sem_post(sem) assert(sem_post(sem) == 0);
#endif // __linux__
#endif // __common_threads_h__

View File

@ -0,0 +1,31 @@
#include <stdio.h>
#include "common_threads.h"
pthread_mutex_t g = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER;
void* worker(void* arg) {
Pthread_mutex_lock(&g);
if ((long long) arg == 0) {
Pthread_mutex_lock(&m1);
Pthread_mutex_lock(&m2);
} else {
Pthread_mutex_lock(&m2);
Pthread_mutex_lock(&m1);
}
Pthread_mutex_unlock(&m1);
Pthread_mutex_unlock(&m2);
Pthread_mutex_unlock(&g);
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p1, p2;
Pthread_create(&p1, NULL, worker, (void *) (long long) 0);
Pthread_create(&p2, NULL, worker, (void *) (long long) 1);
Pthread_join(p1, NULL);
Pthread_join(p2, NULL);
return 0;
}

View File

@ -0,0 +1,28 @@
#include <stdio.h>
#include "common_threads.h"
pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER;
void* worker(void* arg) {
if ((long long) arg == 0) {
Pthread_mutex_lock(&m1);
Pthread_mutex_lock(&m2);
} else {
Pthread_mutex_lock(&m2);
Pthread_mutex_lock(&m1);
}
Pthread_mutex_unlock(&m1);
Pthread_mutex_unlock(&m2);
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p1, p2;
Pthread_create(&p1, NULL, worker, (void *) (long long) 0);
Pthread_create(&p2, NULL, worker, (void *) (long long) 1);
Pthread_join(p1, NULL);
Pthread_join(p2, NULL);
return 0;
}

18
threads-api/main-race.c Normal file
View File

@ -0,0 +1,18 @@
#include <stdio.h>
#include "common_threads.h"
int balance = 0;
void* worker(void* arg) {
balance++; // unprotected access
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p;
Pthread_create(&p, NULL, worker, NULL);
balance++; // unprotected access
Pthread_join(p, NULL);
return 0;
}

View File

@ -0,0 +1,55 @@
#include <stdio.h>
#include "common_threads.h"
//
// simple synchronizer: allows one thread to wait for another
// structure "synchronizer_t" has all the needed data
// methods are:
// init (called by one thread)
// wait (to wait for a thread)
// done (to indicate thread is done)
//
typedef struct __synchronizer_t {
pthread_mutex_t lock;
pthread_cond_t cond;
int done;
} synchronizer_t;
synchronizer_t s;
void signal_init(synchronizer_t *s) {
Pthread_mutex_init(&s->lock, NULL);
Pthread_cond_init(&s->cond, NULL);
s->done = 0;
}
void signal_done(synchronizer_t *s) {
Pthread_mutex_lock(&s->lock);
s->done = 1;
Pthread_cond_signal(&s->cond);
Pthread_mutex_unlock(&s->lock);
}
void signal_wait(synchronizer_t *s) {
Pthread_mutex_lock(&s->lock);
while (s->done == 0)
Pthread_cond_wait(&s->cond, &s->lock);
Pthread_mutex_unlock(&s->lock);
}
void* worker(void* arg) {
printf("this should print first\n");
signal_done(&s);
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p;
signal_init(&s);
Pthread_create(&p, NULL, worker, NULL);
signal_wait(&s);
printf("this should print last\n");
return 0;
}

20
threads-api/main-signal.c Normal file
View File

@ -0,0 +1,20 @@
#include <stdio.h>
#include "common_threads.h"
int done = 0;
void* worker(void* arg) {
printf("this should print first\n");
done = 1;
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p;
Pthread_create(&p, NULL, worker, NULL);
while (done == 0)
;
printf("this should print last\n");
return 0;
}