summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--proj1.txt126
1 files changed, 113 insertions, 13 deletions
diff --git a/proj1.txt b/proj1.txt
index 0cb22e1..03c61f0 100644
--- a/proj1.txt
+++ b/proj1.txt
@@ -54,23 +54,123 @@ struct semaphore_elem:
54>> Use ASCII art to diagram a nested donation. (Alternately, submit a 54>> Use ASCII art to diagram a nested donation. (Alternately, submit a
55>> .png file.) 55>> .png file.)
56 56
57TODO 57In case a priority gets donated to a thread we store the origin/base priority
58in the variable "saved_priority" inside the structure of the thread itself.
59Additional we flip the boolean variable "is_donated".
60For multiple priority donations we store the maximum donated priority to a lock
61inside the structure of a lock. Additional we store an ordered list of locks a
62thread is currently holding. This combination allows us to donate a priority to
63a lock and alter the holders priority. In case this lock gets released we either
64restore the holder's base/origin priority (if the lock list is empty) or change
65the holder's priority to highest priority donated by the locks the thread still
66holds (the lock list is not empty). See B5 for additional information.
67
68For nested priority donation we additional store a pointer to the lock the
69thread is waiting on ("blocked_lock"). We then walk recursively from the current
70lock to the lock the holder of the current lock is waiting on and at the same
71time donate the thread's priority to the occurring holders. The loop ends as
72soon as no holder of a lock in this chain is waiting on another lock. See B4 for
73additional information.
74
75Nested donation example:
76* 3 threads with different priority
77* 2 locks
78* L holds lock A
79* M holds lock B and is waiting on lock A
80* H is waiting on lock B
81
82 _____
83| | Legend:
84| L | x ...thread holds lock
85|_____| o ...thread is waiting on lock
86 |
87/--|---------------------------\
88| x o Lock A |
89\--------|---------------------/
90 __|__
91 | |
92 | M |
93 |_____|
94 |
95/--------|---------------------\
96| x o Lock B |
97\--------------|---------------/
98 __|__
99 | |
100 | H |
101 |_____|
102
103L.locks = [A]
104L.blocked_lock = NULL
105M.locks = [B]
106M.blocked_lock = A
107H.locks = []
108H.blocked_lock = B
109
110H enters the nested priority loop and donates its priority to
111H.blocked_lock.holder (=M):
112=> M.priority = H
113
114H donates its priority to M.blocked_lock.holder (=L):
115=> L.priority = H
116
117Loop ends as L.blocked_lock is NULL
58 118
59---- ALGORITHMS ---- 119---- ALGORITHMS ----
60 120
61>> B3: How do you ensure that the highest priority thread waiting for 121>> B3: How do you ensure that the highest priority thread waiting for
62>> a lock, semaphore, or condition variable wakes up first? 122>> a lock, semaphore, or condition variable wakes up first?
63 123
64TODO 124We use list_sort() inside sema_up() to sort the list of blocked/waiting threads
125by their priority. Afterwards we unblock the first thread in the list which is
126the thread with the highest priority.
127This ensures working locks as well (lock implementation uses semaphores).
128
129For conditions the list of blocked/waiting threads consists of elements of the
130structure semaphore_elem which itself contains a semaphore. In order to sort
131this list by the thread's priority we simply added a pointer pointing to the
132thread structure owning the element in the list. Just as in sema_up() we use
133list_sort() inside cond_signal() to sort the list and unblock the thread with
134the highest priority.
135
136See B7 for a reason why used list_sort() instead of list_insert_ordered().
65 137
66>> B4: Describe the sequence of events when a call to lock_acquire() 138>> B4: Describe the sequence of events when a call to lock_acquire()
67>> causes a priority donation. How is nested donation handled? 139>> causes a priority donation. How is nested donation handled?
68 140
69TODO 141In lock_acquire() we first check if the lock is already held by another thread.
142In case there is a holder we check for a possible priority donation by comparing
143the thread's priority with the priority of the lock which is the same value as
144the current priority of the holder.
145We need this additional priority variable per lock because a thread may hold
146multiple locks which could donate any number of priorities to this thread.
147As soon as the thread releases one of these locks we need to change the thread's
148priority to the highest (donated) priority of the other locks the thread still
149holds. See B5 on how we accomplish this.
150
151For nested priority donation we not only have to donate the thread's priority to
152the holder of the lock the thread currently is waiting on but also have to look
153into the depth if the holder itself is waiting on another lock too. This
154lookup/recursion continues until a thread holding a lock in the chain isn't
155waiting on another lock (blocked_lock isn't set).
156For every lock in this chain we donate the priority of the current thread to
157their holder.
70 158
71>> B5: Describe the sequence of events when lock_release() is called 159>> B5: Describe the sequence of events when lock_release() is called
72>> on a lock that a higher-priority thread is waiting for. 160>> on a lock that a higher-priority thread is waiting for.
73 161
162In order to handle multiple priority donations to the same lock we not only
163store the currently donated priority inside the lock structure (as already
164described in B4) but also a list of currently held locks of a thread. This list
165is ordered by the locks priority. In lock_release() we first check if this
166ordered lock list is empty and then call thread_donation_pass_back() which
167restores the origin priority of the thread.
168In case the list is not empty we simply retreive the first lock and change the
169thread's current priority to the priority of the lock. This is accomplished by
170calling thread_other_set_priority() which yields the cpu in case the new
171priority is lower than the priority of the thread with the highest priority in
172the ready list.
173
74---- SYNCHRONIZATION ---- 174---- SYNCHRONIZATION ----
75 175
76>> B6: Describe a potential race in thread_set_priority() and explain 176>> B6: Describe a potential race in thread_set_priority() and explain
@@ -80,13 +180,13 @@ TODO
80thread_set_priority() first changes the priority and then has to either just 180thread_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 181re-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 182priorities 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 183the 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. 184raises a few possible races if the timer interrupt triggers in between.
85e.g. no proper sorted ready list, corrupt ready list (wrong internal pointers) 185E.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() 186or 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 187while having only two threads (one active, the other one ready). The scheduler
88then schedules the other thread which runs and perhaps terminates, thus removing 188then 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 189itself from all lists. After that our thread gets scheduled again and reads from
90an empty list, although it wasn't empty before. 190an empty list, although it wasn't empty before.
91 191
92Using a lock instead of disabling the interrupts would require locking the 192Using a lock instead of disabling the interrupts would require locking the
@@ -98,18 +198,18 @@ conventions and would rather be a lot more cpu intensive.
98>> B7: Why did you choose this design? In what ways is it superior to 198>> B7: Why did you choose this design? In what ways is it superior to
99>> another design you considered? 199>> another design you considered?
100 200
101Acutally we didn't consider any other design. We first tried to solve the 201Actually we didn't consider any other design. We first tried to solve the
102assignment without pre-planning and just started coding but that failed. 202assignment without pre-planning and just started coding but that failed.
103Afterwards we looked at the various cases, did some drafts and decided on the 203Afterwards we looked at the various cases, did some drafts and decided on the
104required structure modifications. That did work quite well. 204required structure modifications. That did work quite well.
105 205
106In case of priority donation with locks our design needs only a few cpu 206In case of priority donation with locks our design only consumes little cpu
107instructions because we sort the list of locks during insertion and whenever a 207instructions because we sort the list of locks by their donated priority upon
108donated priority changes. This allows us to retreive the next donated priority 208insertion and whenever a donated priority changes. This allows us to retrieve
109with just looking at the first element in the list. 209the next donated priority with just looking at the first element in the list.
110 210
111In case of priority donation with semaphores and conditions we can't sort the 211In case of priority donation with semaphores and conditions we can't sort the
112blocked/waiting threads during insertion as their priority may change afterwards 212blocked/waiting threads on insertion as their priority may change afterwards
113and we don't have any reference to currently used semaphores/conditions inside 213and 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()/ 214the thread structure. Thus we simply sort the whole list inside sema_up()/
115cond_signal() before unblocking the next thread. 215cond_signal() before unblocking the next thread.