Mutual Exclusion: if one thread is in the CS, then no other isProgress: threads waiting for access to CS are not prevented from entering by threads not in the CS; threads do not wait indefinitely.Bounded Waiting (no starvation): all waiting threads are guaranteed to eventually get access to the CS.Performance: The overhead of entering and exiting the CS is small compared to the work done within it.Atomic instruction: A machine instruction that can be executed in a single clock cycle.lock() when it tries to acquire the lock: pthread: https://linux.die.net/man/3/pthread_mutex_locklock() will disable interrupts.unlock() will enable interrupts.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct __lock_t (
int flag;
) lock_t;
void init (lock_t *mutex) {
// 0: lock is available, 1: lock is held
mutex->flag = 0;
}
void lock (lock_t *mutext) {
while (mutex->flag == 1) // TEST the flag
; // spin-wait (do nothing)
mutex->flag = 1; //now SET it!
}
void unlock (lock_t *mutext) {
mutext->flag = 0;
}
TestAndSet can be performed atomically by hardware.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct __lock_t { int flag; } lock_t;
void init(lock_t *mutex) {
// 0 -> lock is available, 1 -> held
mutex->flag = 0;
}
void lock(lock_t *mutex) {
while (mutex->flag == 1) // TEST the flag
; // spin-wait (do nothing)
mutex->flag = 1; // now SET it!
}
void unlock(lock_t *mutex) {
mutex->flag = 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct __lock_t {
int flag;
} lock_t;
void init(lock_t *lock) {
// 0: lock is available, 1: lock is held
lock->flag = 0;
}
void lock(lock_t *lock) {
while (TestAndSet(&lock->flag, 1) == 1)
; // spin-wait (do nothing)
}
void unlock(lock_t *lock) {
lock->flag = 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void *child(void *arg) {
printf("child\n");
// how to indicate we are done?
// ...
return NULL;
}
int main(int argc, char *argv[]) {
printf("parent: begin\n");
pthread_t c;
Pthread_create(&c, NULL, child, NULL); // create child
// how to wait for child?
// ...
printf("parent: end\n");
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
volatile int done = 0;
void *child(void *arg) {
printf("child\n");
done = 1;
return NULL;
}
int main(int argc, char *argv[]) {
printf("parent: begin\n");
pthread_t c;
Pthread_create(&c, NULL, child, NULL); // create child
while (done == 0)
; // spin
printf("parent: end\n");
return 0;
}
parent while spinning.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int done = 0;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
void thr_exit() {
Pthread_mutex_lock(&m);
done = 1;
Pthread_cond_signal(&c);
Pthread_mutex_unlock(&m);
}
void *child(void *arg) {
printf("child\n");
thr_exit();
return NULL;
}
void thr_join() {
Pthread_mutex_lock(&m);
while (done == 0)
Pthread_cond_wait(&c, &m);
Pthread_mutex_unlock(&m);
}
int main(int argc, char *argv[]) {
printf("parent: begin\n");
pthread_t p;
Pthread_create(&p, NULL, child, NULL);
thr_join();
printf("parent: end\n");
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int done = 0;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
void thr_exit() {
Pthread_mutex_lock(&m);
done = 1;
Pthread_cond_signal(&c);
Pthread_mutex_unlock(&m);
}
void *child(void *arg) {
printf("child\n");
thr_exit();
return NULL;
}
void thr_join() {
Pthread_mutex_lock(&m);
while (done == 0)
Pthread_cond_wait(&c, &m);
Pthread_mutex_unlock(&m);
}
int main(int argc, char *argv[]) {
printf("parent: begin\n");
pthread_t p;
Pthread_create(&p, NULL, child, NULL);
thr_join();
printf("parent: end\n");
return 0;
}
1
2
3
4
5
6
7
8
9
void thr_exit() {
done = 1;
Pthread_cond_signal(&c);
}
void thr_join() {
if (done == 0)
Pthread_cond_wait(&c);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int buffer;
int count = 0; // initially, empty
void put(int value) {
assert(count == 0);
count = 1;
buffer = value;
}
int get() {
assert(count == 1);
count = 0;
return buffer;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void *producer(void *arg) {
int i;
int loops = (int) arg;
for (i = 0; i < loops; i++) {
put(i);
}
}
void *consumer(void *arg) {
int i;
while (1) {
int tmp = get();
printf("%d\n", tmp);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int buffer;
int count = 0; // initially, empty
void put(int value) {
assert(count == 0);
count = 1;
buffer = value;
}
int get() {
assert(count == 1);
count = 0;
return buffer;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int loops; // must initialize somewhere...
cond_t cond;
mutex_t mutex;
void *producer(void *arg) {
int i;
for (i = 0; i < loops; i++) {
Pthread_mutex_lock(&mutex); // p1
if (count == 1) // p2
Pthread_cond_wait(&cond, &mutex); // p3
put(i); // p4
Pthread_cond_signal(&cond); // p5
Pthread_mutex_unlock(&mutex); // p6
}
}
void *consumer(void *arg) {
int i;
for (i = 0; i < loops; i++) {
Pthread_mutex_lock(&mutex); // c1
if (count == 0) // c2
Pthread_cond_wait(&cond, &mutex); // c3
int tmp = get(); // c4
Pthread_cond_signal(&cond); // c5
Pthread_mutex_unlock(&mutex); // c6
printf("%d\n", tmp);
}
}
| Tc1 | State | Tc2 | State | Tp | State | Count | Comment |
|---|---|---|---|---|---|---|---|
| c1 | Running | Ready | Ready | 0 | |||
| c2 | Running | Ready | Ready | 0 | |||
| c3 | Sleep | Ready | Ready | 0 | Nothing to get | ||
| Sleep | Ready | p1 | Running | 0 | |||
| Sleep | Ready | p2 | Running | 0 | |||
| Sleep | Ready | p4 | Running | 1 | Buffer now full | ||
| Ready | Ready | p5 | Running | 1 | Tc1 awoken | ||
| Ready | Ready | p6 | Running | 1 | |||
| Ready | Ready | p1 | Running | 1 | |||
| Ready | Ready | p2 | Running | 1 | |||
| Ready | Ready | p3 | Sleep | 1 | Buffer full; sleep | ||
| Ready | c1 | Running | Sleep | 1 | Tc2 sneaks in … | ||
| Ready | c2 | Running | Sleep | 1 | |||
| Ready | c4 | Running | Sleep | 0 | … and grabs data | ||
| Ready | c5 | Running | Ready | 0 | Tp awoken | ||
| Ready | c6 | Running | Ready | 0 | |||
| c4 | Running | Ready | Ready | 0 | Oh oh! No data |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int loops; // must initialize somewhere...
cond_t cond;
mutex_t mutex;
void *producer(void *arg) {
int i;
for (i = 0; i < loops; i++) {
Pthread_mutex_lock(&mutex); // p1
if (count == 1) // p2
Pthread_cond_wait(&cond, &mutex); // p3
put(i); // p4
Pthread_cond_signal(&cond); // p5
Pthread_mutex_unlock(&mutex); // p6
}
}
void *consumer(void *arg) {
int i;
for (i = 0; i < loops; i++) {
Pthread_mutex_lock(&mutex); // c1
if (count == 0) // c2
Pthread_cond_wait(&cond, &mutex); // c3
int tmp = get(); // c4
Pthread_cond_signal(&cond); // c5
Pthread_mutex_unlock(&mutex); // c6
printf("%d\n", tmp);
}
}
| Tc1 | State | Tc2 | State | Tp | State | Count | Comment |
|---|---|---|---|---|---|---|---|
| c1 | Running | Ready | Ready | 0 | |||
| c2 | Running | Ready | Ready | 0 | |||
| c3 | Sleep | Ready | Ready | 0 | Nothing to get | ||
| Sleep | c1 | Running | Ready | 0 | |||
| Sleep | c2 | Running | Ready | 0 | |||
| Sleep | c3 | Sleep | Ready | 0 | Nothing to get | ||
| Sleep | Sleep | p1 | Running | 0 | |||
| Sleep | Sleep | p2 | Running | 0 | |||
| Sleep | Sleep | p4 | Running | 1 | Buffer now full | ||
| Ready | Sleep | p5 | Running | 1 | Tc1 awoken | ||
| Ready | Sleep | p6 | Running | 1 | |||
| Ready | Sleep | p1 | Running | 1 | |||
| Ready | Sleep | p2 | Running | 1 | |||
| Ready | Sleep | p3 | Sleep | 1 | Must sleep (full) | ||
| c2 | Running | Sleep | Sleep | 1 | Recheck condition | ||
| c4 | Running | Sleep | Sleep | 0 | Tc1 grabs data | ||
| c5 | Running | Ready | Sleep | 0 | Oops! Woke Tc2 | ||
| c6 | Running | Ready | Sleep | 0 | |||
| c1 | Running | Ready | Sleep | 0 | |||
| c2 | Running | Ready | Sleep | 0 | |||
| c3 | Sleep | Ready | Sleep | 0 | Nothing to get | ||
| Sleep | c2 | Running | Sleep | 0 | |||
| Sleep | c3 | Sleep | Sleep | 0 | Everyone asleep… |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int loops;
cond_t cond;
mutex_t mutex;
void *producer(void *arg) {
int i;
for (i = 0; i < loops; i++) {
Pthread_mutex_lock(&mutex); // p1
while (count == 1) // p2
Pthread_cond_wait(&cond, &mutex); // p3
put(i); // p4
Pthread_cond_signal(&cond); // p5
Pthread_mutex_unlock(&mutex); // p6
}
}
void *consumer(void *arg) {
int i;
for (i = 0; i < loops; i++) {
Pthread_mutex_lock(&mutex); // c1
while (count == 0) // c2
Pthread_cond_wait(&cond, &mutex); // c3
int tmp = get(); // c4
Pthread_cond_signal(&cond); // c5
Pthread_mutex_unlock(&mutex); // c6
printf("%d\n", tmp);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
cond_t empty, fill;
mutex_t mutex;
void *producer(void *arg) {
int i;
for (i = 0; i < loops; i++) {
Pthread_mutex_lock(&mutex);
while (count == 1)
Pthread_cond_wait(&empty, &mutex);
put(i);
Pthread_cond_signal(&fill);
Pthread_mutex_unlock(&mutex);
}
}
void *consumer(void *arg) {
int i;
for (i = 0; i < loops; i++) {
Pthread_mutex_lock(&mutex);
while (count == 0)
Pthread_cond_wait(&fill, &mutex);
int tmp = get();
Pthread_cond_signal(&empty);
Pthread_mutex_unlock(&mutex);
printf("%d\n", tmp);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int buffer[MAX];
int fill_ptr = 0;
int use_ptr = 0;
int count = 0;
void put(int value) {
buffer[fill_ptr] = value;
fill_ptr = (fill_ptr + 1) % MAX;
count++;
}
int get() {
int tmp = buffer[use_ptr];
use_ptr = (use_ptr + 1) % MAX;
count--;
return tmp;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
cond_t empty, fill;
mutex_t mutex;
void *producer(void *arg) {
int i;
for (i = 0; i < loops; i++) {
Pthread_mutex_lock(&mutex); // p1
while (count == MAX) // p2
Pthread_cond_wait(&empty, &mutex); // p3
put(i); // p4
Pthread_cond_signal(&fill); // p5
Pthread_mutex_unlock(&mutex); // p6
}
}
void *consumer(void *arg) {
int i;
for (i = 0; i < loops; i++) {
Pthread_mutex_lock(&mutex); // c1
while (count == 0) // c2
Pthread_cond_wait(&fill, &mutex); // c3
int tmp = get(); // c4
Pthread_cond_signal(&empty); // c5
Pthread_mutex_unlock(&mutex); // c6
printf("%d\n", tmp);
}
}