-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbanker2.c
104 lines (86 loc) · 2.6 KB
/
banker2.c
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/*
Deadlock
Deadlock is the second villain of concurrent programming, and happens when
threads wait on each others’ locks, but no thread unlocks for any other.
The case of the bankers is a classic simple form called the deadly embrace.
*/
/* banker.c */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#define N_ACCOUNTS 10
#define N_THREADS 20
#define N_ROUNDS 10000
/* 10 accounts with $100 apiece means there's $1,000
in the system. Let's hope it stays that way... */
#define INIT_BALANCE 100
/* making a struct here for the benefit of future
versions of this program */
struct account
{
long balance;
pthread_mutex_t mtx;
} accts[N_ACCOUNTS];
/* Helper for bankers to choose an account and amount at
random. It came from Steve Summit's excellent C FAQ
http://c-faq.com/lib/randrange.html */
int rand_range(int N)
{
return (int)((double)rand() / ((double)RAND_MAX + 1) * N);
}
/* each banker will run this function concurrently. The
weird signature is required for a thread function */
void *disburse(void *arg)
{
size_t i, from, to;
long payment;
/* idiom to tell compiler arg is unused */
(void)arg;
for (i = 0; i < N_ROUNDS; i++)
{
/* pick distinct 'from' and 'to' accounts */
from = rand_range(N_ACCOUNTS);
do {
to = rand_range(N_ACCOUNTS);
} while (to == from);
/* go nuts sending money, try not to overdraft */
/* get an exclusive lock on both balances before
updating (there's a problem with this, see below) */
pthread_mutex_lock(&accts[from].mtx);
pthread_mutex_lock(&accts[to].mtx);
if (accts[from].balance > 0)
{
payment = 1 + rand_range(accts[from].balance);
accts[from].balance -= payment;
accts[to].balance += payment;
}
pthread_mutex_unlock(&accts[to].mtx);
pthread_mutex_unlock(&accts[from].mtx);
}
return NULL;
}
int main(void)
{
size_t i;
long total;
pthread_t ts[N_THREADS];
srand(time(NULL));
for (i = 0; i < N_ACCOUNTS; i++) {
accts[i] = (struct account){INIT_BALANCE, PTHREAD_MUTEX_INITIALIZER};
}
printf("Initial money in system: %d\n",
N_ACCOUNTS * INIT_BALANCE);
/* start the threads, using whatever parallelism the
system happens to offer. Note that pthread_create
is the *only* function that creates concurrency */
for (i = 0; i < N_THREADS; i++)
pthread_create(&ts[i], NULL, disburse, NULL);
/* wait for the threads to all finish, using the
pthread_t handles pthread_create gave us */
for (i = 0; i < N_THREADS; i++)
pthread_join(ts[i], NULL);
for (total = 0, i = 0; i < N_ACCOUNTS; i++)
total += accts[i].balance;
printf("Final money in system: %ld\n", total);
}