Journal/Paper.thy
changeset 14 1bf194825a4e
parent 13 735e36c64a71
child 15 9e664c268e25
equal deleted inserted replaced
13:735e36c64a71 14:1bf194825a4e
    95   in the Mars Pathfinder mission, in Boeing's 787 Dreamliner, Honda's
    95   in the Mars Pathfinder mission, in Boeing's 787 Dreamliner, Honda's
    96   ASIMO robot, etc.), but also the POSIX 1003.1c Standard realised for
    96   ASIMO robot, etc.), but also the POSIX 1003.1c Standard realised for
    97   example in libraries for FreeBSD, Solaris and Linux. 
    97   example in libraries for FreeBSD, Solaris and Linux. 
    98 
    98 
    99   One advantage of PIP is that increasing the priority of a thread
    99   One advantage of PIP is that increasing the priority of a thread
   100   can be dynamically calculated by the scheduler. This is in contrast
   100   can be performed dynamically by the scheduler. This is in contrast
   101   to, for example, \emph{Priority Ceiling} \cite{Sha90}, another
   101   to, for example, \emph{Priority Ceiling} \cite{Sha90}, another
   102   solution to the Priority Inversion problem, which requires static
   102   solution to the Priority Inversion problem, which requires static
   103   analysis of the program in order to prevent Priority
   103   analysis of the program in order to prevent Priority
   104   Inversion. However, there has also been strong criticism against
   104   Inversion. However, there has also been strong criticism against
   105   PIP. For instance, PIP cannot prevent deadlocks when lock
   105   PIP. For instance, PIP cannot prevent deadlocks when lock
   181   quickly.  We were also able to generalise the scheduler of Sha
   181   quickly.  We were also able to generalise the scheduler of Sha
   182   et al.~\cite{Sha90} to the practically relevant case where critical 
   182   et al.~\cite{Sha90} to the practically relevant case where critical 
   183   sections can overlap.
   183   sections can overlap.
   184 *}
   184 *}
   185 
   185 
   186 section {* Formal Model of the Priority Inheritance Protocol *}
   186 section {* Formal Model of the Priority Inheritance Protocol\label{model} *}
   187 
   187 
   188 text {*
   188 text {*
   189   The Priority Inheritance Protocol, short PIP, is a scheduling
   189   The Priority Inheritance Protocol, short PIP, is a scheduling
   190   algorithm for a single-processor system.\footnote{We shall come back
   190   algorithm for a single-processor system.\footnote{We shall come back
   191   later to the case of PIP on multi-processor systems.} 
   191   later to the case of PIP on multi-processor systems.} 
  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 of the function {\tt lock\_acquire}, 
  1296   shown in Figure~\ref{code}.  This function implements the operation that
  1296   shown in Figure~\ref{code}.  This function implements the operation of requesting and, if free, 
  1297   the currently running thread asks for the lock of a resource. 
  1297   locking of a resource by the currently running thread. The convention in the PINTOS
  1298   In C such a lock is represented as a pointer to the structure {\tt lock} (Line 1). 
  1298   code is use the terminology \emph{locks} rather than resources. 
  1299 
  1299   A lock is represented as a pointer to the structure {\tt lock} (Line 1). 
  1300   Lines 2 to 4 of {\tt lock\_acquire} contain diagnostic code: first, we check that 
  1300 
       
  1301   Lines 2 to 4 of the function @{ML_text "lock_acquire"} contain diagnostic code: first, we check that 
  1301   the lock is a ``valid'' lock 
  1302   the lock is a ``valid'' lock 
  1302   by testing it is not {\tt NULL}; second, we check that the code is not called
  1303   by testing whether it is not {\tt NULL}; second, we check that the code is not called
  1303   as part of an interrupt---acquiring a lock should only be initiated by a 
  1304   as part of an interrupt---acquiring a lock should only be initiated by a 
  1304   request from a (user) thread, not an interrupt; third, we make sure the 
  1305   request from a (user) thread, not an interrupt; third, we make sure the 
  1305   current thread does not ask twice for a lock. These assertions are supposed
  1306   current thread does not ask twice for a lock. These assertions are supposed
  1306   to be satisfied because of the assumptions in PINTOS about how this code is called.
  1307   to be satisfied because of the assumptions in PINTOS about how this code is called.
  1307   If not, then the assertions indicate a bug in PINTOS.
  1308   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 
       
  1310   @{ML_text "lock_acquire"} in PINTOS.
  1308 
  1311 
  1309 
  1312 
  1310 
  1313 
  1311   \begin{figure}
  1314   \begin{figure}
  1312   \begin{lstlisting}
  1315   \begin{lstlisting}
  1349 
  1352 
  1350  
  1353  
  1351   Line 6 and 7 of {\tt lock\_acquire} make the operation of acquiring a lock atomic by disabling all 
  1354   Line 6 and 7 of {\tt lock\_acquire} make the operation of acquiring a lock atomic by disabling all 
  1352   interrupts, but saving them for resumption at the end of the function (Line 31).
  1355   interrupts, but saving them for resumption at the end of the function (Line 31).
  1353   In Line 8, the interesting code with respect to scheduling starts: we 
  1356   In Line 8, the interesting code with respect to scheduling starts: we 
  1354   test whether the lock is already taken (its value is then 0 indicating ``already 
  1357   first check whether the lock is already taken (its value is then 0 indicating ``already 
  1355   taken'', or 1 for being ``free''). In case the lock is taken, we enter the
  1358   taken'', or 1 for being ``free''). In case the lock is taken, we enter the
  1356   if-branch by first inserting the current thread into the waiting queue of this lock (Line 9).
  1359   if-branch inserting the current thread into the waiting queue of this lock (Line 9).
  1357   The waiting queue is referenced in the usual C-way as @{ML_text "&lock->wq"}. 
  1360   The waiting queue is referenced in the usual C-way as @{ML_text "&lock->wq"}. 
  1358   Next we record that the current thread is waiting for the lock (Line 10).
  1361   Next we record that the current thread is waiting for the lock (Line 10).
  1359   According to our specification, we need to next ``chase'' the dependants 
  1362   Thus we established two pointers: one in the waiting queue of the lock pointing to the 
  1360   in the RAG (Resource Allocation Graph), but we can stop the ``chase'' whenever 
  1363   current thread, and the other from the currend thread pointing to the lock.
  1361   there is no change in the @{text cprec}. To start the loop, we 
  1364   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   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 from one thread at hand to another. 
       
  1369 
       
  1370   The ``chase'' is implemented in the while-loop in Lines 13 to 24. 
       
  1371   To initialise the loop, we 
  1362   assign in Lines 11 and 12 the variable @{ML_text pt} to the owner 
  1372   assign in Lines 11 and 12 the variable @{ML_text pt} to the owner 
  1363   of the lock, and then enter the while-loop in Lines 13 to 24. 
  1373   of the lock.
       
  1374   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   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   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   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   the holder of that lock 
       
  1383   (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 
       
  1386   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   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   thread already possesses (Line 29).
       
  1391   The very last is to enable interrupts again thus leaving the protected section.
  1364   
  1392   
  1365   Inside the loop, we first update the precedence of the lock held by @{ML_text pt} (Line 14).
  1393 
  1366   Next, we test if there is a change in the current precedence of @{ML_text pt}. If not,
       
  1367   then we leave the loop since nothing else needs to be updated (Lines 15 and 16).
       
  1368   If there is a change, then we have to continue our ``chase''. We check the lock
       
  1369   @{ML_text pt} is waiting for (Lines 17 and 18). If there isn't one, the thread @{ML_text pt}
       
  1370   is ready (the ``chase'' finished with finding a root) and we update the ready-queue accordingly
       
  1371   (Lines 19 and 20). If there is a lock  @{ML_text pt} is waiting for, we update the
       
  1372   waiting queue for this lock and we continue the loop with the holder of that lock 
       
  1373   (Lines 22 and 23). Finally we need to block the current thread, since the lock 
       
  1374   it asked for was taken (Line 25). 
       
  1375 
       
  1376   If the lock was not taken, we take the else-branch (Lines 26 to 30). We first 
       
  1377   decrease the value of the lock to 0, meaning it is taken now (Line 27).
       
  1378   We update the reference of holder of the lock (Line 28) and finally update
       
  1379   the queue of locks the current thread already possesses (Line 29).
       
  1380   Finally, we enable interrupts again, leaving the protected section of PINTOS.
       
  1381   Similar operations need to be implementated for the @{ML_text lock_release} function, which
  1394   Similar operations need to be implementated for the @{ML_text lock_release} function, which
  1382   we omit.
  1395   we however do not show. The reader should note that we did not verify our C-code. The
       
  1396   verification of the specification however provided us with the justification for designing 
       
  1397   the C-code in this way. It gave us confidence for leaving the ``chase'' early whenever
       
  1398   there is no change in the calculated current precedence.
  1383 *}
  1399 *}
  1384 
  1400 
  1385 section {* Conclusion *}
  1401 section {* Conclusion *}
  1386 
  1402 
  1387 text {* 
  1403 text {* 
  1418   design choices for the implementation of PIP scheduler are backed up with a proved
  1434   design choices for the implementation of PIP scheduler are backed up with a proved
  1419   lemma. We were also able to establish the property that the choice of
  1435   lemma. We were also able to establish the property that the choice of
  1420   the next thread which takes over a lock is irrelevant for the correctness
  1436   the next thread which takes over a lock is irrelevant for the correctness
  1421   of PIP. Moreover, we eliminated a crucial restriction present in 
  1437   of PIP. Moreover, we eliminated a crucial restriction present in 
  1422   the proof of Sha et al.: they require that critical sections nest properly, 
  1438   the proof of Sha et al.: they require that critical sections nest properly, 
  1423   whereas our scheduler allows critical sections to overlap. This is the default
  1439   whereas our scheduler allows critical sections to overlap. 
  1424   in implementations of PIP. 
       
  1425 
  1440 
  1426   PIP is a scheduling algorithm for single-processor systems. We are
  1441   PIP is a scheduling algorithm for single-processor systems. We are
  1427   now living in a multi-processor world. Priority Inversion certainly
  1442   now living in a multi-processor world. Priority Inversion certainly
  1428   occurs also there.  However, there is very little ``foundational''
  1443   occurs also there.  However, there is very little ``foundational''
  1429   work about PIP-algorithms on multi-processor systems.  We are not
  1444   work about PIP-algorithms on multi-processor systems.  We are not