- File:
-
- 1 edited
-
doc/papers/concurrency/Paper.tex (modified) (16 diffs)
Legend:
- Unmodified
- Added
- Removed
-
doc/papers/concurrency/Paper.tex
rca0f061f r1e5d0f0c 215 215 {} 216 216 \lstnewenvironment{Go}[1][] 217 {\lstset{#1}} 217 {\lstset{language=go,moredelim=**[is][\protect\color{red}]{`}{`},#1}\lstset{#1}} 218 {} 219 \lstnewenvironment{python}[1][] 220 {\lstset{language=python,moredelim=**[is][\protect\color{red}]{`}{`},#1}\lstset{#1}} 218 221 {} 219 222 … … 228 231 } 229 232 230 \title{\texorpdfstring{Advanced Control-flow in \protect\CFA}{Advanced Control-flow in Cforall}}233 \title{\texorpdfstring{Advanced Control-flow and Concurrency in \protect\CFA}{Advanced Control-flow in Cforall}} 231 234 232 235 \author[1]{Thierry Delisle} … … 242 245 \abstract[Summary]{ 243 246 \CFA is a modern, polymorphic, non-object-oriented, backwards-compatible extension of the C programming language. 244 This paper discusses the advanced control-flow features in \CFA, which include concurrency and parallelism, and its supporting runtime system.245 These features are created from scratch as ISO C's concurrency is low-level and unimplemented, so C programmers continue to rely on the C pthreads library.246 \CFA provides high-level control-flow mechanisms, like coroutines and user-level threads, and monitors for mutual exclusion and synchronization.247 A unique contribution of this work is allowing multiple monitors to be safely acquired \emph{simultaneously} (deadlock free), while integrating this capability with allmonitor synchronization mechanisms.248 All features respect the expectations of C programmers, while being fully integrate with the \CFA polymorphic type-system and other language features.247 This paper discusses some advanced control-flow and concurrency/parallelism features in \CFA, along with the supporting runtime. 248 These features are created from scratch because they do not exist in ISO C, or are low-level and/or unimplemented, so C programmers continue to rely on library features, like C pthreads. 249 \CFA introduces language-level control-flow mechanisms, like coroutines, user-level threading, and monitors for mutual exclusion and synchronization. 250 A unique contribution of this work is allowing multiple monitors to be safely acquired \emph{simultaneously} (deadlock free), while integrating this capability with monitor synchronization mechanisms. 251 These features also integrate with the \CFA polymorphic type-system and exception handling, while respecting the expectations and style of C programmers. 249 252 Experimental results show comparable performance of the new features with similar mechanisms in other concurrent programming-languages. 250 253 }% … … 261 264 \section{Introduction} 262 265 263 This paper discusses the design of advanced, high-level control-flow extensions (especially concurrency and parallelism)in \CFA and its runtime.266 This paper discusses the design of language-level control-flow and concurrency/parallelism extensions in \CFA and its runtime. 264 267 \CFA is a modern, polymorphic, non-object-oriented\footnote{ 265 268 \CFA has features often associated with object-oriented programming languages, such as constructors, destructors, virtuals and simple inheritance. 266 269 However, functions \emph{cannot} be nested in structures, so there is no lexical binding between a structure and set of functions (member/method) implemented by an implicit \lstinline@this@ (receiver) parameter.}, 267 270 backwards-compatible extension of the C programming language~\cite{Moss18}. 268 Within the \CFA framework, new control-flow features were created from scratch. 269 ISO \Celeven defines only a subset of the \CFA extensions, and with respect to concurrency~\cite[\S~7.26]{C11}, the features are largely wrappers for a subset of the pthreads library~\cite{Butenhof97,Pthreads}. 270 Furthermore, \Celeven and pthreads concurrency is basic, based on thread fork/join in a function and a few locks, which is low-level and error prone; 271 no high-level language concurrency features exist. 272 Interestingly, almost a decade after publication of the \Celeven standard, neither gcc-8, clang-8 nor msvc-19 (most recent versions) support the \Celeven include @threads.h@, indicating little interest in the C concurrency approach. 273 Finally, while the \Celeven standard does not state a concurrent threading-model, the historical association with pthreads suggests the threading model is kernel-level threading (1:1)~\cite{ThreadModel}. 271 Within the \CFA framework, new control-flow features are created from scratch. 272 ISO \Celeven defines only a subset of the \CFA extensions, where the overlapping features are concurrency~\cite[\S~7.26]{C11}. 273 However, \Celeven concurrency is largely wrappers for a subset of the pthreads library~\cite{Butenhof97,Pthreads}. 274 Furthermore, \Celeven and pthreads concurrency is simple, based on thread fork/join in a function and a few locks, which is low-level and error prone; 275 no high-level language concurrency features are defined. 276 Interestingly, almost a decade after publication of the \Celeven standard, neither gcc-8, clang-8 nor msvc-19 (most recent versions) support the \Celeven include @threads.h@, indicating little interest in the C11 concurrency approach. 277 Finally, while the \Celeven standard does not state a concurrent threading-model, the historical association with pthreads suggests implementations would adopt kernel-level threading (1:1)~\cite{ThreadModel}. 274 278 275 279 In contrast, there has been a renewed interest during the past decade in user-level (M:N, green) threading in old and new programming languages. … … 284 288 285 289 A further effort over the past decade is the development of language memory-models to deal with the conflict between certain language features and compiler/hardware optimizations. 286 This issue can be rephrased as some features are pervasive (language and runtime) and cannot be safely added via a library to prevent invalidation by sequential optimizations~\cite{Buhr95a,Boehm05}.290 This issue can be rephrased as: some language features are pervasive (language and runtime) and cannot be safely added via a library to prevent invalidation by sequential optimizations~\cite{Buhr95a,Boehm05}. 287 291 The consequence is that a language must be cognizant of these features and provide sufficient tools to program around any safety issues. 288 292 For example, C created the @volatile@ qualifier to provide correct execution for @setjmp@/@logjmp@ (concurrency came later). 289 The simplestsolution is to provide a handful of complex qualifiers and functions (e.g., @volatile@ and atomics) allowing programmers to write consistent/race-free programs, often in the sequentially-consistent memory-model~\cite{Boehm12}.293 The common solution is to provide a handful of complex qualifiers and functions (e.g., @volatile@ and atomics) allowing programmers to write consistent/race-free programs, often in the sequentially-consistent memory-model~\cite{Boehm12}. 290 294 291 295 While having a sufficient memory-model allows sound libraries to be constructed, writing these libraries can quickly become awkward and error prone, and using these low-level libraries has the same issues. 292 296 Essentially, using low-level explicit locks is the concurrent equivalent of assembler programming. 293 Just as most assembler programming is replaced with programming in a high-level language, explicit locks can be replaced with high-level concurrency constructsin a programming language.294 The goal is to getthe compiler to check for correct usage and follow any complex coding conventions implicitly.297 Just as most assembler programming is replaced with high-level programming, explicit locks can be replaced with high-level concurrency in a programming language. 298 Then the goal is for the compiler to check for correct usage and follow any complex coding conventions implicitly. 295 299 The drawback is that language constructs may preclude certain specialized techniques, therefore introducing inefficiency or inhibiting concurrency. 296 300 For most concurrent programs, these drawbacks are insignificant in comparison to the speed of composition, and subsequent reliability and maintainability of the high-level concurrent program. … … 299 303 As stated, this observation applies to non-concurrent forms of complex control-flow, like exception handling and coroutines. 300 304 301 Adapting the programming language allows matching the control-flow model with the programming-language style, versus adapting toone general (sound) library/paradigm.305 Adapting the programming language to these features also allows matching the control-flow model with the programming-language style, versus adopting one general (sound) library/paradigm. 302 306 For example, it is possible to provide exceptions, coroutines, monitors, and tasks as specialized types in an object-oriented language, integrating these constructs to allow leveraging the type-system (static type-checking) and all other object-oriented capabilities~\cite{uC++}. 303 307 It is also possible to leverage call/return for blocking communication via new control structures, versus switching to alternative communication paradigms, like channels or message passing. … … 307 311 however, the reverse is seldom true, i.e., given implicit concurrency, e.g., actors, it is virtually impossible to create explicit concurrency, e.g., blocking thread objects.} 308 312 Finally, with extended language features and user-level threading it is possible to discretely fold locking and non-blocking I/O multiplexing into the language's I/O libraries, so threading implicitly dovetails with the I/O subsystem. 309 310 \CFA embraces language extensions and user-level threading to provide advanced control-flow and concurrency. 311 We attempt to show the \CFA extensions and runtime are demonstrably better than those proposed for \CC and other concurrent, imperative programming languages. 312 The contributions of this work are: 313 \CFA embraces language extensions and user-level threading to provide advanced control-flow (exception handling\footnote{ 314 \CFA exception handling will be presented in a separate paper. 315 The key feature that dovetails with this paper is non-local exceptions allowing exceptions to be raised across stacks, with synchronous exceptions raised among coroutines and asynchronous exceptions raised among threads, similar to that in \uC~\cite[\S~5]{uC++} 316 } and coroutines) and concurrency. 317 318 Most augmented traditional (Fortran 18~\cite{Fortran18}, Cobol 14~\cite{Cobol14}, Ada 12~\cite{Ada12}, Java 11~\cite{Java11}) and new languages (Go~\cite{Go}, Rust~\cite{Rust}, and D~\cite{D}), except \CC, diverge from C with different syntax and semantics, only interoperate indirectly with C, and are not systems languages, for those with managed memory. 319 As a result, there is a significant learning curve to move to these languages, and C legacy-code must be rewritten. 320 While \CC, like \CFA, takes an evolutionary approach to extend C, \CC's constantly growing complex and interdependent features-set (e.g., objects, inheritance, templates, etc.) mean idiomatic \CC code is difficult to use from C, and C programmers must expend significant effort learning \CC. 321 Hence, rewriting and retraining costs for these languages, even \CC, are prohibitive for companies with a large C software-base. 322 \CFA with its orthogonal feature-set, its high-performance runtime, and direct access to all existing C libraries circumvents these problems. 323 324 We present comparative examples so the reader can judge if the \CFA control-flow extensions are equivalent or better than those in or proposed for \Celeven, \CC and other concurrent, imperative programming languages, and perform experiments to show the \CFA runtime is competitive with other similar mechanisms. 325 The detailed contributions of this work are: 313 326 \begin{itemize} 314 327 \item … … 615 628 616 629 617 \section{Coroutines: A Stepping Stone}\label{coroutine}618 619 Advanced controlWhile the focus of this discussion is concurrency and parallelism, it is important to address coroutines, which are a significant building block of a concurrency system (but not concurrent among themselves). 630 \section{Coroutines: Stepping Stone} 631 \label{coroutine} 632 620 633 Coroutines are generalized routines allowing execution to be temporarily suspended and later resumed. 621 634 Hence, unlike a normal routine, a coroutine may not terminate when it returns to its caller, allowing it to be restarted with the values and execution location present at the point of suspension. … … 641 654 \centering 642 655 \newbox\myboxA 656 % \begin{lrbox}{\myboxA} 657 % \begin{cfa}[aboveskip=0pt,belowskip=0pt] 658 % `int fn1, fn2, state = 1;` // single global variables 659 % int fib() { 660 % int fn; 661 % `switch ( state )` { // explicit execution state 662 % case 1: fn = 0; fn1 = fn; state = 2; break; 663 % case 2: fn = 1; fn2 = fn1; fn1 = fn; state = 3; break; 664 % case 3: fn = fn1 + fn2; fn2 = fn1; fn1 = fn; break; 665 % } 666 % return fn; 667 % } 668 % int main() { 669 % 670 % for ( int i = 0; i < 10; i += 1 ) { 671 % printf( "%d\n", fib() ); 672 % } 673 % } 674 % \end{cfa} 675 % \end{lrbox} 643 676 \begin{lrbox}{\myboxA} 644 677 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 645 `int f1, f2, state = 1;` // single global variables 646 int fib() { 647 int fn; 648 `switch ( state )` { // explicit execution state 649 case 1: fn = 0; f1 = fn; state = 2; break; 650 case 2: fn = 1; f2 = f1; f1 = fn; state = 3; break; 651 case 3: fn = f1 + f2; f2 = f1; f1 = fn; break; 652 } 653 return fn; 654 } 678 #define FIB_INIT { 0, 1 } 679 typedef struct { int fn1, fn; } Fib; 680 int fib( Fib * f ) { 681 682 int ret = f->fn1; 683 f->fn1 = f->fn; 684 f->fn = ret + f->fn; 685 return ret; 686 } 687 688 689 655 690 int main() { 656 691 Fib f1 = FIB_INIT, f2 = FIB_INIT; 657 692 for ( int i = 0; i < 10; i += 1 ) { 658 printf( "%d\n", fib() ); 693 printf( "%d %d\n", 694 fib( &f1 ), fib( &f2 ) ); 659 695 } 660 696 } … … 665 701 \begin{lrbox}{\myboxB} 666 702 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 667 #define FIB_INIT `{ 0, 1 }` 668 typedef struct { int f2, f1; } Fib; 669 int fib( Fib * f ) { 670 671 int ret = f->f2; 672 int fn = f->f1 + f->f2; 673 f->f2 = f->f1; f->f1 = fn; 674 675 return ret; 676 } 677 int main() { 678 Fib f1 = FIB_INIT, f2 = FIB_INIT; 679 for ( int i = 0; i < 10; i += 1 ) { 680 printf( "%d %d\n", fib( &f1 ), fib( &f2 ) ); 703 `coroutine` Fib { int fn1; }; 704 void main( Fib & fib ) with( fib ) { 705 int fn; 706 [fn1, fn] = [0, 1]; 707 for () { 708 `suspend();` 709 [fn1, fn] = [fn, fn1 + fn]; 681 710 } 682 711 } 683 \end{cfa} 684 \end{lrbox} 685 686 \subfloat[3 States: global variables]{\label{f:GlobalVariables}\usebox\myboxA} 687 \qquad 688 \subfloat[1 State: external variables]{\label{f:ExternalState}\usebox\myboxB} 689 \caption{C Fibonacci Implementations} 690 \label{f:C-fibonacci} 691 692 \bigskip 693 694 \newbox\myboxA 695 \begin{lrbox}{\myboxA} 696 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 697 `coroutine` Fib { int fn; }; 698 void main( Fib & fib ) with( fib ) { 699 int f1, f2; 700 fn = 0; f1 = fn; `suspend()`; 701 fn = 1; f2 = f1; f1 = fn; `suspend()`; 702 for ( ;; ) { 703 fn = f1 + f2; f2 = f1; f1 = fn; `suspend()`; 704 } 705 } 706 int next( Fib & fib ) with( fib ) { 707 `resume( fib );` 708 return fn; 712 int ?()( Fib & fib ) with( fib ) { 713 `resume( fib );` return fn1; 709 714 } 710 715 int main() { 711 716 Fib f1, f2; 712 for ( int i = 1; i <= 10; i += 1 ) { 713 sout | next( f1 ) | next( f2 ); 714 } 715 } 717 for ( 10 ) { 718 sout | f1() | f2(); 719 } 720 721 716 722 \end{cfa} 717 723 \end{lrbox} 718 \newbox\myboxB 719 \begin{lrbox}{\myboxB} 720 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 721 `coroutine` Fib { int ret; }; 722 void main( Fib & f ) with( fib ) { 723 int fn, f1 = 1, f2 = 0; 724 for ( ;; ) { 725 ret = f2; 726 727 fn = f1 + f2; f2 = f1; f1 = fn; `suspend();` 728 } 729 } 730 int next( Fib & fib ) with( fib ) { 731 `resume( fib );` 732 return ret; 733 } 734 735 736 737 738 739 740 \end{cfa} 724 725 \newbox\myboxC 726 \begin{lrbox}{\myboxC} 727 \begin{python}[aboveskip=0pt,belowskip=0pt] 728 729 def Fib(): 730 731 fn1, fn = 0, 1 732 while True: 733 `yield fn1` 734 fn1, fn = fn, fn1 + fn 735 736 737 // next prewritten 738 739 740 f1 = Fib() 741 f2 = Fib() 742 for i in range( 10 ): 743 print( next( f1 ), next( f2 ) ) 744 745 746 747 \end{python} 741 748 \end{lrbox} 742 \subfloat[3 States, internal variables]{\label{f:Coroutine3States}\usebox\myboxA} 743 \qquad\qquad 744 \subfloat[1 State, internal variables]{\label{f:Coroutine1State}\usebox\myboxB} 745 \caption{\CFA Coroutine Fibonacci Implementations} 746 \label{f:cfa-fibonacci} 749 750 \subfloat[C]{\label{f:GlobalVariables}\usebox\myboxA} 751 \hspace{3pt} 752 \vrule 753 \hspace{3pt} 754 \subfloat[\CFA]{\label{f:ExternalState}\usebox\myboxB} 755 \hspace{3pt} 756 \vrule 757 \hspace{3pt} 758 \subfloat[Python]{\label{f:ExternalState}\usebox\myboxC} 759 \caption{Fibonacci Generator} 760 \label{f:C-fibonacci} 761 762 % \bigskip 763 % 764 % \newbox\myboxA 765 % \begin{lrbox}{\myboxA} 766 % \begin{cfa}[aboveskip=0pt,belowskip=0pt] 767 % `coroutine` Fib { int fn; }; 768 % void main( Fib & fib ) with( fib ) { 769 % fn = 0; int fn1 = fn; `suspend()`; 770 % fn = 1; int fn2 = fn1; fn1 = fn; `suspend()`; 771 % for () { 772 % fn = fn1 + fn2; fn2 = fn1; fn1 = fn; `suspend()`; } 773 % } 774 % int next( Fib & fib ) with( fib ) { `resume( fib );` return fn; } 775 % int main() { 776 % Fib f1, f2; 777 % for ( 10 ) 778 % sout | next( f1 ) | next( f2 ); 779 % } 780 % \end{cfa} 781 % \end{lrbox} 782 % \newbox\myboxB 783 % \begin{lrbox}{\myboxB} 784 % \begin{python}[aboveskip=0pt,belowskip=0pt] 785 % 786 % def Fibonacci(): 787 % fn = 0; fn1 = fn; `yield fn` # suspend 788 % fn = 1; fn2 = fn1; fn1 = fn; `yield fn` 789 % while True: 790 % fn = fn1 + fn2; fn2 = fn1; fn1 = fn; `yield fn` 791 % 792 % 793 % f1 = Fibonacci() 794 % f2 = Fibonacci() 795 % for i in range( 10 ): 796 % print( `next( f1 )`, `next( f2 )` ) # resume 797 % 798 % \end{python} 799 % \end{lrbox} 800 % \subfloat[\CFA]{\label{f:Coroutine3States}\usebox\myboxA} 801 % \qquad 802 % \subfloat[Python]{\label{f:Coroutine1State}\usebox\myboxB} 803 % \caption{Fibonacci input coroutine, 3 states, internal variables} 804 % \label{f:cfa-fibonacci} 747 805 \end{figure} 748 806 … … 784 842 \begin{lrbox}{\myboxA} 785 843 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 786 `coroutine` F ormat {787 char ch; // used for communication788 int g, b; // global because used in destructor844 `coroutine` Fmt { 845 char ch; // communication variables 846 int g, b; // needed in destructor 789 847 }; 790 void main( F ormat & fmt ) with( fmt ) {791 for ( ;;) {792 for ( g = 0; g < 5; g += 1 ) { // group793 for ( b = 0; b < 4; b += 1 ) { // block 848 void main( Fmt & fmt ) with( fmt ) { 849 for () { 850 for ( g = 0; g < 5; g += 1 ) { // groups 851 for ( b = 0; b < 4; b += 1 ) { // blocks 794 852 `suspend();` 795 sout | ch; // separator 796 } 797 sout | " "; // separator 798 } 799 sout | nl; 800 } 801 } 802 void ?{}( Format & fmt ) { `resume( fmt );` } 803 void ^?{}( Format & fmt ) with( fmt ) { 804 if ( g != 0 || b != 0 ) sout | nl; 805 } 806 void format( Format & fmt ) { 807 `resume( fmt );` 808 } 853 sout | ch; } // print character 854 sout | " "; } // block separator 855 sout | nl; } // group separator 856 } 857 void ?{}( Fmt & fmt ) { `resume( fmt );` } // prime 858 void ^?{}( Fmt & fmt ) with( fmt ) { // destructor 859 if ( g != 0 || b != 0 ) // special case 860 sout | nl; } 861 void send( Fmt & fmt, char c ) { fmt.ch = c; `resume( fmt )`; } 809 862 int main() { 810 Format fmt; 811 eof: for ( ;; ) { 812 sin | fmt.ch; 813 if ( eof( sin ) ) break eof; 814 format( fmt ); 815 } 863 Fmt fmt; 864 sout | nlOff; // turn off auto newline 865 for ( 41 ) 866 send( fmt, 'a' ); 816 867 } 817 868 \end{cfa} … … 820 871 \newbox\myboxB 821 872 \begin{lrbox}{\myboxB} 822 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 823 struct Format { 824 char ch; 825 int g, b; 826 }; 827 void format( struct Format * fmt ) { 828 if ( fmt->ch != -1 ) { // not EOF ? 829 printf( "%c", fmt->ch ); 830 fmt->b += 1; 831 if ( fmt->b == 4 ) { // block 832 printf( " " ); // separator 833 fmt->b = 0; 834 fmt->g += 1; 835 } 836 if ( fmt->g == 5 ) { // group 837 printf( "\n" ); // separator 838 fmt->g = 0; 839 } 840 } else { 841 if ( fmt->g != 0 || fmt->b != 0 ) printf( "\n" ); 842 } 843 } 844 int main() { 845 struct Format fmt = { 0, 0, 0 }; 846 for ( ;; ) { 847 scanf( "%c", &fmt.ch ); 848 if ( feof( stdin ) ) break; 849 format( &fmt ); 850 } 851 fmt.ch = -1; 852 format( &fmt ); 853 } 854 \end{cfa} 873 \begin{python}[aboveskip=0pt,belowskip=0pt] 874 875 876 877 def Fmt(): 878 try: 879 while True: 880 for g in range( 5 ): 881 for b in range( 4 ): 882 883 print( `(yield)`, end='' ) 884 print( ' ', end='' ) 885 print() 886 887 888 except GeneratorExit: 889 if g != 0 | b != 0: 890 print() 891 892 893 fmt = Fmt() 894 `next( fmt )` # prime 895 for i in range( 41 ): 896 `fmt.send( 'a' );` # send to yield 897 898 \end{python} 855 899 \end{lrbox} 856 \subfloat[\CFA Coroutine]{\label{f:CFAFmt}\usebox\myboxA}900 \subfloat[\CFA]{\label{f:CFAFmt}\usebox\myboxA} 857 901 \qquad 858 \subfloat[ C Linearized]{\label{f:CFmt}\usebox\myboxB}859 \caption{ Formatting text into lines of 5 blocks of 4 characters.}902 \subfloat[Python]{\label{f:CFmt}\usebox\myboxB} 903 \caption{Output formatting text} 860 904 \label{f:fmt-line} 861 905 \end{figure} … … 878 922 void main( Prod & prod ) with( prod ) { 879 923 // 1st resume starts here 880 for ( i nt i = 0; i < N; i += 1) {924 for ( i; N ) { 881 925 int p1 = random( 100 ), p2 = random( 100 ); 882 926 sout | p1 | " " | p2; … … 894 938 } 895 939 void start( Prod & prod, int N, Cons &c ) { 896 &prod.c = &c; 940 &prod.c = &c; // reassignable reference 897 941 prod.[N, receipt] = [N, 0]; 898 942 `resume( prod );` … … 909 953 Prod & p; 910 954 int p1, p2, status; 911 _Bool done;955 bool done; 912 956 }; 913 957 void ?{}( Cons & cons, Prod & p ) { 914 &cons.p = &p; 958 &cons.p = &p; // reassignable reference 915 959 cons.[status, done ] = [0, false]; 916 960 } … … 969 1013 The program main restarts after the resume in @start@. 970 1014 @start@ returns and the program main terminates. 1015 1016 One \emph{killer} application for a coroutine is device drivers, which at one time caused 70\%-85\% of failures in Windows/Linux~\cite{Swift05}. 1017 Many device drivers are a finite state-machine parsing a protocol, e.g.: 1018 \begin{tabbing} 1019 \ldots STX \= \ldots message \ldots \= ESC \= ETX \= \ldots message \ldots \= ETX \= 2-byte crc \= \ldots \kill 1020 \ldots STX \> \ldots message \ldots \> ESC \> ETX \> \ldots message \ldots \> ETX \> 2-byte crc \> \ldots 1021 \end{tabbing} 1022 where a network message begins with the control character STX and ends with an ETX, followed by a 2-byte cyclic-redundancy check. 1023 Control characters may appear in a message if preceded by an ESC. 1024 Because FSMs can be complex and occur frequently in important domains, direct support of the coroutine is crucial in a systems programminglanguage. 1025 1026 \begin{figure} 1027 \begin{cfa} 1028 enum Status { CONT, MSG, ESTX, ELNTH, ECRC }; 1029 `coroutine` Driver { 1030 Status status; 1031 char * msg, byte; 1032 }; 1033 void ?{}( Driver & d, char * m ) { d.msg = m; } $\C[3.0in]{// constructor}$ 1034 Status next( Driver & d, char b ) with( d ) { $\C{// 'with' opens scope}$ 1035 byte = b; `resume( d );` return status; 1036 } 1037 void main( Driver & d ) with( d ) { 1038 enum { STX = '\002', ESC = '\033', ETX = '\003', MaxMsg = 64 }; 1039 unsigned short int crc; $\C{// error checking}$ 1040 msg: for () { $\C{// parse message}$ 1041 status = CONT; 1042 unsigned int lnth = 0, sum = 0; 1043 while ( byte != STX ) `suspend();` 1044 emsg: for () { 1045 `suspend();` $\C{// process byte}$ 1046 choose ( byte ) { $\C{// switch with default break}$ 1047 case STX: 1048 status = ESTX; `suspend();` continue msg; 1049 case ETX: 1050 break emsg; 1051 case ESC: 1052 suspend(); 1053 } // choose 1054 if ( lnth >= MaxMsg ) { $\C{// buffer full ?}$ 1055 status = ELNTH; `suspend();` continue msg; } 1056 msg[lnth++] = byte; 1057 sum += byte; 1058 } // for 1059 msg[lnth] = '\0'; $\C{// terminate string}\CRT$ 1060 `suspend();` 1061 crc = (unsigned char)byte << 8; // prevent sign extension for signed char 1062 `suspend();` 1063 status = (crc | (unsigned char)byte) == sum ? MSG : ECRC; 1064 `suspend();` 1065 } // for 1066 } 1067 \end{cfa} 1068 \caption{Device driver for simple communication protocol} 1069 \end{figure} 971 1070 972 1071
Note:
See TracChangeset
for help on using the changeset viewer.