Journal/Paper.thy
changeset 15 9e664c268e25
parent 14 1bf194825a4e
child 16 9764023f719e
equal deleted inserted replaced
14:1bf194825a4e 15:9e664c268e25
  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 {*