1283 \end{tabular} |
1283 \end{tabular} |
1284 \end{center} |
1284 \end{center} |
1285 |
1285 |
1286 \noindent |
1286 \noindent |
1287 Our implicit assumption that every event is an atomic operation is ensured by the architecture of |
1287 Our implicit assumption that every event is an atomic operation is ensured by the architecture of |
1288 PINTOS (which allows to disable interrupts when some operations are performed). The case where |
1288 PINTOS (which allows disabling of interrupts when some operations are performed). The case where |
1289 an unlocked resource is given next to the waiting thread with the |
1289 an unlocked resource is given next to the waiting thread with the |
1290 highest precedence is realised in our implementation by priority queues. We implemented |
1290 highest precedence is realised in our implementation by priority queues. We implemented |
1291 them as \emph{Braun trees} \cite{Paulson96}, which provide efficient @{text "O(log n)"}-operations |
1291 them as \emph{Braun trees} \cite{Paulson96}, which provide efficient @{text "O(log n)"}-operations |
1292 for accessing and updating. Apart from having to implement relatively complex data\-structures in C |
1292 for accessing and updating. Apart from having to implement relatively complex data\-structures in C |
1293 using pointers, our experience with the implementation has been very positive: our specification |
1293 using pointers, our experience with the implementation has been very positive: our specification |
1294 and formalisation of PIP translates smoothly to an efficent implementation in PINTOS. |
1294 and formalisation of PIP translates smoothly to an efficent implementation in PINTOS. |
1295 Let us illustrate this with the C-code of the function {\tt lock\_acquire}, |
1295 Let us illustrate this with the C-code for the function @{ML_text "lock_acquire"}, |
1296 shown in Figure~\ref{code}. This function implements the operation of requesting and, if free, |
1296 shown in Figure~\ref{code}. This function implements the operation of requesting and, if free, |
1297 locking of a resource by the currently running thread. The convention in the PINTOS |
1297 locking of a resource by the current running thread. The convention in the PINTOS |
1298 code is use the terminology \emph{locks} rather than resources. |
1298 code is to use the terminology \emph{locks} rather than resources. |
1299 A lock is represented as a pointer to the structure {\tt lock} (Line 1). |
1299 A lock is represented as a pointer to the structure {\tt lock} (Line 1). |
1300 |
1300 |
1301 Lines 2 to 4 of the function @{ML_text "lock_acquire"} contain diagnostic code: first, we check that |
1301 Lines 2 to 4 are taken from the original |
|
1302 code of @{ML_text "lock_acquire"} in PINTOS. They contain diagnostic code: first, |
|
1303 there is a check that |
1302 the lock is a ``valid'' lock |
1304 the lock is a ``valid'' lock |
1303 by testing whether it is not {\tt NULL}; second, we check that the code is not called |
1305 by testing whether it is not {\tt NULL}; second, a check that the code is not called |
1304 as part of an interrupt---acquiring a lock should only be initiated by a |
1306 as part of an interrupt---acquiring a lock should only be initiated by a |
1305 request from a (user) thread, not an interrupt; third, we make sure the |
1307 request from a (user) thread, not from an interrupt; third, it is ensured that the |
1306 current thread does not ask twice for a lock. These assertions are supposed |
1308 current thread does not ask twice for a lock. These assertions are supposed |
1307 to be satisfied because of the assumptions in PINTOS about how this code is called. |
1309 to be satisfied because of the assumptions in PINTOS about how this code is called. |
1308 If not, then the assertions indicate a bug in PINTOS and the result will be |
1310 If not, then the assertions indicate a bug in PINTOS and the result will be |
1309 a \emph{kernel panic}. We took these three lines from the original code of |
1311 a \emph{kernel panic}. |
1310 @{ML_text "lock_acquire"} in PINTOS. |
1312 |
1311 |
1313 |
1312 |
1314 |
1313 |
1315 \begin{figure}[t] |
1314 \begin{figure} |
|
1315 \begin{lstlisting} |
1316 \begin{lstlisting} |
1316 void lock_acquire (struct lock *lock) |
1317 void lock_acquire (struct lock *lock) |
1317 { ASSERT (lock != NULL); |
1318 { ASSERT (lock != NULL); |
1318 ASSERT (!intr_context()); |
1319 ASSERT (!intr_context()); |
1319 ASSERT (!lock_held_by_current_thread (lock)); |
1320 ASSERT (!lock_held_by_current_thread (lock)); |
1344 queue_insert(lock_prec, &thread_current()->held, &lock->helem); |
1345 queue_insert(lock_prec, &thread_current()->held, &lock->helem); |
1345 }; |
1346 }; |
1346 intr_set_level(old_level); |
1347 intr_set_level(old_level); |
1347 } |
1348 } |
1348 \end{lstlisting} |
1349 \end{lstlisting} |
1349 \caption{Our version of the {\tt lock\_release} function in |
1350 \caption{Our version of the {\tt lock\_release} function for the small operating |
1350 PINTOS. It implements the operation corresponding to a @{text P}-event.\label{code}} |
1351 system PINTOS. It implements the operation corresponding to a @{text P}-event.\label{code}} |
1351 \end{figure} |
1352 \end{figure} |
1352 |
1353 |
1353 |
1354 |
1354 Line 6 and 7 of {\tt lock\_acquire} make the operation of acquiring a lock atomic by disabling all |
1355 Line 6 and 7 of {\tt lock\_acquire} make the operation of acquiring a lock atomic by disabling all |
1355 interrupts, but saving them for resumption at the end of the function (Line 31). |
1356 interrupts, but saving them for resumption at the end of the function (Line 31). |
1356 In Line 8, the interesting code with respect to scheduling starts: we |
1357 In Line 8, the interesting code with respect to scheduling starts: we |
1357 first check whether the lock is already taken (its value is then 0 indicating ``already |
1358 first check whether the lock is already taken (its value is then 0 indicating ``already |
1358 taken'', or 1 for being ``free''). In case the lock is taken, we enter the |
1359 taken'', or 1 for being ``free''). In case the lock is taken, we enter the |
1359 if-branch inserting the current thread into the waiting queue of this lock (Line 9). |
1360 if-branch inserting the current thread into the waiting queue of this lock (Line 9). |
1360 The waiting queue is referenced in the usual C-way as @{ML_text "&lock->wq"}. |
1361 The waiting queue is referenced in the usual C-way as @{ML_text "&lock->wq"}. |
1361 Next we record that the current thread is waiting for the lock (Line 10). |
1362 Next, we record that the current thread is waiting for the lock (Line 10). |
1362 Thus we established two pointers: one in the waiting queue of the lock pointing to the |
1363 Thus we established two pointers: one in the waiting queue of the lock pointing to the |
1363 current thread, and the other from the currend thread pointing to the lock. |
1364 current thread, and the other from the currend thread pointing to the lock. |
1364 According to our specification in Section~\ref{model} and the properties we were able |
1365 According to our specification in Section~\ref{model} and the properties we were able |
1365 to prove for @{text P}, we need to ``chase'' all the dependants |
1366 to prove for @{text P}, we need to ``chase'' all the dependants |
1366 in the RAG (Resource Allocation Graph) and update their |
1367 in the RAG (Resource Allocation Graph) and update their |
1367 current precedence, however we only have to do this as long as there is change in the |
1368 current precedence; however we only have to do this as long as there is change in the |
1368 current precedence from one thread at hand to another. |
1369 current precedence from one thread at hand to another. |
1369 |
1370 |
1370 The ``chase'' is implemented in the while-loop in Lines 13 to 24. |
1371 The ``chase'' is implemented in the while-loop in Lines 13 to 24. |
1371 To initialise the loop, we |
1372 To initialise the loop, we |
1372 assign in Lines 11 and 12 the variable @{ML_text pt} to the owner |
1373 assign in Lines 11 and 12 the variable @{ML_text pt} to the owner |
1374 Inside the loop, we first update the precedence of the lock held by @{ML_text pt} (Line 14). |
1375 Inside the loop, we first update the precedence of the lock held by @{ML_text pt} (Line 14). |
1375 Next, we check whether there is a change in the current precedence of @{ML_text pt}. If not, |
1376 Next, we check whether there is a change in the current precedence of @{ML_text pt}. If not, |
1376 then we leave the loop, since nothing else needs to be updated (Lines 15 and 16). |
1377 then we leave the loop, since nothing else needs to be updated (Lines 15 and 16). |
1377 If there is a change, then we have to continue our ``chase''. We check what lock the |
1378 If there is a change, then we have to continue our ``chase''. We check what lock the |
1378 thread @{ML_text pt} is waiting for (Lines 17 and 18). If there is none, then |
1379 thread @{ML_text pt} is waiting for (Lines 17 and 18). If there is none, then |
1379 the thread @{ML_text pt} is ready (the ``chase'' is finished with finding a root). In this |
1380 the thread @{ML_text pt} is ready (the ``chase'' is finished with finding a root in the RAG). In this |
1380 case we update the ready-queue accordingly (Lines 19 and 20). If there is a lock @{ML_text pt} is |
1381 case we update the ready-queue accordingly (Lines 19 and 20). If there is a lock @{ML_text pt} is |
1381 waiting for, we update the waiting queue for this lock and we continue the loop with |
1382 waiting for, we update the waiting queue for this lock and we continue the loop with |
1382 the holder of that lock |
1383 the holder of that lock |
1383 (Lines 22 and 23). After all current precedences have been updated, we finally need |
1384 (Lines 22 and 23). After all current precedences have been updated, we finally need |
1384 to block the current thread, because the lock it asked for was taken (Line 25). |
1385 to block the current thread, because the lock it asked for was taken (Line 25). |
1386 If the lock the current thread asked for is \emph{not} taken, we proceed with the else-branch |
1387 If the lock the current thread asked for is \emph{not} taken, we proceed with the else-branch |
1387 (Lines 26 to 30). We first decrease the value of the lock to 0, meaning |
1388 (Lines 26 to 30). We first decrease the value of the lock to 0, meaning |
1388 it is taken now (Line 27). Second, we update the reference of the holder of |
1389 it is taken now (Line 27). Second, we update the reference of the holder of |
1389 the lock (Line 28), and finally update the queue of locks the current |
1390 the lock (Line 28), and finally update the queue of locks the current |
1390 thread already possesses (Line 29). |
1391 thread already possesses (Line 29). |
1391 The very last is to enable interrupts again thus leaving the protected section. |
1392 The very last step is to enable interrupts again thus leaving the protected section. |
1392 |
1393 |
1393 |
1394 |
1394 Similar operations need to be implementated for the @{ML_text lock_release} function, which |
1395 Similar operations need to be implementated for the @{ML_text lock_release} function, which |
1395 we however do not show. The reader should note that we did not verify our C-code. The |
1396 we however do not show. The reader should note though that we did \emph{not} verify our C-code. |
1396 verification of the specification however provided us with the justification for designing |
1397 This is in contrast, for example, to the work on seL4, which actually verified in Isabelle/HOL |
1397 the C-code in this way. It gave us confidence for leaving the ``chase'' early whenever |
1398 that their C-code satisfies its specification \cite{sel4}. |
1398 there is no change in the calculated current precedence. |
1399 Our verification however provided us with the justification for designing |
|
1400 the C-code. It gave us confidence that leaving the ``chase'' early, whenever |
|
1401 there is no change in the calculated current precedence, does not break the |
|
1402 correctness of the algorithm. |
1399 *} |
1403 *} |
1400 |
1404 |
1401 section {* Conclusion *} |
1405 section {* Conclusion *} |
1402 |
1406 |
1403 text {* |
1407 text {* |