diff options
| author | manuel <manuel@mausz.at> | 2012-05-11 20:48:58 +0200 |
|---|---|---|
| committer | manuel <manuel@mausz.at> | 2012-05-11 20:48:58 +0200 |
| commit | 9b19a84ed640cb15e6ca87ddc025c90d78a5fbdc (patch) | |
| tree | 54345ae3ba366d1a4663e93b6b883e842f7a3918 | |
| parent | 4571d64e74dadd1b2169e8cd276f8a298decd5f0 (diff) | |
| download | progos-9b19a84ed640cb15e6ca87ddc025c90d78a5fbdc.tar.gz progos-9b19a84ed640cb15e6ca87ddc025c90d78a5fbdc.tar.bz2 progos-9b19a84ed640cb15e6ca87ddc025c90d78a5fbdc.zip | |
* fix possible race in thread_set_priority
* fill in same parts of proj1.txt
* use thread_get_priority() whenever possible
| -rw-r--r-- | proj1.txt | 53 | ||||
| -rw-r--r-- | threads/synch.c | 16 | ||||
| -rw-r--r-- | threads/thread.c | 21 |
3 files changed, 78 insertions, 12 deletions
| @@ -35,13 +35,16 @@ http://hynek.me/studies/sched_ausarbeitung.pdf | |||
| 35 | >> enumeration. Identify the purpose of each in 25 words or less. | 35 | >> enumeration. Identify the purpose of each in 25 words or less. |
| 36 | 36 | ||
| 37 | struct thread: | 37 | struct thread: |
| 38 | int is_donated ...flag if threads priority has been donated by another thread | 38 | int is_donated ...flag if threads priority has been donated by |
| 39 | another thread | ||
| 39 | int saved_priority ...saved base priority in case of priority dontation | 40 | int saved_priority ...saved base priority in case of priority dontation |
| 40 | struct list locks ...list of locks the thread currently holds | 41 | struct list locks ...list of locks the thread currently holds |
| 41 | struct lock *blocked_lock ...the lock the thread currently tries to acquire (but hasn't yet) | 42 | struct lock *blocked_lock ...the lock the thread currently tries to acquire |
| 43 | (but hasn't yet) | ||
| 42 | 44 | ||
| 43 | struct lock: | 45 | struct lock: |
| 44 | int priority ...the priority of the thread with the highest priority currently acquiring the lock | 46 | int priority ...the priority of the thread with the highest |
| 47 | priority currently acquiring the lock | ||
| 45 | struct list_elem elem ...list element for (struct thread)->locks | 48 | struct list_elem elem ...list element for (struct thread)->locks |
| 46 | 49 | ||
| 47 | struct semaphore_elem: | 50 | struct semaphore_elem: |
| @@ -51,14 +54,20 @@ struct semaphore_elem: | |||
| 51 | >> Use ASCII art to diagram a nested donation. (Alternately, submit a | 54 | >> Use ASCII art to diagram a nested donation. (Alternately, submit a |
| 52 | >> .png file.) | 55 | >> .png file.) |
| 53 | 56 | ||
| 57 | TODO | ||
| 58 | |||
| 54 | ---- ALGORITHMS ---- | 59 | ---- ALGORITHMS ---- |
| 55 | 60 | ||
| 56 | >> B3: How do you ensure that the highest priority thread waiting for | 61 | >> B3: How do you ensure that the highest priority thread waiting for |
| 57 | >> a lock, semaphore, or condition variable wakes up first? | 62 | >> a lock, semaphore, or condition variable wakes up first? |
| 58 | 63 | ||
| 64 | TODO | ||
| 65 | |||
| 59 | >> B4: Describe the sequence of events when a call to lock_acquire() | 66 | >> B4: Describe the sequence of events when a call to lock_acquire() |
| 60 | >> causes a priority donation. How is nested donation handled? | 67 | >> causes a priority donation. How is nested donation handled? |
| 61 | 68 | ||
| 69 | TODO | ||
| 70 | |||
| 62 | >> B5: Describe the sequence of events when lock_release() is called | 71 | >> B5: Describe the sequence of events when lock_release() is called |
| 63 | >> on a lock that a higher-priority thread is waiting for. | 72 | >> on a lock that a higher-priority thread is waiting for. |
| 64 | 73 | ||
| @@ -68,11 +77,43 @@ struct semaphore_elem: | |||
| 68 | >> how your implementation avoids it. Can you use a lock to avoid | 77 | >> how your implementation avoids it. Can you use a lock to avoid |
| 69 | >> this race? | 78 | >> this race? |
| 70 | 79 | ||
| 80 | thread_set_priority() first changes the priority and then has to either just | ||
| 81 | re-sort the ready list or has to decide whether it should yield by comparing the | ||
| 82 | priorities of the current thread and the thread with the highest priority in | ||
| 83 | the ready list. the gap between changing the priority and doing the other stuff | ||
| 84 | raises a few possible races if the timer interrupt triggers in between. | ||
| 85 | e.g. no proper sorted ready list, corrupt ready list (wrong internal pointers) | ||
| 86 | or the timer interrupt fires after calling list_empty() but before list_front() | ||
| 87 | while having only two threads (one active, the other one ready). the scheduler | ||
| 88 | then schedules the other thread which runs and perhaps terminates, thus removing | ||
| 89 | itself from all lists. after that our thread gets scheduled again and reads from | ||
| 90 | an empty list, although it wasn't empty before. | ||
| 91 | |||
| 92 | Using a lock instead of disabling the interrupts would require locking the | ||
| 93 | access to the ready list everywhere. This doesn't follow the already established | ||
| 94 | conventions and would rather be a lot more cpu intensive. | ||
| 95 | |||
| 71 | ---- RATIONALE ---- | 96 | ---- RATIONALE ---- |
| 72 | 97 | ||
| 73 | >> B7: Why did you choose this design? In what ways is it superior to | 98 | >> B7: Why did you choose this design? In what ways is it superior to |
| 74 | >> another design you considered? | 99 | >> another design you considered? |
| 75 | 100 | ||
| 101 | Acutally we didn't consider any other design. We first tried to solve the | ||
| 102 | assignment without pre-planning and just started coding but that failed. | ||
| 103 | Afterwards we looked at the various cases, did some drafts and decided on the | ||
| 104 | required structure modifications. That did work quite well. | ||
| 105 | |||
| 106 | In case of priority donation with locks our design needs only a few cpu | ||
| 107 | instructions because we sort the list of locks during insertion and whenever a | ||
| 108 | donated priority changes. This allows us to retreive the next donated priority | ||
| 109 | with just looking at the first element in the list. | ||
| 110 | |||
| 111 | In case of priority donation with semaphores and conditions we can't sort the | ||
| 112 | blocked/waiting threads during insertion as their priority may change afterwards | ||
| 113 | and we don't have any reference to currently used semaphores/conditions inside | ||
| 114 | the thread structure. Thus we simply sort the whole list inside sema_up()/ | ||
| 115 | cond_signal() before unblocking the next thread. | ||
| 116 | |||
| 76 | SURVEY QUESTIONS | 117 | SURVEY QUESTIONS |
| 77 | ================ | 118 | ================ |
| 78 | 119 | ||
| @@ -85,6 +126,10 @@ the quarter. | |||
| 85 | >> In your opinion, was this assignment, or any one of the three problems | 126 | >> In your opinion, was this assignment, or any one of the three problems |
| 86 | >> in it, too easy or too hard? Did it take too long or too little time? | 127 | >> in it, too easy or too hard? Did it take too long or too little time? |
| 87 | 128 | ||
| 129 | working on the priority donation without being able to use printf() inside | ||
| 130 | lock_acquire()/lock_release() (as printf() requires a lock for the console | ||
| 131 | itself) was challenging. | ||
| 132 | |||
| 88 | >> Did you find that working on a particular part of the assignment gave | 133 | >> Did you find that working on a particular part of the assignment gave |
| 89 | >> you greater insight into some aspect of OS design? | 134 | >> you greater insight into some aspect of OS design? |
| 90 | 135 | ||
| @@ -92,6 +137,8 @@ the quarter. | |||
| 92 | >> future quarters to help them solve the problems? Conversely, did you | 137 | >> future quarters to help them solve the problems? Conversely, did you |
| 93 | >> find any of our guidance to be misleading? | 138 | >> find any of our guidance to be misleading? |
| 94 | 139 | ||
| 140 | do not use printf() inside lock_acquire()/lock_release() :) | ||
| 141 | |||
| 95 | >> Do you have any suggestions for the TAs to more effectively assist | 142 | >> Do you have any suggestions for the TAs to more effectively assist |
| 96 | >> students, either for future quarters or the remaining projects? | 143 | >> students, either for future quarters or the remaining projects? |
| 97 | 144 | ||
diff --git a/threads/synch.c b/threads/synch.c index 98905b1..a1b7549 100644 --- a/threads/synch.c +++ b/threads/synch.c | |||
| @@ -128,7 +128,7 @@ sema_up (struct semaphore *sema) | |||
| 128 | list_sort (&sema->waiters, &thread_priority_cmp_greater, NULL); | 128 | list_sort (&sema->waiters, &thread_priority_cmp_greater, NULL); |
| 129 | struct thread *t = list_entry (list_pop_front (&sema->waiters), struct thread, elem); | 129 | struct thread *t = list_entry (list_pop_front (&sema->waiters), struct thread, elem); |
| 130 | /* thread_unblock() doesn't yield so check if there's a need */ | 130 | /* thread_unblock() doesn't yield so check if there's a need */ |
| 131 | if (t->priority > thread_current ()->priority) | 131 | if (t->priority > thread_get_priority ()) |
| 132 | yield = true; | 132 | yield = true; |
| 133 | thread_unblock (t); | 133 | thread_unblock (t); |
| 134 | } | 134 | } |
| @@ -240,10 +240,10 @@ lock_acquire (struct lock *lock) | |||
| 240 | while (blocked_lock != NULL && blocked_lock->holder != NULL) | 240 | while (blocked_lock != NULL && blocked_lock->holder != NULL) |
| 241 | { | 241 | { |
| 242 | /* check for a possible priority donation */ | 242 | /* check for a possible priority donation */ |
| 243 | if (cur->priority > blocked_lock->priority) | 243 | if (thread_get_priority () > blocked_lock->priority) |
| 244 | { | 244 | { |
| 245 | /* the new priority of this lock is our own priority */ | 245 | /* the new priority of this lock is our own priority */ |
| 246 | blocked_lock->priority = cur->priority; | 246 | blocked_lock->priority = thread_get_priority (); |
| 247 | 247 | ||
| 248 | /* the lock priority changed so we need to sort the lock list of the lock holder */ | 248 | /* the lock priority changed so we need to sort the lock list of the lock holder */ |
| 249 | list_remove (&blocked_lock->elem); | 249 | list_remove (&blocked_lock->elem); |
| @@ -264,7 +264,7 @@ lock_acquire (struct lock *lock) | |||
| 264 | cur->blocked_lock = NULL; | 264 | cur->blocked_lock = NULL; |
| 265 | 265 | ||
| 266 | /* the priority of this lock is our own priority */ | 266 | /* the priority of this lock is our own priority */ |
| 267 | lock->priority = cur->priority; | 267 | lock->priority = thread_get_priority (); |
| 268 | 268 | ||
| 269 | /* save the locks we hold in descending order of their priority */ | 269 | /* save the locks we hold in descending order of their priority */ |
| 270 | list_insert_ordered (&lock->holder->locks, &lock->elem, | 270 | list_insert_ordered (&lock->holder->locks, &lock->elem, |
| @@ -305,7 +305,7 @@ lock_release (struct lock *lock) | |||
| 305 | ASSERT (lock_held_by_current_thread (lock)); | 305 | ASSERT (lock_held_by_current_thread (lock)); |
| 306 | 306 | ||
| 307 | lock->holder = NULL; | 307 | lock->holder = NULL; |
| 308 | //lock->priority = we don't care as the next lock_acquire()-call does | 308 | //lock->priority = we don't care since the next lock_acquire()-call does |
| 309 | sema_up (&lock->semaphore); | 309 | sema_up (&lock->semaphore); |
| 310 | 310 | ||
| 311 | /* we don't hold the lock any longer so remove it from the lock list */ | 311 | /* we don't hold the lock any longer so remove it from the lock list */ |
| @@ -424,9 +424,9 @@ cond_signal (struct condition *cond, struct lock *lock UNUSED) | |||
| 424 | this is the same as with sema_down()/sema_up() */ | 424 | this is the same as with sema_down()/sema_up() */ |
| 425 | if (!list_empty (&cond->waiters)) | 425 | if (!list_empty (&cond->waiters)) |
| 426 | { | 426 | { |
| 427 | list_sort (&cond->waiters, &semaphore_priority_cmp_greater, NULL); | 427 | list_sort (&cond->waiters, &semaphore_priority_cmp_greater, NULL); |
| 428 | sema_up (&list_entry (list_pop_front (&cond->waiters), | 428 | sema_up (&list_entry (list_pop_front (&cond->waiters), |
| 429 | struct semaphore_elem, elem)->semaphore); | 429 | struct semaphore_elem, elem)->semaphore); |
| 430 | } | 430 | } |
| 431 | } | 431 | } |
| 432 | 432 | ||
diff --git a/threads/thread.c b/threads/thread.c index 61ab5d9..cf404b6 100644 --- a/threads/thread.c +++ b/threads/thread.c | |||
| @@ -379,12 +379,26 @@ thread_set_priority (int new_priority) | |||
| 379 | void | 379 | void |
| 380 | thread_other_set_priority (struct thread *t, int new_priority) | 380 | thread_other_set_priority (struct thread *t, int new_priority) |
| 381 | { | 381 | { |
| 382 | enum intr_level old_level; | ||
| 383 | bool yield = false; | ||
| 384 | |||
| 382 | ASSERT (is_thread (t)); | 385 | ASSERT (is_thread (t)); |
| 383 | ASSERT (new_priority >= PRI_MIN && new_priority <= PRI_MAX); | 386 | ASSERT (new_priority >= PRI_MIN && new_priority <= PRI_MAX); |
| 384 | 387 | ||
| 385 | if (t->priority == new_priority) | 388 | if (t->priority == new_priority) |
| 386 | return; | 389 | return; |
| 387 | 390 | ||
| 391 | /* we need to disable interrupts here because after changing the thread's | ||
| 392 | priority and a possible timer interrupt which triggers thread_yield() we | ||
| 393 | could run into a couple of races. e.g. no proper sorted ready list, | ||
| 394 | corrupt ready list (wrong internal pointers) or the timer interrupt fires | ||
| 395 | after calling list_empty() but before list_front() while having only two | ||
| 396 | threads (one active, the other one ready). the scheduler then schedules the | ||
| 397 | other thread which runs and terminates, thus removing itself from all lists. | ||
| 398 | after that our thread gets scheduled again and reads from an empty list, | ||
| 399 | although it wasn't empty before. */ | ||
| 400 | old_level = intr_disable (); | ||
| 401 | |||
| 388 | t->priority = new_priority; | 402 | t->priority = new_priority; |
| 389 | 403 | ||
| 390 | if (t->status == THREAD_READY) | 404 | if (t->status == THREAD_READY) |
| @@ -398,8 +412,13 @@ thread_other_set_priority (struct thread *t, int new_priority) | |||
| 398 | /* compare priority with the highest priority in the list */ | 412 | /* compare priority with the highest priority in the list */ |
| 399 | struct thread *t2 = list_entry (list_front (&ready_list), struct thread, elem); | 413 | struct thread *t2 = list_entry (list_front (&ready_list), struct thread, elem); |
| 400 | if (t2->priority > t->priority) | 414 | if (t2->priority > t->priority) |
| 401 | thread_yield (); | 415 | yield = true; |
| 402 | } | 416 | } |
| 417 | |||
| 418 | intr_set_level (old_level); | ||
| 419 | |||
| 420 | if (yield) | ||
| 421 | thread_yield (); | ||
| 403 | } | 422 | } |
| 404 | 423 | ||
| 405 | void | 424 | void |
