Changeset 7951100
- Timestamp:
- Jun 7, 2018, 4:40:05 PM (6 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- b067d9b
- Parents:
- b4e1876 (diff), 08b5a7e (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - Files:
-
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/papers/concurrency/Paper.tex
rb4e1876 r7951100 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 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% … … 260 256 \section{Introduction} 261 257 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.258 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 259 While the simplest concurrency system is a thread and a lock, this low-level approach is hard to master. 264 260 An easier approach for programmers is to support higher-level constructs as the basis of concurrency. … … 589 585 As such, library support for threading is far from widespread. 590 586 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.587 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 588 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 589 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 623 \newbox\myboxA 628 624 \begin{lrbox}{\myboxA} 629 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]625 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 630 626 `int f1, f2, state = 1;` // single global variables 631 627 int fib() { … … 644 640 } 645 641 } 646 \end{ lstlisting}642 \end{cfa} 647 643 \end{lrbox} 648 644 649 645 \newbox\myboxB 650 646 \begin{lrbox}{\myboxB} 651 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]647 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 652 648 #define FIB_INIT `{ 0, 1 }` 653 649 typedef struct { int f2, f1; } Fib; … … 666 662 } 667 663 } 668 \end{ lstlisting}664 \end{cfa} 669 665 \end{lrbox} 670 666 … … 679 675 \newbox\myboxA 680 676 \begin{lrbox}{\myboxA} 681 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]677 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 682 678 `coroutine` Fib { int fn; }; 683 679 void main( Fib & fib ) with( fib ) { … … 699 695 } 700 696 } 701 \end{ lstlisting}697 \end{cfa} 702 698 \end{lrbox} 703 699 \newbox\myboxB 704 700 \begin{lrbox}{\myboxB} 705 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]701 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 706 702 `coroutine` Fib { int ret; }; 707 703 void main( Fib & f ) with( fib ) { … … 723 719 724 720 725 \end{ lstlisting}721 \end{cfa} 726 722 \end{lrbox} 727 723 \subfloat[3 States, internal variables]{\label{f:Coroutine3States}\usebox\myboxA} … … 771 767 \newbox\myboxA 772 768 \begin{lrbox}{\myboxA} 773 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]769 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 774 770 `coroutine` Format { 775 771 char ch; // used for communication … … 803 799 } 804 800 } 805 \end{ lstlisting}801 \end{cfa} 806 802 \end{lrbox} 807 803 808 804 \newbox\myboxB 809 805 \begin{lrbox}{\myboxB} 810 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]806 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 811 807 struct Format { 812 808 char ch; … … 840 836 format( &fmt ); 841 837 } 842 \end{ lstlisting}838 \end{cfa} 843 839 \end{lrbox} 844 840 \subfloat[\CFA Coroutine]{\label{f:CFAFmt}\usebox\myboxA} … … 1046 1042 }; 1047 1043 \end{cfa} 1048 & {\Large $\Rightarrow$} & 1044 & 1045 {\Large $\Rightarrow$} 1046 & 1049 1047 \begin{tabular}{@{}ccc@{}} 1050 1048 \begin{cfa} … … 1447 1445 \label{s:InternalScheduling} 1448 1446 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.1447 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 1448 Leaving the monitor and trying again (busy waiting) is impractical for high-level programming. 1451 1449 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 1450 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.1451 \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 1452 1455 1453 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 1458 \begin{enumerate} 1461 1459 \item 1462 The signalling thread leaves immediately, and the signalled thread continues.1460 The signalling thread returns immediately, and the signalled thread continues. 1463 1461 \item 1464 The signalling thread continues and the signalled thread is marked for urgent unblocking at subsequent scheduling points(exit/wait).1462 The signalling thread continues and the signalled thread is marked for urgent unblocking at the next scheduling point (exit/wait). 1465 1463 \item 1466 The signalling thread blocks but is marked for urgrent unblocking a nd the signalled thread continues.1464 The signalling thread blocks but is marked for urgrent unblocking at the next scheduling point and the signalled thread continues. 1467 1465 \end{enumerate} 1468 1466 The first approach is too restrictive, as it precludes solving a reasonable class of problems (\eg dating service). 1469 1467 \CFA supports the next two semantics as both are useful. 1470 1468 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. 1469 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 1470 1472 1471 \begin{figure} … … 1474 1473 \newbox\myboxA 1475 1474 \begin{lrbox}{\myboxA} 1476 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]1475 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1477 1476 forall( otype T ) { // distribute forall 1478 1477 monitor Buffer { … … 1498 1497 } 1499 1498 } 1500 \end{ lstlisting}1499 \end{cfa} 1501 1500 \end{lrbox} 1502 1501 1503 1502 \newbox\myboxB 1504 1503 \begin{lrbox}{\myboxB} 1505 \begin{ lstlisting}[aboveskip=0pt,belowskip=0pt]1504 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1506 1505 forall( otype T ) { // distribute forall 1507 1506 monitor Buffer { … … 1527 1526 } 1528 1527 } 1529 \end{ lstlisting}1528 \end{cfa} 1530 1529 \end{lrbox} 1531 1530 … … 1534 1533 \subfloat[External Scheduling]{\label{f:BBExt}\usebox\myboxB} 1535 1534 \caption{Generic Bounded-Buffer} 1536 \label{f: BoundedBuffer}1535 \label{f:GenericBoundedBuffer} 1537 1536 \end{figure} 1538 1537 … … 1540 1539 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 1540 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? 1541 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. 1542 1543 Both internal and external scheduling extend to multiple monitors in a natural way. 1544 \begin{cfa} 1545 monitor M { `condition e`; ... }; 1546 void foo( M & mutex m1, M & mutex m2 ) { 1547 ... wait( `e` ); ... $\C{// wait( e, m1, m2 )}$ 1548 ... wait( `e, m1` ); ... 1549 ... wait( `e, m2` ); ... 1550 } 1551 1552 void rtn$\(_1\)$( M & mutex m1, M & mutex m2 ); 1553 void rtn$\(_2\)$( M & mutex m1 ); 1554 void bar( M & mutex m1, M & mutex m2 ) { 1555 ... waitfor( `rtn` ); ... $\C{// waitfor( rtn\(_1\), m1, m2 )}$ 1556 ... waitfor( `rtn, m1` ); ... $\C{// waitfor( rtn\(_2\), m1 )}$ 1557 } 1558 \end{cfa} 1559 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 )@. 1560 To override the implicit multi-monitor wait, specific mutex parameter(s) can be specified, \eg @wait( e, m1 )@. 1561 Wait statically verifies the released monitors are the acquired mutex-parameters so unconditional release is safe. 1562 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 )@. 1563 To override the implicit multi-monitor wait, specific mutex parameter(s) can be specified, \eg @waitfor( rtn, m1 )@. 1564 Waitfor statically verifies the released monitors are the same as the acquired mutex-parameters of the given routine or routine pointer. 1565 To statically verify the released monitors match with the accepted routine's mutex parameters, the routine (pointer) prototype must be accessible. 1566 1567 Given the ability to release a subset of acquired monitors can result in a \newterm{nested monitor}~\cite{Lister77} deadlock. 1568 \begin{cfa} 1569 void foo( M & mutex m1, M & mutex m2 ) { 1570 ... wait( `e, m1` ); ... $\C{// release m1, keeping m2 acquired )}$ 1571 void baz( M & mutex m1, M & mutex m2 ) { $\C{// must acquire m1 and m2 )}$ 1572 ... signal( `e` ); ... 1573 \end{cfa} 1574 The @wait@ only releases @m1@ so the signalling thread cannot acquire both @m1@ and @m2@ to enter @baz@ to get to the @signal@. 1575 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. 1576 1577 Finally, an important aspect of monitor implementation is barging, \ie can calling threads barge ahead of signalled threads? 1545 1578 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. 1579 \begin{quote} 1580 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. 1581 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} 1582 \end{quote} 1583 \CFA scheduling \emph{precludes} barging, which simplifies synchronization among threads in the monitor and increases correctness. 1584 For example, there are no loops in either bounded buffer solution in Figure~\ref{f:GenericBoundedBuffer}. 1547 1585 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 1586 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) { 1587 1588 \subsection{Barging Prevention} 1589 1590 Figure~\ref{f:BargingPrevention} shows \CFA code where bulk acquire adds complexity to the internal-signalling semantics. 1591 The complexity begins at the end of the inner @mutex@ statement, where the semantics of internal scheduling need to be extended for multiple monitors. 1592 The problem is that bulk acquire is used in the inner @mutex@ statement where one of the monitors is already acquired. 1593 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. 1594 However, both the signalling and signalled threads still need monitor @m1@. 1595 1596 \begin{figure} 1597 \newbox\myboxA 1598 \begin{lrbox}{\myboxA} 1599 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1600 monitor M m1, m2; 1601 condition c; 1602 mutex( m1 ) { 1559 1603 ... 1560 // Wait for cooperation from bar() 1561 wait(a1.e); 1604 mutex( m1, m2 ) { 1605 ... `wait( c )`; // block and release m1, m2 1606 // m1, m2 acquired 1607 } // $\LstCommentStyle{\color{red}release m2}$ 1608 // m1 acquired 1609 } // release m1 1610 \end{cfa} 1611 \end{lrbox} 1612 1613 \newbox\myboxB 1614 \begin{lrbox}{\myboxB} 1615 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1616 1617 1618 mutex( m1 ) { 1562 1619 ... 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} 1620 mutex( m1, m2 ) { 1621 ... `signal( c )`; ... 1622 // m1, m2 acquired 1623 } // $\LstCommentStyle{\color{red}release m2}$ 1624 // m1 acquired 1625 } // release m1 1626 \end{cfa} 1627 \end{lrbox} 1628 1629 \newbox\myboxC 1630 \begin{lrbox}{\myboxC} 1631 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1632 1633 1634 mutex( m1 ) { 1635 ... `wait( c )`; ... 1636 // m1 acquired 1637 } // $\LstCommentStyle{\color{red}release m1}$ 1638 1639 1640 1641 1642 \end{cfa} 1643 \end{lrbox} 1644 1645 \begin{cquote} 1646 \subfloat[Waiting Thread]{\label{f:WaitingThread}\usebox\myboxA} 1647 \hspace{2\parindentlnth} 1648 \subfloat[Signalling Thread]{\label{f:SignallingThread}\usebox\myboxB} 1649 \hspace{2\parindentlnth} 1650 \subfloat[Other Waiting Thread]{\label{f:SignallingThread}\usebox\myboxC} 1651 \end{cquote} 1652 \caption{Barging Prevention} 1653 \label{f:BargingPrevention} 1775 1654 \end{figure} 1776 1655 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 1656 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 1657 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 1666 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 1667 1668 \begin{comment} 1796 1669 \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 1670 \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 1676 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 1677 1678 1805 1679 \subsubsection{Dependency graphs} 1806 1807 1680 1808 1681 \begin{figure} … … 1883 1756 1884 1757 \subsubsection{Partial Signalling} \label{partial-sig} 1758 \end{comment} 1759 1885 1760 Finally, the solution that is chosen for \CFA is to use partial signalling. 1886 1761 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 1772 \end{itemize} 1898 1773 1899 % ====================================================================== 1900 % ====================================================================== 1774 1901 1775 \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; 1776 1777 \begin{figure} 1778 \centering 1779 \newbox\myboxA 1780 \begin{lrbox}{\myboxA} 1781 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1782 enum { CCodes = 20 }; 1783 monitor DS { 1784 int GirlPhNo, BoyPhNo; 1785 condition Girls[CCodes], Boys[CCodes]; 1786 condition exchange; 1915 1787 }; 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 1788 int girl( DS & mutex ds, int phNo, int ccode ) { 1789 if ( is_empty( Boys[ccode] ) ) { 1790 wait( Girls[ccode] ); 1791 GirlPhNo = phNo; 1792 exchange.signal(); 1927 1793 } 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; 1794 GirlPhNo = phNo; 1795 signal( Boys[ccode] ); 1796 exchange.wait(); 1797 } // if 1798 return BoyPhNo; 1799 } 1800 int boy( DS & mutex ds, int phNo, int ccode ) { 1801 // as above with boy/girl interchanged 1802 } 1803 \end{cfa} 1804 \end{lrbox} 1805 1806 \newbox\myboxB 1807 \begin{lrbox}{\myboxB} 1808 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 1809 1810 monitor DS { 1811 int GirlPhNo, BoyPhNo; 1812 condition Girls[CCodes], Boys[CCodes]; 1813 1945 1814 }; 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 1815 int girl( DS & mutex ds, int phNo, int ccode ) { 1816 if ( is_empty( Boys[ccode] ) ) { // no compatible 1817 wait( Girls[ccode] ); // wait for boy 1818 GirlPhNo = phNo; // make phone number available 1819 1957 1820 } 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} 1821 GirlPhNo = phNo; // make phone number available 1822 signal_block( Boys[ccode] ); // restart boy 1823 1824 } // if 1825 return BoyPhNo; 1826 } 1827 int boy( DS & mutex ds, int phNo, int ccode ) { 1828 // as above with boy/girl interchanged 1829 } 1830 \end{cfa} 1831 \end{lrbox} 1832 1833 \subfloat[\lstinline@signal@]{\label{f:DatingSignal}\usebox\myboxA} 1834 \qquad 1835 \subfloat[\lstinline@signal_block@]{\label{f:DatingSignalBlock}\usebox\myboxB} 1836 \caption{Dating service. } 1837 \label{f:Dating service} 1838 \end{figure} 1839 1976 1840 An important note is that, until now, signalling a monitor was a delayed operation. 1977 1841 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 1854 % ====================================================================== 1991 1855 An alternative to internal scheduling is external scheduling (see Table~\ref{tbl:sched}). 1856 1857 \begin{comment} 1992 1858 \begin{table} 1993 1859 \begin{tabular}{|c|c|c|} … … 2053 1919 \label{tbl:sched} 2054 1920 \end{table} 1921 \end{comment} 1922 2055 1923 This method is more constrained and explicit, which helps users reduce the non-deterministic nature of concurrency. 2056 1924 Indeed, as the following examples demonstrate, external scheduling allows users to wait for events from other threads without the concern of unrelated events occurring. -
src/Common/SemanticError.cc
rb4e1876 r7951100 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed May 16 15:01:20201813 // Update Count : 912 // Last Modified On : Thu Jun 7 08:05:26 2018 13 // Update Count : 10 14 14 // 15 15 … … 97 97 void SemanticError( CodeLocation location, std::string error ) { 98 98 SemanticErrorThrow = true; 99 throw SemanticErrorException( location, error);99 throw SemanticErrorException( location, error ); 100 100 } 101 101 -
src/Parser/DeclarationNode.cc
rb4e1876 r7951100 10 10 // Created On : Sat May 16 12:34:05 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jun 6 15:57:50201813 // Update Count : 107 612 // Last Modified On : Thu Jun 7 12:08:55 2018 13 // Update Count : 1079 14 14 // 15 15 … … 545 545 type->aggregate.params->appendList( q->type->forall ); // augment forall qualifier 546 546 } else { // not polymorphic 547 type->aggregate.params = q->type->forall; // make polymorphic type 548 // change implicit typedef from TYPEDEFname to TYPEGENname 549 typedefTable.changeKind( *type->aggregate.name, TYPEGENname ); 547 type->aggregate.params = q->type->forall; // set forall qualifier 550 548 } // if 551 549 } else { // not polymorphic -
src/Parser/TypedefTable.cc
rb4e1876 r7951100 10 10 // Created On : Sat May 16 15:20:13 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jun 1 16:54:18201813 // Update Count : 1 5512 // Last Modified On : Thu Jun 7 13:17:56 2018 13 // Update Count : 192 14 14 // 15 15 … … 17 17 #include "TypedefTable.h" 18 18 #include <cassert> // for assert 19 #include <iostream> 19 20 20 21 #if 0 21 #include <iostream>22 22 #define debugPrint( code ) code 23 23 #else … … 27 27 using namespace std; // string, iostream 28 28 29 debugPrint( 30 static const char *kindName( int kind ) { 31 switch ( kind ) { 32 case IDENTIFIER: return "identifier"; 33 case TYPEDEFname: return "typedef"; 34 case TYPEGENname: return "typegen"; 35 default: 36 cerr << "Error: cfa-cpp internal error, invalid kind of identifier" << endl; 37 abort(); 38 } // switch 39 } // kindName 40 ) 41 29 42 TypedefTable::~TypedefTable() { 30 43 if ( ! SemanticErrorThrow && kindTable.currentScope() != 0 ) { 31 std::cerr << "scope failure " << kindTable.currentScope() << endl; 44 cerr << "Error: cfa-cpp internal error, scope failure " << kindTable.currentScope() << endl; 45 abort(); 32 46 } // if 33 47 } // TypedefTable::~TypedefTable … … 44 58 } // TypedefTable::isKind 45 59 46 void TypedefTable::changeKind( const string & identifier, int kind ) {47 KindTable::iterator posn = kindTable.find( identifier );48 if ( posn != kindTable.end() ) posn->second = kind; // exists => update49 } // TypedefTable::changeKind50 51 60 // SKULLDUGGERY: Generate a typedef for the aggregate name so the aggregate does not have to be qualified by 52 61 // "struct". Only generate the typedef, if the name is not in use. The typedef is implicitly (silently) removed if the 53 62 // name is explicitly used. 54 void TypedefTable::makeTypedef( const string & name ) { 63 void TypedefTable::makeTypedef( const string & name, int kind ) { 64 // Check for existence is necessary to handle: 65 // struct Fred {}; 66 // void Fred(); 67 // void fred() { 68 // struct Fred act; // do not add as type in this scope 69 // Fred(); 70 // } 55 71 if ( ! typedefTable.exists( name ) ) { 56 typedefTable.addToEnclosingScope( name, TYPEDEFname, "MTD" );72 typedefTable.addToEnclosingScope( name, kind, "MTD" ); 57 73 } // if 58 74 } // TypedefTable::makeTypedef 59 75 60 void TypedefTable::addToScope( const st d::string & identifier, int kind, const char * locn __attribute__((unused)) ) {76 void TypedefTable::addToScope( const string & identifier, int kind, const char * locn __attribute__((unused)) ) { 61 77 auto scope = kindTable.currentScope(); 62 debugPrint( cerr << "Adding at " << locn << " " << identifier << " as kind " << kind<< " scope " << scope << endl );78 debugPrint( cerr << "Adding current at " << locn << " " << identifier << " as " << kindName( kind ) << " scope " << scope << endl ); 63 79 auto ret = kindTable.insertAt( scope, identifier, kind ); 64 80 if ( ! ret.second ) ret.first->second = kind; // exists => update 65 81 } // TypedefTable::addToScope 66 82 67 void TypedefTable::addToEnclosingScope( const st d::string & identifier, int kind, const char * locn __attribute__((unused)) ) {83 void TypedefTable::addToEnclosingScope( const string & identifier, int kind, const char * locn __attribute__((unused)) ) { 68 84 assert( kindTable.currentScope() >= 1 ); 69 85 auto scope = kindTable.currentScope() - 1; 70 debugPrint( cerr << "Adding +1 at " << locn << " " << identifier << " as kind " << kind<< " scope " << scope << endl );86 debugPrint( cerr << "Adding enclosing at " << locn << " " << identifier << " as " << kindName( kind ) << " scope " << scope << endl ); 71 87 auto ret = kindTable.insertAt( scope, identifier, kind ); 72 88 if ( ! ret.second ) ret.first->second = kind; // exists => update … … 93 109 debugPrint( cerr << endl << "[" << scope << "]" ); 94 110 } // while 95 debugPrint( cerr << " " << (*i).first << ":" << (*i).second);111 debugPrint( cerr << " " << (*i).first << ":" << kindName( (*i).second ) ); 96 112 } // for 97 113 while ( scope > 0 ) { -
src/Parser/TypedefTable.h
rb4e1876 r7951100 10 10 // Created On : Sat May 16 15:24:36 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu May 31 23:23:47 201813 // Update Count : 8 312 // Last Modified On : Thu Jun 7 12:10:17 2018 13 // Update Count : 85 14 14 // 15 15 … … 30 30 bool exists( const std::string & identifier ); 31 31 int isKind( const std::string & identifier ) const; 32 void changeKind( const std::string & identifier, int kind ); 33 void makeTypedef( const std::string & name ); 32 void makeTypedef( const std::string & name, int kind = TYPEDEFname ); 34 33 void addToScope( const std::string & identifier, int kind, const char * ); 35 34 void addToEnclosingScope( const std::string & identifier, int kind, const char * ); -
src/Parser/lex.ll
rb4e1876 r7951100 10 10 * Created On : Sat Sep 22 08:58:10 2001 11 11 * Last Modified By : Peter A. Buhr 12 * Last Modified On : Wed Jun 6 17:31:09201813 * Update Count : 67 712 * Last Modified On : Thu Jun 7 08:27:40 2018 13 * Update Count : 679 14 14 */ 15 15 … … 452 452 453 453 %% 454 454 455 // ----end of lexer---- 455 456 456 457 void yyerror( const char * errmsg ) { 458 SemanticErrorThrow = true; 457 459 cout << (yyfilename ? yyfilename : "*unknown file*") << ':' << yylineno << ':' << column - yyleng + 1 458 460 << ": " << ErrorHelpers::error_str() << errmsg << " at token \"" << (yytext[0] == '\0' ? "EOF" : yytext) << '"' << endl; -
src/Parser/parser.yy
rb4e1876 r7951100 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jun 6 14:53:38201813 // Update Count : 352 212 // Last Modified On : Thu Jun 7 10:07:12 2018 13 // Update Count : 3527 14 14 // 15 15 … … 1826 1826 | aggregate_key attribute_list_opt no_attr_identifier 1827 1827 { 1828 typedefTable.makeTypedef( *$3 );// create typedef1829 if ( forall ) typedefTable.changeKind( *$3, TYPEGENname ); // possibly update1828 typedefTable.makeTypedef( *$3, forall ? TYPEGENname : TYPEDEFname ); // create typedef 1829 //if ( forall ) typedefTable.changeKind( *$3, TYPEGENname ); // possibly update 1830 1830 forall = false; // reset 1831 1831 } … … 1834 1834 | aggregate_key attribute_list_opt type_name 1835 1835 { 1836 typedefTable.makeTypedef( *$3->type->symbolic.name ); // create typedef1837 if ( forall ) typedefTable.changeKind( *$3->type->symbolic.name, TYPEGENname ); // possibly update1836 typedefTable.makeTypedef( *$3->type->symbolic.name, forall ? TYPEGENname : TYPEDEFname ); // create typedef 1837 //if ( forall ) typedefTable.changeKind( *$3->type->symbolic.name, TYPEGENname ); // possibly update 1838 1838 forall = false; // reset 1839 1839 } … … 1848 1848 aggregate_key attribute_list_opt no_attr_identifier 1849 1849 { 1850 typedefTable.makeTypedef( *$3 );1851 if ( forall ) typedefTable.changeKind( *$3, TYPEGENname ); // possibly update1850 typedefTable.makeTypedef( *$3, forall ? TYPEGENname : TYPEDEFname ); 1851 //if ( forall ) typedefTable.changeKind( *$3, TYPEGENname ); // possibly update 1852 1852 forall = false; // reset 1853 1853 $$ = DeclarationNode::newAggregate( $1, $3, nullptr, nullptr, false )->addQualifiers( $2 ); … … 3264 3264 3265 3265 %% 3266 3266 3267 // ----end of grammar---- 3267 3268 -
src/libcfa/bits/locks.h
rb4e1876 r7951100 18 18 #include "bits/debug.h" 19 19 #include "bits/defs.h" 20 #include <assert.h> 21 22 #ifdef __cforall 23 extern "C" { 24 #include <pthread.h> 25 } 26 #endif 20 27 21 28 // pause to prevent excess processor bus usage … … 112 119 __atomic_clear( &this.lock, __ATOMIC_RELEASE ); 113 120 } 121 122 123 #ifdef __CFA_WITH_VERIFY__ 124 extern bool __cfaabi_dbg_in_kernel(); 125 #endif 126 127 struct __bin_sem_t { 128 int_fast8_t counter; 129 pthread_mutex_t lock; 130 pthread_cond_t cond; 131 }; 132 133 static inline void ?{}(__bin_sem_t & this) with( this ) { 134 counter = 0; 135 pthread_mutex_init(&lock, NULL); 136 pthread_cond_init (&cond, NULL); 137 } 138 139 static inline void ^?{}(__bin_sem_t & this) with( this ) { 140 pthread_mutex_destroy(&lock); 141 pthread_cond_destroy (&cond); 142 } 143 144 static inline void wait(__bin_sem_t & this) with( this ) { 145 verify(__cfaabi_dbg_in_kernel()); 146 pthread_mutex_lock(&lock); 147 if(counter != 0) { // this must be a loop, not if! 148 pthread_cond_wait(&cond, &lock); 149 } 150 counter = 1; 151 pthread_mutex_unlock(&lock); 152 } 153 154 static inline void post(__bin_sem_t & this) with( this ) { 155 verify(__cfaabi_dbg_in_kernel()); 156 pthread_mutex_lock(&lock); 157 bool needs_signal = counter == 0; 158 counter = 1; 159 pthread_mutex_unlock(&lock); 160 if (!needs_signal) 161 pthread_cond_signal(&cond); 162 } 114 163 #endif -
src/libcfa/concurrency/kernel
rb4e1876 r7951100 133 133 // Idle lock 134 134 sem_t idleLock; 135 // __bin_sem_t idleLock; 135 136 136 137 // Link lists fields 137 struct {138 struct __dbg_node_proc { 138 139 struct processor * next; 139 140 struct processor * prev; … … 182 183 183 184 // Link lists fields 184 struct {185 struct __dbg_node_cltr { 185 186 cluster * next; 186 187 cluster * prev; -
src/libcfa/concurrency/kernel.c
rb4e1876 r7951100 17 17 #include <stddef.h> 18 18 #include <errno.h> 19 #include <string.h> 19 20 extern "C" { 20 21 #include <stdio.h> … … 50 51 thread_desc * mainThread; 51 52 52 struct { __dllist_t(cluster) list; __spinlock_t lock; } global_clusters; 53 extern "C" { 54 struct { __dllist_t(cluster) list; __spinlock_t lock; } __cfa_dbg_global_clusters; 55 } 53 56 54 57 //----------------------------------------------------------------------------- … … 150 153 151 154 void ^?{}(processor & this) with( this ){ 152 if( ! do_terminate) {155 if( ! __atomic_load_n(&do_terminate, __ATOMIC_ACQUIRE) ) { 153 156 __cfaabi_dbg_print_safe("Kernel : core %p signaling termination\n", &this); 154 157 terminate(&this); 155 verify( this.do_terminate);158 verify( __atomic_load_n(&do_terminate, __ATOMIC_SEQ_CST) ); 156 159 verify( kernelTLS.this_processor != &this); 157 160 P( terminated ); … … 199 202 200 203 thread_desc * readyThread = NULL; 201 for( unsigned int spin_count = 0; ! this->do_terminate; spin_count++ )204 for( unsigned int spin_count = 0; ! __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST); spin_count++ ) 202 205 { 203 206 readyThread = nextThread( this->cltr ); … … 218 221 else 219 222 { 220 spin(this, &spin_count); 223 // spin(this, &spin_count); 224 halt(this); 221 225 } 222 226 } … … 545 549 __cfaabi_dbg_print_safe("Kernel : Starting\n"); 546 550 547 global_clusters.list{ __get };548 global_clusters.lock{};551 __cfa_dbg_global_clusters.list{ __get }; 552 __cfa_dbg_global_clusters.lock{}; 549 553 550 554 // Initialize the main cluster … … 627 631 // When its coroutine terminates, it return control to the mainThread 628 632 // which is currently here 629 mainProcessor->do_terminate = true;633 __atomic_store_n(&mainProcessor->do_terminate, true, __ATOMIC_RELEASE); 630 634 returnToKernel(); 635 mainThread->self_cor.state = Halted; 631 636 632 637 // THE SYSTEM IS NOW COMPLETELY STOPPED … … 644 649 ^(mainThread){}; 645 650 646 ^( global_clusters.list){};647 ^( global_clusters.lock){};651 ^(__cfa_dbg_global_clusters.list){}; 652 ^(__cfa_dbg_global_clusters.lock){}; 648 653 649 654 __cfaabi_dbg_print_safe("Kernel : Shutdown complete\n"); … … 655 660 656 661 void halt(processor * this) with( *this ) { 662 verify( ! __atomic_load_n(&do_terminate, __ATOMIC_SEQ_CST) ); 663 657 664 with( *cltr ) { 658 665 lock (proc_list_lock __cfaabi_dbg_ctx2); … … 664 671 __cfaabi_dbg_print_safe("Kernel : Processor %p ready to sleep\n", this); 665 672 666 verify( ({int sval = 0; sem_getvalue(&this->idleLock, &sval); sval; }) < 200); 673 // #ifdef __CFA_WITH_VERIFY__ 674 // int sval = 0; 675 // sem_getvalue(&this->idleLock, &sval); 676 // verifyf(sval < 200, "Binary semaphore reached value %d : \n", sval); 677 // #endif 678 679 verify( ! __atomic_load_n(&do_terminate, __ATOMIC_SEQ_CST) ); 667 680 int __attribute__((unused)) ret = sem_wait(&idleLock); 668 verify(ret > 0 || errno == EINTR); 681 // verifyf(ret >= 0 || errno == EINTR, "Sem_wait returned %d (errno %d : %s\n", ret, errno, strerror(errno)); 682 683 // wait( idleLock ); 669 684 670 685 __cfaabi_dbg_print_safe("Kernel : Processor %p woke up and ready to run\n", this); … … 681 696 __cfaabi_dbg_print_safe("Kernel : Waking up processor %p\n", this); 682 697 int __attribute__((unused)) ret = sem_post(&this->idleLock); 683 verify(ret > 0 || errno == EINTR); 684 verify( ({int sval = 0; sem_getvalue(&this->idleLock, &sval); sval; }) < 200); 698 // verifyf(ret >= 0 || errno == EINTR, "Sem_post returned %d (errno %d : %s\n", ret, errno, strerror(errno)); 699 700 // #ifdef __CFA_WITH_VERIFY__ 701 // int sval = 0; 702 // sem_getvalue(&this->idleLock, &sval); 703 // verifyf(sval < 200, "Binary semaphore reached value %d\n", sval); 704 // #endif 705 706 // post( this->idleLock ); 685 707 } 686 708 … … 798 820 // Global Queues 799 821 void doregister( cluster & cltr ) { 800 lock ( global_clusters.lock __cfaabi_dbg_ctx2);801 push_front( global_clusters.list, cltr );802 unlock ( global_clusters.lock );822 lock ( __cfa_dbg_global_clusters.lock __cfaabi_dbg_ctx2); 823 push_front( __cfa_dbg_global_clusters.list, cltr ); 824 unlock ( __cfa_dbg_global_clusters.lock ); 803 825 } 804 826 805 827 void unregister( cluster & cltr ) { 806 lock ( global_clusters.lock __cfaabi_dbg_ctx2);807 remove( global_clusters.list, cltr );808 unlock( global_clusters.lock );828 lock ( __cfa_dbg_global_clusters.lock __cfaabi_dbg_ctx2); 829 remove( __cfa_dbg_global_clusters.list, cltr ); 830 unlock( __cfa_dbg_global_clusters.lock ); 809 831 } 810 832 -
src/libcfa/concurrency/preemption.c
rb4e1876 r7951100 265 265 // kill wrapper : signal a processor 266 266 void terminate(processor * this) { 267 this->do_terminate = true; 268 wake(this); 267 disable_interrupts(); 268 __atomic_store_n(&this->do_terminate, true, __ATOMIC_SEQ_CST); 269 wake( this ); 269 270 sigval_t value = { PREEMPT_TERMINATE }; 271 enable_interrupts( __cfaabi_dbg_ctx ); 270 272 pthread_sigqueue( this->kernel_thread, SIGUSR1, value ); 271 273 } … … 369 371 choose(sfp->si_value.sival_int) { 370 372 case PREEMPT_NORMAL : ;// Normal case, nothing to do here 371 case PREEMPT_TERMINATE: verify( kernelTLS.this_processor->do_terminate);373 case PREEMPT_TERMINATE: verify( __atomic_load_n( &kernelTLS.this_processor->do_terminate, __ATOMIC_SEQ_CST ) ); 372 374 default: 373 375 abort( "internal error, signal value is %d", sfp->si_value.sival_int ); … … 488 490 } 489 491 492 #ifdef __CFA_WITH_VERIFY__ 493 bool __cfaabi_dbg_in_kernel() { 494 return !kernelTLS.preemption_state.enabled; 495 } 496 #endif 497 490 498 // Local Variables: // 491 499 // mode: c // -
src/libcfa/stdhdr/assert.h
rb4e1876 r7951100 33 33 #define verify(x) assert(x) 34 34 #define verifyf(x, ...) assertf(x, __VA_ARGS__) 35 #define __CFA_WITH_VERIFY__ 35 36 #else 36 37 #define verify(x) -
src/prelude/sync-builtins.cf
rb4e1876 r7951100 248 248 #endif 249 249 250 _Bool __atomic_load_n(const volatile _Bool *, int); 251 void __atomic_load(const volatile _Bool *, volatile _Bool *, int); 250 252 char __atomic_load_n(const volatile char *, int); 251 253 char __atomic_load_1(const volatile char *, int); … … 285 287 286 288 void __atomic_store_n(volatile _Bool *, _Bool, int); 287 void __atomic_store_1(volatile _Bool *, _Bool, int);288 289 void __atomic_store(volatile _Bool *, _Bool *, int); 289 290 void __atomic_store_n(volatile char *, char, int); -
src/tests/preempt_longrun/processor.c
rb4e1876 r7951100 2 2 #include <thread> 3 3 #include <time> 4 5 #include <unistd.h> 4 6 5 7 #ifndef PREEMPTION_RATE … … 15 17 int main(int argc, char* argv[]) { 16 18 processor * p[15]; 19 write(STDERR_FILENO, "Preparing\n", sizeof("Preparing\n")); 17 20 for ( int pi = 0; pi < 15; pi++ ) { 18 21 p[pi] = new(); 19 22 } 23 write(STDERR_FILENO, "Starting\n", sizeof("Starting\n")); 20 24 for ( int i = 0; i < N; i++) { 21 25 int pi = i % 15; … … 23 27 p[pi] = new(); 24 28 } 29 write(STDERR_FILENO, "Stopping\n", sizeof("Stopping\n")); 25 30 for ( int pi = 0; pi < 15; pi++ ) { 26 31 delete( p[pi] ); 27 32 } 33 write(STDERR_FILENO, "Done\n", sizeof("Done\n")); 28 34 }
Note: See TracChangeset
for help on using the changeset viewer.