- File:
-
- 1 edited
-
doc/papers/concurrency/Paper.tex (modified) (32 diffs)
Legend:
- Unmodified
- Added
- Removed
-
doc/papers/concurrency/Paper.tex
rf6f0d06f r08b5a7e 56 56 \newcommand{\Textbf}[2][red]{{\color{#1}{\textbf{#2}}}} 57 57 \newcommand{\Emph}[2][red]{{\color{#1}\textbf{\emph{#2}}}} 58 \newcommand{\R}[1]{\Textbf{#1}}59 \newcommand{\B}[1]{{\Textbf[blue]{#1}}}60 \newcommand{\G}[1]{{\Textbf[OliveGreen]{#1}}}61 58 \newcommand{\uC}{$\mu$\CC} 62 \newcommand{\cit}{\textsuperscript{[Citation Needed]}\xspace} 63 \newcommand{\TODO}{{\Textbf{TODO}}} 59 \newcommand{\TODO}[1]{{\Textbf{#1}}} 64 60 65 61 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% … … 217 213 \lstMakeShortInline@% 218 214 219 \newcommand{\commenttd}[1]{{\color{red}{Thierry : #1}}}220 221 215 \let\OLDthebibliography\thebibliography 222 216 \renewcommand\thebibliography[1]{ … … 260 254 \section{Introduction} 261 255 262 This paper provides a minimal concurrency \newterm{A bstractProgram Interface} (API) that is simple, efficient and can be used to build other concurrency features.256 This paper provides a minimal concurrency \newterm{Application Program Interface} (API) that is simple, efficient and can be used to build other concurrency features. 263 257 While the simplest concurrency system is a thread and a lock, this low-level approach is hard to master. 264 258 An easier approach for programmers is to support higher-level constructs as the basis of concurrency. … … 310 304 `&`r3 = &y; `&&`r3 = &`&`r4; // change r1, r2: cancel implicit dereferences (&*)**r3, (&(&*)*)*r3, &(&*)r4 311 305 \end{cfa} 312 A reference is a handle to an object, like a pointer, but is automatically dereferenced bythe specified number of levels.306 A reference is a handle to an object, like a pointer, but is automatically dereferenced the specified number of levels. 313 307 Referencing (address-of @&@) a reference variable cancels one of the implicit dereferences, until there are no more implicit references, after which normal expression behaviour applies. 314 308 … … 480 474 481 475 The signature feature of \CFA is parametric-polymorphic routines~\cite{} with routines generalized using a @forall@ clause (giving the language its name), which allow separately compiled routines to support generic usage over multiple types. 482 For example, the following sum routine works for any type that supports construction from 0 and addition \commenttd{constructors have not been introduced yet.}:476 For example, the following sum routine works for any type that supports construction from 0 and addition: 483 477 \begin{cfa} 484 478 forall( otype T | { void `?{}`( T *, zero_t ); T `?+?`( T, T ); } ) // constraint type, 0 and + … … 532 526 { 533 527 VLA x, y = { 20, 0x01 }, z = y; $\C{// z points to y}$ 534 // x{}; y{ 20, 0x01 }; z{ z, y }; 528 // x{}; y{ 20, 0x01 }; z{ z, y }; 535 529 ^x{}; $\C{// deallocate x}$ 536 530 x{}; $\C{// reallocate x}$ … … 569 563 The resulting execution system now follows a cooperative threading-model, called \newterm{non-preemptive scheduling}. 570 564 571 Because the scheduler is special, it can either be a stackless or stackfull coroutine. \commenttd{I dislike this sentence, it seems imply 1-step vs 2-step but also seems to say that some kind of coroutine is required, which is not the case.}565 Because the scheduler is special, it can either be a stackless or stackfull coroutine. 572 566 For stackless, the scheduler performs scheduling on the stack of the current coroutine and switches directly to the next coroutine, so there is one context switch. 573 567 For stackfull, the current coroutine switches to the scheduler, which performs scheduling, and it then switches to the next coroutine, so there are two context switches. 574 A stackfull scheduler is often used for simplicity and security, even through there is a slightly higher runtime-cost. \commenttd{I'm not a fan of the fact that we don't quantify this but yet imply it is negligeable.}568 A stackfull scheduler is often used for simplicity and security, even through there is a slightly higher runtime-cost. 575 569 576 570 Regardless of the approach used, a subset of concurrency related challenges start to appear. … … 589 583 As such, library support for threading is far from widespread. 590 584 At the time of writing the paper, neither \protect\lstinline|gcc| nor \protect\lstinline|clang| support ``threads.h'' in their standard libraries.}. 591 On modern architectures, a lack of threading is unacceptable~\cite{Sutter05, Sutter05b}, and therefore existing and new programming languages must have tools for writing efficient concurrent programs to take advantage of parallelism.585 In modern programming languages, a lack of threading is unacceptable~\cite{Sutter05, Sutter05b}, and therefore existing and new programming languages must have tools for writing efficient concurrent programs to take advantage of parallelism. 592 586 As an extension of C, \CFA needs to express these concepts in a way that is as natural as possible to programmers familiar with imperative languages. 593 587 Furthermore, because C is a system-level language, programmers expect to choose precisely which features they need and which cost they are willing to pay. … … 627 621 \newbox\myboxA 628 622 \begin{lrbox}{\myboxA} 629 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]623 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 630 624 `int f1, f2, state = 1;` // single global variables 631 625 int fib() { … … 644 638 } 645 639 } 646 \end{ lstlisting}640 \end{cfa} 647 641 \end{lrbox} 648 642 649 643 \newbox\myboxB 650 644 \begin{lrbox}{\myboxB} 651 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]645 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 652 646 #define FIB_INIT `{ 0, 1 }` 653 647 typedef struct { int f2, f1; } Fib; … … 666 660 } 667 661 } 668 \end{ lstlisting}662 \end{cfa} 669 663 \end{lrbox} 670 664 … … 679 673 \newbox\myboxA 680 674 \begin{lrbox}{\myboxA} 681 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]675 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 682 676 `coroutine` Fib { int fn; }; 683 677 void main( Fib & fib ) with( fib ) { … … 699 693 } 700 694 } 701 \end{ lstlisting}695 \end{cfa} 702 696 \end{lrbox} 703 697 \newbox\myboxB 704 698 \begin{lrbox}{\myboxB} 705 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]699 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 706 700 `coroutine` Fib { int ret; }; 707 701 void main( Fib & f ) with( fib ) { … … 723 717 724 718 725 \end{ lstlisting}719 \end{cfa} 726 720 \end{lrbox} 727 721 \subfloat[3 States, internal variables]{\label{f:Coroutine3States}\usebox\myboxA} … … 771 765 \newbox\myboxA 772 766 \begin{lrbox}{\myboxA} 773 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]767 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 774 768 `coroutine` Format { 775 769 char ch; // used for communication … … 777 771 }; 778 772 void main( Format & fmt ) with( fmt ) { 779 for ( ;; ) { 773 for ( ;; ) { 780 774 for ( g = 0; g < 5; g += 1 ) { // group 781 775 for ( b = 0; b < 4; b += 1 ) { // block … … 803 797 } 804 798 } 805 \end{ lstlisting}799 \end{cfa} 806 800 \end{lrbox} 807 801 808 802 \newbox\myboxB 809 803 \begin{lrbox}{\myboxB} 810 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]804 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 811 805 struct Format { 812 806 char ch; … … 840 834 format( &fmt ); 841 835 } 842 \end{ lstlisting}836 \end{cfa} 843 837 \end{lrbox} 844 838 \subfloat[\CFA Coroutine]{\label{f:CFAFmt}\usebox\myboxA} … … 1046 1040 }; 1047 1041 \end{cfa} 1048 & {\Large $\Rightarrow$} & 1042 & 1043 {\Large $\Rightarrow$} 1044 & 1049 1045 \begin{tabular}{@{}ccc@{}} 1050 1046 \begin{cfa} … … 1447 1443 \label{s:InternalScheduling} 1448 1444 1449 While monitor mutual-exclusion provides safe access to shared data, the monitor data may indicate that a thread accessing it cannot proceed, \eg a bounded buffer, Figure~\ref{f: BoundedBuffer}, may be full/empty so produce/consumer threads must block.1445 While monitor mutual-exclusion provides safe access to shared data, the monitor data may indicate that a thread accessing it cannot proceed, \eg a bounded buffer, Figure~\ref{f:GenericBoundedBuffer}, may be full/empty so produce/consumer threads must block. 1450 1446 Leaving the monitor and trying again (busy waiting) is impractical for high-level programming. 1451 1447 Monitors eliminate busy waiting by providing internal synchronization to schedule threads needing access to the shared data, where the synchronization is blocking (threads are parked) versus spinning. 1452 1448 The synchronization is generally achieved with internal~\cite{Hoare74} or external~\cite[\S~2.9.2]{uC++} scheduling, where \newterm{scheduling} is defined as indicating which thread acquires the critical section next. 1453 \newterm{Internal scheduling} is characterized by each thread entering the monitor and making an individual decision about proceeding or blocking, while \newterm{external scheduling} is characterized by an entering thread making a decision about proceeding for itself and behalf of other threads attempting entry.1449 \newterm{Internal scheduling} is characterized by each thread entering the monitor and making an individual decision about proceeding or blocking, while \newterm{external scheduling} is characterized by an entering thread making a decision about proceeding for itself and on behalf of other threads attempting entry. 1454 1450 1455 1451 Figure~\ref{f:BBInt} shows a \CFA bounded-buffer with internal scheduling, where producers/consumers enter the monitor, see the buffer is full/empty, and block on an appropriate condition lock, @full@/@empty@. … … 1460 1456 \begin{enumerate} 1461 1457 \item 1462 The signalling thread leaves immediately, and the signalled thread continues.1458 The signalling thread returns immediately, and the signalled thread continues. 1463 1459 \item 1464 The signalling thread continues and the signalled thread is marked for urgent unblocking at subsequent scheduling points(exit/wait).1460 The signalling thread continues and the signalled thread is marked for urgent unblocking at the next scheduling point (exit/wait). 1465 1461 \item 1466 The signalling thread blocks but is marked for urgrent unblocking a nd the signalled thread continues.1462 The signalling thread blocks but is marked for urgrent unblocking at the next scheduling point and the signalled thread continues. 1467 1463 \end{enumerate} 1468 1464 The first approach is too restrictive, as it precludes solving a reasonable class of problems (\eg dating service). 1469 1465 \CFA supports the next two semantics as both are useful. 1470 1466 Finally, while it is common to store a @condition@ as a field of the monitor, in \CFA, a @condition@ variable can be created/stored independently. 1467 Furthermore, a condition variable is tied to a \emph{group} of monitors on first use (called \newterm{branding}), which means that using internal scheduling with distinct sets of monitors requires one condition variable per set of monitors. 1471 1468 1472 1469 \begin{figure} … … 1474 1471 \newbox\myboxA 1475 1472 \begin{lrbox}{\myboxA} 1476 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]1473 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1477 1474 forall( otype T ) { // distribute forall 1478 1475 monitor Buffer { … … 1498 1495 } 1499 1496 } 1500 \end{ lstlisting}1497 \end{cfa} 1501 1498 \end{lrbox} 1502 1499 1503 1500 \newbox\myboxB 1504 1501 \begin{lrbox}{\myboxB} 1505 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]1502 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1506 1503 forall( otype T ) { // distribute forall 1507 1504 monitor Buffer { … … 1527 1524 } 1528 1525 } 1529 \end{ lstlisting}1526 \end{cfa} 1530 1527 \end{lrbox} 1531 1528 … … 1534 1531 \subfloat[External Scheduling]{\label{f:BBExt}\usebox\myboxB} 1535 1532 \caption{Generic Bounded-Buffer} 1536 \label{f: BoundedBuffer}1533 \label{f:GenericBoundedBuffer} 1537 1534 \end{figure} 1538 1535 … … 1540 1537 External scheduling is controlled by the @waitfor@ statement, which atomically blocks the calling thread, releases the monitor lock, and restricts the routine calls that can next acquire mutual exclusion. 1541 1538 If the buffer is full, only calls to @remove@ can acquire the buffer, and if the buffer is empty, only calls to @insert@ can acquire the buffer. 1542 Threads making calls to routines that are currently excluded wait outside (externally) of the monitor on a calling queue. 1543 1544 An important aspect of monitor implementation is barging, \ie can calling threads barge ahead of signalled threads? 1539 Threads making calls to routines that are currently excluded block outside (externally) of the monitor on a calling queue, versus blocking on condition queues inside the monitor. 1540 1541 Both internal and external scheduling extend to multiple monitors in a natural way. 1542 \begin{cfa} 1543 monitor M { `condition e`; ... }; 1544 void foo( M & mutex m1, M & mutex m2 ) { 1545 ... wait( `e` ); ... $\C{// wait( e, m1, m2 )}$ 1546 ... wait( `e, m1` ); ... 1547 ... wait( `e, m2` ); ... 1548 } 1549 1550 void rtn$\(_1\)$( M & mutex m1, M & mutex m2 ); 1551 void rtn$\(_2\)$( M & mutex m1 ); 1552 void bar( M & mutex m1, M & mutex m2 ) { 1553 ... waitfor( `rtn` ); ... $\C{// waitfor( rtn\(_1\), m1, m2 )}$ 1554 ... waitfor( `rtn, m1` ); ... $\C{// waitfor( rtn\(_2\), m1 )}$ 1555 } 1556 \end{cfa} 1557 For @wait( e )@, the default semantics is to atomically block the signaller and release all acquired mutex types in the parameter list, \ie @wait( e, m1, m2 )@. 1558 To override the implicit multi-monitor wait, specific mutex parameter(s) can be specified, \eg @wait( e, m1 )@. 1559 Wait statically verifies the released monitors are the acquired mutex-parameters so unconditional release is safe. 1560 Similarly, for @waitfor( rtn, ... )@, the default semantics is to atomically block the acceptor and release all acquired mutex types in the parameter list, \ie @waitfor( rtn, m1, m2 )@. 1561 To override the implicit multi-monitor wait, specific mutex parameter(s) can be specified, \eg @waitfor( rtn, m1 )@. 1562 Waitfor statically verifies the released monitors are the same as the acquired mutex-parameters of the given routine or routine pointer. 1563 To statically verify the released monitors match with the accepted routine's mutex parameters, the routine (pointer) prototype must be accessible. 1564 1565 Given the ability to release a subset of acquired monitors can result in a \newterm{nested monitor}~\cite{Lister77} deadlock. 1566 \begin{cfa} 1567 void foo( M & mutex m1, M & mutex m2 ) { 1568 ... wait( `e, m1` ); ... $\C{// release m1, keeping m2 acquired )}$ 1569 void baz( M & mutex m1, M & mutex m2 ) { $\C{// must acquire m1 and m2 )}$ 1570 ... signal( `e` ); ... 1571 \end{cfa} 1572 The @wait@ only releases @m1@ so the signalling thread cannot acquire both @m1@ and @m2@ to enter @baz@ to get to the @signal@. 1573 While deadlock issues can occur with multiple/nesting acquisition, this issue results from the fact that locks, and by extension monitors, are not perfectly composable. 1574 1575 Finally, an important aspect of monitor implementation is barging, \ie can calling threads barge ahead of signalled threads? 1545 1576 If barging is allowed, synchronization between a singller and signallee is difficult, often requiring multiple unblock/block cycles (looping around a wait rechecking if a condition is met). 1546 \CFA scheduling does \emph{not} have barging, which simplifies synchronization among threads in the monitor. 1577 \begin{quote} 1578 However, we decree that a signal operation be followed immediately by resumption of a waiting program, without possibility of an intervening procedure call from yet a third program. 1579 It is only in this way that a waiting program has an absolute guarantee that it can acquire the resource just released by the signalling program without any danger that a third program will interpose a monitor entry and seize the resource instead.~\cite[p.~550]{Hoare74} 1580 \end{quote} 1581 \CFA scheduling \emph{precludes} barging, which simplifies synchronization among threads in the monitor and increases correctness. 1582 For example, there are no loops in either bounded buffer solution in Figure~\ref{f:GenericBoundedBuffer}. 1547 1583 Supporting barging prevention as well as extending internal scheduling to multiple monitors is the main source of complexity in the design and implementation of \CFA concurrency. 1548 1584 1549 Indeed, like the bulk acquire semantics, internal scheduling extends to multiple monitors in a way that is natural to the user but requires additional complexity on the implementation side. 1550 1551 First, here is a simple example of internal scheduling: 1552 1553 \begin{cfa} 1554 monitor A { 1555 condition e; 1556 } 1557 1558 void foo(A& mutex a1, A& mutex a2) { 1585 1586 \subsection{Barging Prevention} 1587 1588 Figure~\ref{f:BargingPrevention} shows \CFA code where bulk acquire adds complexity to the internal-signalling semantics. 1589 The complexity begins at the end of the inner @mutex@ statement, where the semantics of internal scheduling need to be extended for multiple monitors. 1590 The problem is that bulk acquire is used in the inner @mutex@ statement where one of the monitors is already acquired. 1591 When the signalling thread reaches the end of the inner @mutex@ statement, it should transfer ownership of @m1@ and @m2@ to the waiting thread to prevent barging into the outer @mutex@ statement by another thread. 1592 However, both the signalling and signalled threads still need monitor @m1@. 1593 1594 \begin{figure} 1595 \newbox\myboxA 1596 \begin{lrbox}{\myboxA} 1597 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1598 monitor M m1, m2; 1599 condition c; 1600 mutex( m1 ) { 1559 1601 ... 1560 // Wait for cooperation from bar() 1561 wait(a1.e); 1602 mutex( m1, m2 ) { 1603 ... `wait( c )`; // block and release m1, m2 1604 // m1, m2 acquired 1605 } // $\LstCommentStyle{\color{red}release m2}$ 1606 // m1 acquired 1607 } // release m1 1608 \end{cfa} 1609 \end{lrbox} 1610 1611 \newbox\myboxB 1612 \begin{lrbox}{\myboxB} 1613 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1614 1615 1616 mutex( m1 ) { 1562 1617 ... 1563 } 1564 1565 void bar(A& mutex a1, A& mutex a2) { 1566 // Provide cooperation for foo() 1567 ... 1568 // Unblock foo 1569 signal(a1.e); 1570 } 1571 \end{cfa} 1572 1573 % ====================================================================== 1574 % ====================================================================== 1575 \subsection{Internal Scheduling - Multi-Monitor} 1576 % ====================================================================== 1577 % ====================================================================== 1578 It is easy to understand the problem of multi-monitor scheduling using a series of pseudo-code examples. 1579 Note that for simplicity in the following snippets of pseudo-code, waiting and signalling is done using an implicit condition variable, like Java built-in monitors. 1580 Indeed, @wait@ statements always use the implicit condition variable as parameters and explicitly name the monitors (A and B) associated with the condition. 1581 Note that in \CFA, condition variables are tied to a \emph{group} of monitors on first use (called branding), which means that using internal scheduling with distinct sets of monitors requires one condition variable per set of monitors. 1582 The example below shows the simple case of having two threads (one for each column) and a single monitor A. 1583 1584 \begin{multicols}{2} 1585 thread 1 1586 \begin{cfa} 1587 acquire A 1588 wait A 1589 release A 1590 \end{cfa} 1591 1592 \columnbreak 1593 1594 thread 2 1595 \begin{cfa} 1596 acquire A 1597 signal A 1598 release A 1599 \end{cfa} 1600 \end{multicols} 1601 One thread acquires before waiting (atomically blocking and releasing A) and the other acquires before signalling. 1602 It is important to note here that both @wait@ and @signal@ must be called with the proper monitor(s) already acquired. 1603 This semantic is a logical requirement for barging prevention. 1604 1605 A direct extension of the previous example is a bulk acquire version: 1606 \begin{multicols}{2} 1607 \begin{cfa} 1608 acquire A & B 1609 wait A & B 1610 release A & B 1611 \end{cfa} 1612 \columnbreak 1613 \begin{cfa} 1614 acquire A & B 1615 signal A & B 1616 release A & B 1617 \end{cfa} 1618 \end{multicols} 1619 \noindent This version uses bulk acquire (denoted using the {\sf\&} symbol), but the presence of multiple monitors does not add a particularly new meaning. 1620 Synchronization happens between the two threads in exactly the same way and order. 1621 The only difference is that mutual exclusion covers a group of monitors. 1622 On the implementation side, handling multiple monitors does add a degree of complexity as the next few examples demonstrate. 1623 1624 While deadlock issues can occur when nesting monitors, these issues are only a symptom of the fact that locks, and by extension monitors, are not perfectly composable. 1625 For monitors, a well-known deadlock problem is the Nested Monitor Problem~\cite{Lister77}, which occurs when a @wait@ is made by a thread that holds more than one monitor. 1626 For example, the following cfa-code runs into the nested-monitor problem: 1627 \begin{multicols}{2} 1628 \begin{cfa} 1629 acquire A 1630 acquire B 1631 wait B 1632 release B 1633 release A 1634 \end{cfa} 1635 1636 \columnbreak 1637 1638 \begin{cfa} 1639 acquire A 1640 acquire B 1641 signal B 1642 release B 1643 release A 1644 \end{cfa} 1645 \end{multicols} 1646 \noindent The @wait@ only releases monitor @B@ so the signalling thread cannot acquire monitor @A@ to get to the @signal@. 1647 Attempting release of all acquired monitors at the @wait@ introduces a different set of problems, such as releasing monitor @C@, which has nothing to do with the @signal@. 1648 1649 However, for monitors as for locks, it is possible to write a program using nesting without encountering any problems if nesting is done correctly. 1650 For example, the next cfa-code snippet acquires monitors {\sf A} then {\sf B} before waiting, while only acquiring {\sf B} when signalling, effectively avoiding the Nested Monitor Problem~\cite{Lister77}. 1651 1652 \begin{multicols}{2} 1653 \begin{cfa} 1654 acquire A 1655 acquire B 1656 wait B 1657 release B 1658 release A 1659 \end{cfa} 1660 1661 \columnbreak 1662 1663 \begin{cfa} 1664 1665 acquire B 1666 signal B 1667 release B 1668 1669 \end{cfa} 1670 \end{multicols} 1671 1672 \noindent However, this simple refactoring may not be possible, forcing more complex restructuring. 1673 1674 % ====================================================================== 1675 % ====================================================================== 1676 \subsection{Internal Scheduling - In Depth} 1677 % ====================================================================== 1678 % ====================================================================== 1679 1680 A larger example is presented to show complex issues for bulk acquire and its implementation options are analyzed. 1681 Figure~\ref{f:int-bulk-cfa} shows an example where bulk acquire adds a significant layer of complexity to the internal signalling semantics, and listing \ref{f:int-bulk-cfa} shows the corresponding \CFA code to implement the cfa-code in listing \ref{f:int-bulk-cfa}. 1682 For the purpose of translating the given cfa-code into \CFA-code, any method of introducing a monitor is acceptable, \eg @mutex@ parameters, global variables, pointer parameters, or using locals with the @mutex@ statement. 1683 1684 \begin{figure} 1685 \begin{multicols}{2} 1686 Waiting thread 1687 \begin{cfa}[numbers=left] 1688 acquire A 1689 // Code Section 1 1690 acquire A & B 1691 // Code Section 2 1692 wait A & B 1693 // Code Section 3 1694 release A & B 1695 // Code Section 4 1696 release A 1697 \end{cfa} 1698 \columnbreak 1699 Signalling thread 1700 \begin{cfa}[numbers=left, firstnumber=10,escapechar=|] 1701 acquire A 1702 // Code Section 5 1703 acquire A & B 1704 // Code Section 6 1705 |\label{line:signal1}|signal A & B 1706 // Code Section 7 1707 |\label{line:releaseFirst}|release A & B 1708 // Code Section 8 1709 |\label{line:lastRelease}|release A 1710 \end{cfa} 1711 \end{multicols} 1712 \begin{cfa}[caption={Internal scheduling with bulk acquire},label={f:int-bulk-cfa}] 1713 \end{cfa} 1714 \begin{center} 1715 \begin{cfa}[xleftmargin=.4\textwidth] 1716 monitor A a; 1717 monitor B b; 1718 condition c; 1719 \end{cfa} 1720 \end{center} 1721 \begin{multicols}{2} 1722 Waiting thread 1723 \begin{cfa} 1724 mutex(a) { 1725 // Code Section 1 1726 mutex(a, b) { 1727 // Code Section 2 1728 wait(c); 1729 // Code Section 3 1730 } 1731 // Code Section 4 1732 } 1733 \end{cfa} 1734 \columnbreak 1735 Signalling thread 1736 \begin{cfa} 1737 mutex(a) { 1738 // Code Section 5 1739 mutex(a, b) { 1740 // Code Section 6 1741 signal(c); 1742 // Code Section 7 1743 } 1744 // Code Section 8 1745 } 1746 \end{cfa} 1747 \end{multicols} 1748 \begin{cfa}[caption={Equivalent \CFA code for listing \ref{f:int-bulk-cfa}},label={f:int-bulk-cfa}] 1749 \end{cfa} 1750 \begin{multicols}{2} 1751 Waiter 1752 \begin{cfa}[numbers=left] 1753 acquire A 1754 acquire A & B 1755 wait A & B 1756 release A & B 1757 release A 1758 \end{cfa} 1759 1760 \columnbreak 1761 1762 Signaller 1763 \begin{cfa}[numbers=left, firstnumber=6,escapechar=|] 1764 acquire A 1765 acquire A & B 1766 signal A & B 1767 release A & B 1768 |\label{line:secret}|// Secretly keep B here 1769 release A 1770 // Wakeup waiter and transfer A & B 1771 \end{cfa} 1772 \end{multicols} 1773 \begin{cfa}[caption={Figure~\ref{f:int-bulk-cfa}, with delayed signalling comments},label={f:int-secret}] 1774 \end{cfa} 1618 mutex( m1, m2 ) { 1619 ... `signal( c )`; ... 1620 // m1, m2 acquired 1621 } // $\LstCommentStyle{\color{red}release m2}$ 1622 // m1 acquired 1623 } // release m1 1624 \end{cfa} 1625 \end{lrbox} 1626 1627 \newbox\myboxC 1628 \begin{lrbox}{\myboxC} 1629 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1630 1631 1632 mutex( m1 ) { 1633 ... `wait( c )`; ... 1634 // m1 acquired 1635 } // $\LstCommentStyle{\color{red}release m1}$ 1636 1637 1638 1639 1640 \end{cfa} 1641 \end{lrbox} 1642 1643 \begin{cquote} 1644 \subfloat[Waiting Thread]{\label{f:WaitingThread}\usebox\myboxA} 1645 \hspace{2\parindentlnth} 1646 \subfloat[Signalling Thread]{\label{f:SignallingThread}\usebox\myboxB} 1647 \hspace{2\parindentlnth} 1648 \subfloat[Other Waiting Thread]{\label{f:SignallingThread}\usebox\myboxC} 1649 \end{cquote} 1650 \caption{Barging Prevention} 1651 \label{f:BargingPrevention} 1775 1652 \end{figure} 1776 1653 1777 The complexity begins at code sections 4 and 8 in listing \ref{f:int-bulk-cfa}, which are where the existing semantics of internal scheduling needs to be extended for multiple monitors.1778 The root of the problem is that bulk acquire is used in a context where one of the monitors is already acquired, which is why it is important to define the behaviour of the previous cfa-code.1779 When the signaller thread reaches the location where it should ``release @A & B@'' (listing \ref{f:int-bulk-cfa} line \ref{line:releaseFirst}), it must actually transfer ownership of monitor @B@ to the waiting thread.1780 This ownership transfer is required in order to prevent barging into @B@ by another thread, since both the signalling and signalled threads still need monitor @A@.1781 There are three options:1782 1783 \subsubsection{Delaying Signals}1784 1654 The obvious solution to the problem of multi-monitor scheduling is to keep ownership of all locks until the last lock is ready to be transferred. 1785 1655 It can be argued that that moment is when the last lock is no longer needed, because this semantics fits most closely to the behaviour of single-monitor scheduling. … … 1794 1664 Depending on the order of signals (listing \ref{f:dependency} line \ref{line:signal-ab} and \ref{line:signal-a}) two cases can happen: 1795 1665 1666 \begin{comment} 1796 1667 \paragraph{Case 1: thread $\alpha$ goes first.} In this case, the problem is that monitor @A@ needs to be passed to thread $\beta$ when thread $\alpha$ is done with it. 1797 1668 \paragraph{Case 2: thread $\beta$ goes first.} In this case, the problem is that monitor @B@ needs to be retained and passed to thread $\alpha$ along with monitor @A@, which can be done directly or possibly using thread $\beta$ as an intermediate. … … 1803 1674 In both cases, the threads need to be able to distinguish, on a per monitor basis, which ones need to be released and which ones need to be transferred, which means knowing when to release a group becomes complex and inefficient (see next section) and therefore effectively precludes this approach. 1804 1675 1676 1805 1677 \subsubsection{Dependency graphs} 1806 1807 1678 1808 1679 \begin{figure} … … 1883 1754 1884 1755 \subsubsection{Partial Signalling} \label{partial-sig} 1756 \end{comment} 1757 1885 1758 Finally, the solution that is chosen for \CFA is to use partial signalling. 1886 1759 Again using listing \ref{f:int-bulk-cfa}, the partial signalling solution transfers ownership of monitor @B@ at lines \ref{line:signal1} to the waiter but does not wake the waiting thread since it is still using monitor @A@. … … 1897 1770 \end{itemize} 1898 1771 1899 % ====================================================================== 1900 % ====================================================================== 1772 1901 1773 \subsection{Signalling: Now or Later} 1902 % ====================================================================== 1903 % ====================================================================== 1904 \begin{table} 1905 \begin{tabular}{|c|c|} 1906 @signal@ & @signal_block@ \\ 1907 \hline 1908 \begin{cfa}[tabsize=3] 1909 monitor DatingService { 1910 // compatibility codes 1911 enum{ CCodes = 20 }; 1912 1913 int girlPhoneNo 1914 int boyPhoneNo; 1774 1775 \begin{figure} 1776 \centering 1777 \newbox\myboxA 1778 \begin{lrbox}{\myboxA} 1779 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1780 enum { CCodes = 20 }; 1781 monitor DS { 1782 int GirlPhNo, BoyPhNo; 1783 condition Girls[CCodes], Boys[CCodes]; 1784 condition exchange; 1915 1785 }; 1916 1917 condition girls[CCodes]; 1918 condition boys [CCodes]; 1919 condition exchange; 1920 1921 int girl(int phoneNo, int cfa) { 1922 // no compatible boy ? 1923 if(empty(boys[cfa])) { 1924 wait(girls[cfa]); // wait for boy 1925 girlPhoneNo = phoneNo; // make phone number available 1926 signal(exchange); // wake boy from chair 1786 int girl( DS & mutex ds, int phNo, int ccode ) { 1787 if ( is_empty( Boys[ccode] ) ) { 1788 wait( Girls[ccode] ); 1789 GirlPhNo = phNo; 1790 exchange.signal(); 1927 1791 } else { 1928 girlPhoneNo = phoneNo; // make phone number available 1929 signal(boys[cfa]); // wake boy 1930 wait(exchange); // sit in chair 1931 } 1932 return boyPhoneNo; 1933 } 1934 int boy(int phoneNo, int cfa) { 1935 // same as above 1936 // with boy/girl interchanged 1937 } 1938 \end{cfa}&\begin{cfa}[tabsize=3] 1939 monitor DatingService { 1940 1941 enum{ CCodes = 20 }; // compatibility codes 1942 1943 int girlPhoneNo; 1944 int boyPhoneNo; 1792 GirlPhNo = phNo; 1793 signal( Boys[ccode] ); 1794 exchange.wait(); 1795 } // if 1796 return BoyPhNo; 1797 } 1798 int boy( DS & mutex ds, int phNo, int ccode ) { 1799 // as above with boy/girl interchanged 1800 } 1801 \end{cfa} 1802 \end{lrbox} 1803 1804 \newbox\myboxB 1805 \begin{lrbox}{\myboxB} 1806 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1807 1808 monitor DS { 1809 int GirlPhNo, BoyPhNo; 1810 condition Girls[CCodes], Boys[CCodes]; 1811 1945 1812 }; 1946 1947 condition girls[CCodes]; 1948 condition boys [CCodes]; 1949 // exchange is not needed 1950 1951 int girl(int phoneNo, int cfa) { 1952 // no compatible boy ? 1953 if(empty(boys[cfa])) { 1954 wait(girls[cfa]); // wait for boy 1955 girlPhoneNo = phoneNo; // make phone number available 1956 signal(exchange); // wake boy from chair 1813 int girl( DS & mutex ds, int phNo, int ccode ) { 1814 if ( is_empty( Boys[ccode] ) ) { // no compatible 1815 wait( Girls[ccode] ); // wait for boy 1816 GirlPhNo = phNo; // make phone number available 1817 1957 1818 } else { 1958 girlPhoneNo = phoneNo; // make phone number available 1959 signal_block(boys[cfa]); // wake boy 1960 1961 // second handshake unnecessary 1962 1963 } 1964 return boyPhoneNo; 1965 } 1966 1967 int boy(int phoneNo, int cfa) { 1968 // same as above 1969 // with boy/girl interchanged 1970 } 1971 \end{cfa} 1972 \end{tabular} 1973 \caption{Dating service example using \protect\lstinline|signal| and \protect\lstinline|signal_block|. } 1974 \label{tbl:datingservice} 1975 \end{table} 1819 GirlPhNo = phNo; // make phone number available 1820 signal_block( Boys[ccode] ); // restart boy 1821 1822 } // if 1823 return BoyPhNo; 1824 } 1825 int boy( DS & mutex ds, int phNo, int ccode ) { 1826 // as above with boy/girl interchanged 1827 } 1828 \end{cfa} 1829 \end{lrbox} 1830 1831 \subfloat[\lstinline@signal@]{\label{f:DatingSignal}\usebox\myboxA} 1832 \qquad 1833 \subfloat[\lstinline@signal_block@]{\label{f:DatingSignalBlock}\usebox\myboxB} 1834 \caption{Dating service. } 1835 \label{f:Dating service} 1836 \end{figure} 1837 1976 1838 An important note is that, until now, signalling a monitor was a delayed operation. 1977 1839 The ownership of the monitor is transferred only when the monitor would have otherwise been released, not at the point of the @signal@ statement. … … 1990 1852 % ====================================================================== 1991 1853 An alternative to internal scheduling is external scheduling (see Table~\ref{tbl:sched}). 1854 1855 \begin{comment} 1992 1856 \begin{table} 1993 1857 \begin{tabular}{|c|c|c|} … … 2053 1917 \label{tbl:sched} 2054 1918 \end{table} 1919 \end{comment} 1920 2055 1921 This method is more constrained and explicit, which helps users reduce the non-deterministic nature of concurrency. 2056 1922 Indeed, as the following examples demonstrate, external scheduling allows users to wait for events from other threads without the concern of unrelated events occurring.
Note:
See TracChangeset
for help on using the changeset viewer.