diff options
| author | manuel <manuel@mausz.at> | 2012-05-13 18:30:41 +0200 |
|---|---|---|
| committer | manuel <manuel@mausz.at> | 2012-05-13 18:30:41 +0200 |
| commit | d9e0c55d118d0a3923b440b7811f8d1d6db9e1d7 (patch) | |
| tree | 0eb722e0b7ead2cc993e572c2a067aed322ee60b | |
| parent | 8e4412f3270de359d18ed76d951bf6d4cabd6bbd (diff) | |
| download | progos-d9e0c55d118d0a3923b440b7811f8d1d6db9e1d7.tar.gz progos-d9e0c55d118d0a3923b440b7811f8d1d6db9e1d7.tar.bz2 progos-d9e0c55d118d0a3923b440b7811f8d1d6db9e1d7.zip | |
fill in the missing questions of proj1.txtproj1
| -rw-r--r-- | proj1.txt | 126 |
1 files changed, 113 insertions, 13 deletions
| @@ -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 | ||
| 57 | TODO | 57 | In case a priority gets donated to a thread we store the origin/base priority |
| 58 | in the variable "saved_priority" inside the structure of the thread itself. | ||
| 59 | Additional we flip the boolean variable "is_donated". | ||
| 60 | For multiple priority donations we store the maximum donated priority to a lock | ||
| 61 | inside the structure of a lock. Additional we store an ordered list of locks a | ||
| 62 | thread is currently holding. This combination allows us to donate a priority to | ||
| 63 | a lock and alter the holders priority. In case this lock gets released we either | ||
| 64 | restore the holder's base/origin priority (if the lock list is empty) or change | ||
| 65 | the holder's priority to highest priority donated by the locks the thread still | ||
| 66 | holds (the lock list is not empty). See B5 for additional information. | ||
| 67 | |||
| 68 | For nested priority donation we additional store a pointer to the lock the | ||
| 69 | thread is waiting on ("blocked_lock"). We then walk recursively from the current | ||
| 70 | lock to the lock the holder of the current lock is waiting on and at the same | ||
| 71 | time donate the thread's priority to the occurring holders. The loop ends as | ||
| 72 | soon as no holder of a lock in this chain is waiting on another lock. See B4 for | ||
| 73 | additional information. | ||
| 74 | |||
| 75 | Nested 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 | |||
| 103 | L.locks = [A] | ||
| 104 | L.blocked_lock = NULL | ||
| 105 | M.locks = [B] | ||
| 106 | M.blocked_lock = A | ||
| 107 | H.locks = [] | ||
| 108 | H.blocked_lock = B | ||
| 109 | |||
| 110 | H enters the nested priority loop and donates its priority to | ||
| 111 | H.blocked_lock.holder (=M): | ||
| 112 | => M.priority = H | ||
| 113 | |||
| 114 | H donates its priority to M.blocked_lock.holder (=L): | ||
| 115 | => L.priority = H | ||
| 116 | |||
| 117 | Loop 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 | ||
| 64 | TODO | 124 | We use list_sort() inside sema_up() to sort the list of blocked/waiting threads |
| 125 | by their priority. Afterwards we unblock the first thread in the list which is | ||
| 126 | the thread with the highest priority. | ||
| 127 | This ensures working locks as well (lock implementation uses semaphores). | ||
| 128 | |||
| 129 | For conditions the list of blocked/waiting threads consists of elements of the | ||
| 130 | structure semaphore_elem which itself contains a semaphore. In order to sort | ||
| 131 | this list by the thread's priority we simply added a pointer pointing to the | ||
| 132 | thread structure owning the element in the list. Just as in sema_up() we use | ||
| 133 | list_sort() inside cond_signal() to sort the list and unblock the thread with | ||
| 134 | the highest priority. | ||
| 135 | |||
| 136 | See 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 | ||
| 69 | TODO | 141 | In lock_acquire() we first check if the lock is already held by another thread. |
| 142 | In case there is a holder we check for a possible priority donation by comparing | ||
| 143 | the thread's priority with the priority of the lock which is the same value as | ||
| 144 | the current priority of the holder. | ||
| 145 | We need this additional priority variable per lock because a thread may hold | ||
| 146 | multiple locks which could donate any number of priorities to this thread. | ||
| 147 | As soon as the thread releases one of these locks we need to change the thread's | ||
| 148 | priority to the highest (donated) priority of the other locks the thread still | ||
| 149 | holds. See B5 on how we accomplish this. | ||
| 150 | |||
| 151 | For nested priority donation we not only have to donate the thread's priority to | ||
| 152 | the holder of the lock the thread currently is waiting on but also have to look | ||
| 153 | into the depth if the holder itself is waiting on another lock too. This | ||
| 154 | lookup/recursion continues until a thread holding a lock in the chain isn't | ||
| 155 | waiting on another lock (blocked_lock isn't set). | ||
| 156 | For every lock in this chain we donate the priority of the current thread to | ||
| 157 | their 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 | ||
| 162 | In order to handle multiple priority donations to the same lock we not only | ||
| 163 | store the currently donated priority inside the lock structure (as already | ||
| 164 | described in B4) but also a list of currently held locks of a thread. This list | ||
| 165 | is ordered by the locks priority. In lock_release() we first check if this | ||
| 166 | ordered lock list is empty and then call thread_donation_pass_back() which | ||
| 167 | restores the origin priority of the thread. | ||
| 168 | In case the list is not empty we simply retreive the first lock and change the | ||
| 169 | thread's current priority to the priority of the lock. This is accomplished by | ||
| 170 | calling thread_other_set_priority() which yields the cpu in case the new | ||
| 171 | priority is lower than the priority of the thread with the highest priority in | ||
| 172 | the 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 | |||
| 80 | thread_set_priority() first changes the priority and then has to either just | 180 | 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 | 181 | 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 | 182 | 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 | 183 | 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. | 184 | 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) | 185 | 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() | 186 | 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 | 187 | 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 | 188 | 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 | 189 | itself from all lists. After that our thread gets scheduled again and reads from |
| 90 | an empty list, although it wasn't empty before. | 190 | an empty list, although it wasn't empty before. |
| 91 | 191 | ||
| 92 | Using a lock instead of disabling the interrupts would require locking the | 192 | Using 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 | ||
| 101 | Acutally we didn't consider any other design. We first tried to solve the | 201 | Actually 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. | 202 | 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 | 203 | Afterwards we looked at the various cases, did some drafts and decided on the |
| 104 | required structure modifications. That did work quite well. | 204 | required structure modifications. That did work quite well. |
| 105 | 205 | ||
| 106 | In case of priority donation with locks our design needs only a few cpu | 206 | In case of priority donation with locks our design only consumes little cpu |
| 107 | instructions because we sort the list of locks during insertion and whenever a | 207 | instructions because we sort the list of locks by their donated priority upon |
| 108 | donated priority changes. This allows us to retreive the next donated priority | 208 | insertion and whenever a donated priority changes. This allows us to retrieve |
| 109 | with just looking at the first element in the list. | 209 | the next donated priority with just looking at the first element in the list. |
| 110 | 210 | ||
| 111 | In case of priority donation with semaphores and conditions we can't sort the | 211 | 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 | 212 | blocked/waiting threads on insertion as their priority may change afterwards |
| 113 | and we don't have any reference to currently used semaphores/conditions inside | 213 | 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()/ | 214 | the thread structure. Thus we simply sort the whole list inside sema_up()/ |
| 115 | cond_signal() before unblocking the next thread. | 215 | cond_signal() before unblocking the next thread. |
