Lecture 13 Concurrency Bugs

Download Report

Transcript Lecture 13 Concurrency Bugs

Lecture 13
Concurrency Bugs
CV rules of thumb:
• Keep state in addition to CV’s
• Always do wait/signal with lock held
• Whenever you acquire a lock, recheck state
Implementing Join with CV
void thread_exit() {
Mutex_lock(&m);
done = 1;
Cond_signal(&c);
Mutex_unlock(&m);
}
void thread_join() {
Mutex_lock(&m);
if (done == 0)
Cond_wait(&c, &m);
Mutex_unlock(&m);
}
// a
// b
//
//
//
//
w
x
y
z
Producer/Consumer Problem
with CV
void *producer(void *arg) {
for (int i=0; i < loops; i++){
}
}
Mutex_lock(&m);
//p1
while (numfull == max)
//p2
Cond_wait(&E, &m);
//p3
put(i);
//p4
Cond_signal(&F);
//p5
Mutex_unlock(&m);
//p6
void *consumer(void *arg)
while (1) {
Mutex_lock(&m);
while (numfull == 0)
Cond_wait(&F, &m);
int tmp = get();
Cond_signal(&E);
Mutex_unlock(&m);
printf("%d\n", tmp);
}
}
{
//c1
//c2
//c3
//c4
//c5
//c6
Semaphore
• For the following init’s, what might the use be?
• sem_init(&s, 0);
• sem_init(&s, 1);
• sem_init(&s, N);
int main(int argc, char *argv[]) {
P/C problem
semaphore
// ...
sem_init(&empty, MAX);
sem_init(&full, 0);
sem_t empty, full, mutex;
sem_init(&mutex, 1);
// ...
void *consumer(void *arg) {
}
void *producer(void *arg) {
int i, tmp = 0;
while (1) {
sem_wait(&full);
int i;
// C1
for (i = 0; i < loops; i++) {
sem_wait(&mutex); // C1.5
tmp = get();
sem_wait(&empty); // P1
// C2
sem_wait(&mutex); // P1.5
sem_post(&mutex); // C2.5
put(i);
sem_post(&empty); // C3
sem_post(&mutex); // P2.5
printf("%d\n", tmp);
sem_post(&full);
}
}
// P2
}
}
// P3
Build semaphores
with locks and CV’s
typedef struct __sem_t {
int value;
cond_t cond;
lock_t lock;
} sem_t;
void sem_init(sem_t *s, int value) {
s->value = value;
Cond_init(&s->cond);
Lock_init(&s->lock);
}
void sem_wait(sem_t *s) {…}
void sem_post(sem_t *s) {…}
Concurrency bugs in history
Race
• A data race occurs when: two or more threads in a
single process access the same memory location
concurrently, and. at least one of the accesses is for
writing, and. the threads are not using any
exclusive locks to control their accesses to that
memory.
• A race condition is an undesirable situation that
occurs when a device or system attempts to
perform two or more operations at the same time,
but because of the nature of the device or system,
the operations must be done in the proper
sequence in order to be done correctly.
Concurrency Bugs are
Common and Various
Application
Atomicity
Order
Deadlock
other
MySQL
12
1
9
1
Apache
7
6
4
0
Mozilla
29
15
16
0
OpenOffice
3
2
2
1
Atomicity: MySQL
Thread 1:
Thread 2:
pthread_mutex_lock(&lock);
if (thd->proc_info) {
pthread_mutex_lock(&lock);
…
thd->proc_info = NULL;
fputs(thd->proc_info, …);
pthread_mutex_unlock(&lock);
…
}
pthread_mutex_unlock(&lock);
Ordering: Mozilla
Thread 1:
Thread 2:
void init() {
void mMain(…) {
…
…
mThread
Mutex_lock(&mtLock);
= PR_CreateThread(mMain, …);
while(mtInit == 0)
pthread_mutex_lock(&mtLock);
Cond_wait(&mtCond, &mtLock);
mtInit = 1;
Mutex_unlock(&mtLock);
pthread_cond_signal(&mtCond);
mState = mThread->State;
pthread_mutex_unlock(&mtLock);
…
…
}
}
Race: MySQL
Thread 1:
Thread 2:
if (thd->proc_info) {
…
fputs(thd->proc_info, …);
…
}
thd->proc_info = NULL;
Race: Mozilla
Thread 1:
Thread 2:
void init() {
void mMain(…) {
…
…
mThread
mState = mThread->State;
= PR_CreateThread(mMain, …);
…
}
…
}
Data Race Free
DOES not mean
Concurrency Bug Free
Deadlock
Boring Code Example
Thread 1
lock(&A);
lock(&B);
Thread 2
lock(&B);
lock(&A);
What’s Wrong?
set_t *set_union (set_t *s1, set_t *s2) {
set_t *rv = Malloc(sizeof(*rv));
Mutex_lock(&s1->lock);
Mutex_lock(&s2->lock);
for(int i=0; i<s1->len; i++) {
if(set_contains(s2, s1->items[i])
set_add(rv, s1->items[i]);
Mutex_unlock(&s2->lock);
Mutex_unlock(&s1->lock);
}
Encapsulation
• Modularity can make it harder to see deadlocks.
Thread 1
Thread 2
rv = set_union(setA, setB); rv = set_union(setB, setA);
Deadlock Theory
• Deadlocks can only happen with these four
conditions:
•
•
•
•
mutual exclusion
hold-and-wait
no preemption
circular wait
• Eliminate deadlock by eliminating one condition.
Mutual Exclusion
• Def: Threads claim exclusive control of resources
that they require (e.g., thread grabs a lock).
Wait-Free Algorithms
• Strategy: eliminate lock use.
• Assume we have:
• int CompAndSwap(int *addr, int expected, int new)
• 0: fail, 1: success
void add_v1(int *val,
int amt) {
Mutex_lock(&m);
*val += amt;
Mutex_unlock(&m);
}
void add_v2(int *val,
int amt) {
do {
int old = *value;
}
while(!CompAndSwap(val,
old, old+amt);
}
Wait-Free Insert
void insert(int val) {
void insert(int val) {
node_t *n = Malloc(sizeof(*n));
node_t *n = Malloc(sizeof(*n));
n->val = val;
n->val = val;
lock(&m);
do {
n->next = head;
n->next = head;
head = n;
} while (!CompAndSwap(&head,
unlock(&m);
}
n->next, n));
}
Deadlock Theory
• Deadlocks can only happen with these four
conditions:
•
•
•
•
mutual exclusion
hold-and-wait
no preemption
circular wait
• Eliminate deadlock by eliminating one condition.
Hold-and-Wait
• Def: Threads hold resources allocated to them (e.g.,
locks they have already acquired) while waiting for
additional resources (e.g., locks they wish to
acquire).
Eliminate Hold-and-Wait
• Strategy: acquire all locks atomically once
(cannot acquire again until all have been released).
• For this, use a meta lock, like this:
lock(&meta);
lock(&L1);
lock(&L2);
…
unlock(&meta);
• disadvantages?
Deadlock Theory
• Deadlocks can only happen with these four
conditions:
•
•
•
•
mutual exclusion
hold-and-wait
no preemption
circular wait
• Eliminate deadlock by eliminating one condition.
No preemption
• Def: Resources (e.g., locks) cannot be forcibly
removed from threads that are holding them
Support Preemption
• Strategy: if we can’t get what we want, release
what we have.
top:
lock(A);
if (trylock(B) == -1) {
unlock(A);
goto top;
}
…
Deadlock Theory
• Deadlocks can only happen with these four
conditions:
•
•
•
•
mutual exclusion
hold-and-wait
no preemption
circular wait
• Eliminate deadlock by eliminating one condition.
Circular Wait
• Def: There exists a circular chain of threads such
that each thread holds a resource (e.g., lock) being
requested by next thread in the chain.
Eliminating Circular Wait
• Strategy:
• decide which locks should be acquired before others
• if A before B, never acquire A if B is already held!
• document this, and write code accordingly
Other approaches
• Deadlock avoidance vis scheduling
• Detect and recover