summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2012-05-11 20:48:58 +0200
committermanuel <manuel@mausz.at>2012-05-11 20:48:58 +0200
commit9b19a84ed640cb15e6ca87ddc025c90d78a5fbdc (patch)
tree54345ae3ba366d1a4663e93b6b883e842f7a3918
parent4571d64e74dadd1b2169e8cd276f8a298decd5f0 (diff)
downloadprogos-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.txt53
-rw-r--r--threads/synch.c16
-rw-r--r--threads/thread.c21
3 files changed, 78 insertions, 12 deletions
diff --git a/proj1.txt b/proj1.txt
index 27b0d17..0cb22e1 100644
--- a/proj1.txt
+++ b/proj1.txt
@@ -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
37struct thread: 37struct 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
43struct lock: 45struct 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
47struct semaphore_elem: 50struct 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
57TODO
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
64TODO
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
69TODO
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
80thread_set_priority() first changes the priority and then has to either just
81re-sort the ready list or has to decide whether it should yield by comparing the
82priorities of the current thread and the thread with the highest priority in
83the ready list. the gap between changing the priority and doing the other stuff
84raises a few possible races if the timer interrupt triggers in between.
85e.g. no proper sorted ready list, corrupt ready list (wrong internal pointers)
86or the timer interrupt fires after calling list_empty() but before list_front()
87while having only two threads (one active, the other one ready). the scheduler
88then schedules the other thread which runs and perhaps terminates, thus removing
89itself from all lists. after that our thread gets scheduled again and reads from
90an empty list, although it wasn't empty before.
91
92Using a lock instead of disabling the interrupts would require locking the
93access to the ready list everywhere. This doesn't follow the already established
94conventions 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
101Acutally we didn't consider any other design. We first tried to solve the
102assignment without pre-planning and just started coding but that failed.
103Afterwards we looked at the various cases, did some drafts and decided on the
104required structure modifications. That did work quite well.
105
106In case of priority donation with locks our design needs only a few cpu
107instructions because we sort the list of locks during insertion and whenever a
108donated priority changes. This allows us to retreive the next donated priority
109with just looking at the first element in the list.
110
111In case of priority donation with semaphores and conditions we can't sort the
112blocked/waiting threads during insertion as their priority may change afterwards
113and we don't have any reference to currently used semaphores/conditions inside
114the thread structure. Thus we simply sort the whole list inside sema_up()/
115cond_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
129working on the priority donation without being able to use printf() inside
130lock_acquire()/lock_release() (as printf() requires a lock for the console
131itself) 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
140do 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)
379void 379void
380thread_other_set_priority (struct thread *t, int new_priority) 380thread_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
405void 424void