- File:
-
- 1 edited
-
doc/papers/concurrency/Paper.tex (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
doc/papers/concurrency/Paper.tex
ra87c86f rc8ad5d9 22 22 \captionsetup{justification=raggedright,singlelinecheck=false} 23 23 \usepackage{siunitx} 24 \sisetup{ binary-units=true}24 \sisetup{ binary-units=true } 25 25 26 26 \hypersetup{breaklinks=true} … … 32 32 \renewcommand{\linenumberfont}{\scriptsize\sffamily} 33 33 34 \renewcommand{\textfraction}{0.0} % the entire page maybe devoted to floats with no text on the page at all34 \renewcommand{\textfraction}{0.0} % the entire page maybe devoted to floats with no text on the page at all 35 35 36 36 \lefthyphenmin=3 % hyphen only after 4 characters … … 70 70 %\DeclareTextCommandDefault{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.1ex}}} 71 71 \renewcommand{\textunderscore}{\leavevmode\makebox[1.2ex][c]{\rule{1ex}{0.075ex}}} 72 %\def\myCHarFont{\fontencoding{T1}\selectfont}%73 % \def\{{\ttfamily\upshape\myCHarFont \char`\}}}%74 72 75 73 \makeatletter … … 246 244 \maketitle 247 245 248 246 % ====================================================================== 247 % ====================================================================== 249 248 \section{Introduction} 249 % ====================================================================== 250 % ====================================================================== 250 251 251 252 This paper provides a minimal concurrency \newterm{Abstract Program Interface} (API) that is simple, efficient and can be used to build other concurrency features. … … 253 254 An easier approach for programmers is to support higher-level constructs as the basis of concurrency. 254 255 Indeed, for highly productive concurrent programming, high-level approaches are much more popular~\cite{Hochstein05}. 255 Examples of high-level approaches are task (work) based~\cite{TBB}, implicit threading~\cite{OpenMP}, monitors~\cite{Java}, channels~\cite{CSP,Go}, and message passing~\cite{Erlang,MPI}.256 Examples of high-level approaches are task based~\cite{TBB}, message passing~\cite{Erlang,MPI}, and implicit threading~\cite{OpenMP}. 256 257 257 258 This paper uses the following terminology. … … 271 272 The paper discusses how the language features are added to the \CFA translator with respect to parsing, semantic, and type checking, and the corresponding high-perforamnce runtime-library to implement the concurrency features. 272 273 273 274 % ====================================================================== 275 % ====================================================================== 274 276 \section{\CFA Overview} 277 % ====================================================================== 278 % ====================================================================== 275 279 276 280 The following is a quick introduction to the \CFA language, specifically tailored to the features needed to support concurrency. 277 Extended versions of the following code examples are available at the \CFA website~\cite{Cforall} or Moss~\etal~\cite{XXX}.278 279 \CFA is an extension of ISO-C, and hence, supports all C paradigms.281 Most of the following code examples can be found on the \CFA website~\cite{Cforall}. 282 283 \CFA is an extension of ISO-C, and therefore, supports all of the same paradigms as C. 280 284 %It is a non-object-oriented system-language, meaning most of the major abstractions have either no runtime overhead or can be opted out easily. 281 Like C, the basics of \CFA revolve around structures and functions. 282 Virtually all of the code generated by the \CFA translator respects C memory layouts and calling conventions. 283 While \CFA is not an object-oriented language, lacking the concept of a receiver (\eg @this@) and nominal inheritance-relationships, C does have a notion of objects: ``region of data storage in the execution environment, the contents of which can represent values''~\cite[3.15]{C11}. 285 Like C, the basics of \CFA revolve around structures and routines, which are thin abstractions over machine code. 286 The vast majority of the code produced by the \CFA translator respects memory layouts and calling conventions laid out by C. 287 Interestingly, while \CFA is not an object-oriented language, lacking the concept of a receiver (\eg @this@) and inheritance, it does have some notion of objects\footnote{C defines the term objects as : ``region of data storage in the execution environment, the contents of which can represent 288 values''~\cite[3.15]{C11}}, most importantly construction and destruction of objects. 284 289 285 290 286 291 \subsection{References} 287 292 288 \CFA provides multi-level rebindable references, as an alternative to pointers, which significantly reduces syntactic noise. 289 \begin{cfa} 290 int x = 1, y = 2, z = 3; 291 int * p1 = &x, ** p2 = &p1, *** p3 = &p2, $\C{// pointers to x}$ 292 `&` r1 = x, `&&` r2 = r1, `&&&` r3 = r2; $\C{// references to x}$ 293 int * p4 = &z, `&` r4 = z; 294 295 *p1 = 3; **p2 = 3; ***p3 = 3; // change x 296 r1 = 3; r2 = 3; r3 = 3; // change x: implicit dereferences *r1, **r2, ***r3 297 **p3 = &y; *p3 = &p4; // change p1, p2 298 `&`r3 = &y; `&&`r3 = &`&`r4; // change r1, r2: cancel implicit deferences (&*)**r3, (&(&*)*)*r3, &(&*)r4 299 \end{cfa} 300 A reference is a handle to an object, like a pointer, but is automatically dereferenced the specified number of levels. 301 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. 302 303 304 \subsection{\texorpdfstring{\protect\lstinline{with} Statement}{with Statement}} 305 \label{s:WithStatement} 306 307 Heterogeneous data is often aggregated into a structure/union. 308 To reduce syntactic noise, \CFA provides a @with@ statement (see Pascal~\cite[\S~4.F]{Pascal}) to elide aggregate field-qualification by opening a scope containing the field identifiers. 309 \begin{cquote} 310 \vspace*{-\baselineskip}%??? 311 \lstDeleteShortInline@% 312 \begin{cfa} 313 struct S { char c; int i; double d; }; 314 struct T { double m, n; }; 315 // multiple aggregate parameters 316 \end{cfa} 317 \begin{tabular}{@{}l@{\hspace{\parindentlnth}}|@{\hspace{\parindentlnth}}l@{}} 318 \begin{cfa} 319 void f( S & s, T & t ) { 320 `s.`c; `s.`i; `s.`d; 321 `t.`m; `t.`n; 322 } 323 \end{cfa} 324 & 325 \begin{cfa} 326 void f( S & s, T & t ) `with ( s, t )` { 327 c; i; d; // no qualification 328 m; n; 329 } 330 \end{cfa} 331 \end{tabular} 332 \lstMakeShortInline@% 333 \end{cquote} 334 Object-oriented programming languages only provide implicit qualification for the receiver. 335 336 In detail, the @with@ statement has the form: 337 \begin{cfa} 338 $\emph{with-statement}$: 339 'with' '(' $\emph{expression-list}$ ')' $\emph{compound-statement}$ 340 \end{cfa} 341 and may appear as the body of a function or nested within a function body. 342 Each expression in the expression-list provides a type and object. 343 The type must be an aggregate type. 344 (Enumerations are already opened.) 345 The object is the implicit qualifier for the open structure-fields. 346 All expressions in the expression list are open in parallel within the compound statement, which is different from Pascal, which nests the openings from left to right. 347 348 293 Like \CC, \CFA introduces rebind-able references providing multiple dereferencing as an alternative to pointers. 294 In regards to concurrency, the semantic difference between pointers and references are not particularly relevant, but since this document uses mostly references, here is a quick overview of the semantics: 295 \begin{cfa} 296 int x, y, z; 297 int * p1 = &x, ** p2 = &p1, *** p3 = &p2, $\C{// pointers to x}$ 298 & r1 = x, && r2 = r1, &&& r3 = r2; $\C{// references to x}$ 299 300 *p1 = 3; **p2 = 3; ***p3 = 3; $\C{// change x}$ 301 r1 = 3; r2 = 3; r3 = 3; $\C{// change x}$ 302 **p3 = &y; *p3 = &z; $\C{// change p1, p2}$ 303 &&r3 = &y; &r3 = &z; $\C{// change p1, p2}$ 304 int & ar[3] = {x, y, z}; $\C{// initialize array of references}$ 305 306 typeof( ar[1]) p; $\C{// is int, referenced object type}$ 307 typeof(&ar[1]) q; $\C{// is int \&, reference type}$ 308 sizeof( ar[1]) == sizeof(int); $\C{// is true, referenced object size}$ 309 sizeof(&ar[1]) == sizeof(int *); $\C{// is true, reference size}$ 310 \end{cfa} 311 The important take away from this code example is that a reference offers a handle to an object, much like a pointer, but which is automatically dereferenced for convenience. 312 313 % ====================================================================== 349 314 \subsection{Overloading} 350 315 351 \CFA maximizes the ability to reuse names via overloading to aggressively address the naming problem. 352 Both variables and functions may be overloaded, where selection is based on types, and number of returns (as in Ada~\cite{Ada}) and arguments. 353 \begin{cquote} 354 \vspace*{-\baselineskip}%??? 355 \lstDeleteShortInline@% 356 \begin{cfa} 357 // selection based on type 358 \end{cfa} 359 \begin{tabular}{@{}l@{\hspace{\parindentlnth}}|@{\hspace{\parindentlnth}}l@{}} 360 \begin{cfa} 361 const short int MIN = -32768; 362 const int MIN = -2147483648; 363 const long int MIN = -9223372036854775808L; 364 \end{cfa} 365 & 366 \begin{cfa} 367 short int si = MIN; 368 int i = MIN; 369 long int li = MIN; 370 \end{cfa} 371 \end{tabular} 316 Another important feature of \CFA is function overloading as in Java and \CC, where routines with the same name are selected based on the number and type of the arguments. 317 As well, \CFA uses the return type as part of the selection criteria, as in Ada~\cite{Ada}. 318 For routines with multiple parameters and returns, the selection is complex. 372 319 \begin{cfa} 373 320 // selection based on type and number of parameters 374 \end{cfa} 375 \begin{tabular}{@{}l@{\hspace{1.7\parindentlnth}}|@{\hspace{\parindentlnth}}l@{}} 376 \begin{cfa} 377 void f( void ); 378 void f( char ); 379 void f( int, double ); 380 \end{cfa} 381 & 382 \begin{cfa} 383 f(); 384 f( 'a' ); 385 f( 3, 5.2 ); 386 \end{cfa} 387 \end{tabular} 388 \begin{cfa} 389 // selection based on type and number of returns 390 \end{cfa} 391 \begin{tabular}{@{}l@{\hspace{\parindentlnth}}|@{\hspace{\parindentlnth}}l@{}} 392 \begin{cfa} 393 char f( int ); 394 double f( int ); 395 [char, double] f( int ); 396 \end{cfa} 397 & 398 \begin{cfa} 399 char c = f( 3 ); 400 double d = f( 3 ); 401 [d, c] = f( 3 ); 402 \end{cfa} 403 \end{tabular} 404 \lstMakeShortInline@% 405 \end{cquote} 406 Overloading is important for \CFA concurrency since the runtime system relies on creating different types to represent concurrency objects. 321 void f(void); $\C{// (1)}$ 322 void f(char); $\C{// (2)}$ 323 void f(int, double); $\C{// (3)}$ 324 f(); $\C{// select (1)}$ 325 f('a'); $\C{// select (2)}$ 326 f(3, 5.2); $\C{// select (3)}$ 327 328 // selection based on type and number of returns 329 char f(int); $\C{// (1)}$ 330 double f(int); $\C{// (2)}$ 331 char c = f(3); $\C{// select (1)}$ 332 double d = f(4); $\C{// select (2)}$ 333 \end{cfa} 334 This feature is particularly important for concurrency since the runtime system relies on creating different types to represent concurrency objects. 407 335 Therefore, overloading is necessary to prevent the need for long prefixes and other naming conventions that prevent name clashes. 408 As seen in Section~\ref{basics}, function @main@ is heavily overloaded. 409 410 Variable overloading is useful in the parallel semantics of the @with@ statement for fields with the same name: 411 \begin{cfa} 412 struct S { int `i`; int j; double m; } s; 413 struct T { int `i`; int k; int m; } t; 414 with ( s, t ) { 415 j + k; $\C{// unambiguous, s.j + t.k}$ 416 m = 5.0; $\C{// unambiguous, t.m = 5.0}$ 417 m = 1; $\C{// unambiguous, s.m = 1}$ 418 int a = m; $\C{// unambiguous, a = s.i }$ 419 double b = m; $\C{// unambiguous, b = t.m}$ 420 int c = `s.i` + `t.i`; $\C{// unambiguous, qualification}$ 421 (double)m; $\C{// unambiguous, cast}$ 422 } 423 \end{cfa} 424 For parallel semantics, both @s.i@ and @t.i@ are visible with the same type, so only @i@ is ambiguous without qualification. 425 426 336 As seen in section \ref{basics}, routine @main@ is an example that benefits from overloading. 337 338 % ====================================================================== 427 339 \subsection{Operators} 428 429 340 Overloading also extends to operators. 430 Operator-overloading syntax names a routine with the operator symbol and question marks for the operands: 431 \begin{cquote} 432 \lstDeleteShortInline@% 433 \begin{tabular}{@{}ll@{\hspace{\parindentlnth}}|@{\hspace{\parindentlnth}}l@{}} 434 \begin{cfa} 435 int ++? (int op); 436 int ?++ (int op); 437 int `?+?` (int op1, int op2); 438 int ?<=?(int op1, int op2); 439 int ?=? (int & op1, int op2); 440 int ?+=?(int & op1, int op2); 441 \end{cfa} 442 & 443 \begin{cfa} 444 // unary prefix increment 445 // unary postfix increment 446 // binary plus 447 // binary less than 448 // binary assignment 449 // binary plus-assignment 450 \end{cfa} 451 & 452 \begin{cfa} 453 struct S { int i, j; }; 454 S `?+?`( S op1, S op2) { // add two structures 341 The syntax for denoting operator-overloading is to name a routine with the symbol of the operator and question marks where the arguments of the operation appear, \eg: 342 \begin{cfa} 343 int ++? (int op); $\C{// unary prefix increment}$ 344 int ?++ (int op); $\C{// unary postfix increment}$ 345 int ?+? (int op1, int op2); $\C{// binary plus}$ 346 int ?<=?(int op1, int op2); $\C{// binary less than}$ 347 int ?=? (int & op1, int op2); $\C{// binary assignment}$ 348 int ?+=?(int & op1, int op2); $\C{// binary plus-assignment}$ 349 350 struct S {int i, j;}; 351 S ?+?(S op1, S op2) { $\C{// add two structures}$ 455 352 return (S){op1.i + op2.i, op1.j + op2.j}; 456 353 } 457 354 S s1 = {1, 2}, s2 = {2, 3}, s3; 458 s3 = s1 `+` s2; // compute sum: s3 == {2, 5} 459 \end{cfa} 460 \end{tabular} 461 \lstMakeShortInline@% 462 \end{cquote} 463 While concurrency does not use operator overloading directly, it provides an introduction for the syntax of constructors. 464 465 355 s3 = s1 + s2; $\C{// compute sum: s3 == {2, 5}}$ 356 \end{cfa} 357 While concurrency does not use operator overloading directly, this feature is more important as an introduction for the syntax of constructors. 358 359 % ====================================================================== 360 \subsection{Constructors/Destructors} 361 Object lifetime is often a challenge in concurrency. \CFA uses the approach of giving concurrent meaning to object lifetime as a means of synchronization and/or mutual exclusion. 362 Since \CFA relies heavily on the lifetime of objects, constructors and destructors is a core feature required for concurrency and parallelism. \CFA uses the following syntax for constructors and destructors: 363 \begin{cfa} 364 struct S { 365 size_t size; 366 int * ia; 367 }; 368 void ?{}(S & s, int asize) { $\C{// constructor operator}$ 369 s.size = asize; $\C{// initialize fields}$ 370 s.ia = calloc(size, sizeof(S)); 371 } 372 void ^?{}(S & s) { $\C{// destructor operator}$ 373 free(ia); $\C{// de-initialization fields}$ 374 } 375 int main() { 376 S x = {10}, y = {100}; $\C{// implicit calls: ?\{\}(x, 10), ?\{\}(y, 100)}$ 377 ... $\C{// use x and y}$ 378 ^x{}; ^y{}; $\C{// explicit calls to de-initialize}$ 379 x{20}; y{200}; $\C{// explicit calls to reinitialize}$ 380 ... $\C{// reuse x and y}$ 381 } $\C{// implicit calls: \^?\{\}(y), \^?\{\}(x)}$ 382 \end{cfa} 383 The language guarantees that every object and all their fields are constructed. 384 Like \CC, construction of an object is automatically done on allocation and destruction of the object is done on deallocation. 385 Allocation and deallocation can occur on the stack or on the heap. 386 \begin{cfa} 387 { 388 struct S s = {10}; $\C{// allocation, call constructor}$ 389 ... 390 } $\C{// deallocation, call destructor}$ 391 struct S * s = new(); $\C{// allocation, call constructor}$ 392 ... 393 delete(s); $\C{// deallocation, call destructor}$ 394 \end{cfa} 395 Note that like \CC, \CFA introduces @new@ and @delete@, which behave like @malloc@ and @free@ in addition to constructing and destructing objects, after calling @malloc@ and before calling @free@, respectively. 396 397 % ====================================================================== 466 398 \subsection{Parametric Polymorphism} 467 399 \label{s:ParametricPolymorphism} 468 469 Th e signature feature of \CFA is parametric-polymorphic functions~\cite{} with functions generalized using a @forall@ clause (giving the language its name), which allow separately compiled routines to support generic usage over multiple types.400 Routines in \CFA can also be reused for multiple types. 401 This capability is done using the @forall@ clauses, which allow separately compiled routines to support generic usage over multiple types. 470 402 For example, the following sum function works for any type that supports construction from 0 and addition: 471 403 \begin{cfa} 472 forall( otype T | { void `?{}`( T *, zero_t ); T `?+?`( T, T ); } ) // constraint type, 0 and + 473 T sum( T a[$\,$], size_t size ) { 474 `T` total = { `0` }; $\C{// initialize by 0 constructor}$ 475 for ( size_t i = 0; i < size; i += 1 ) 476 total = total `+` a[i]; $\C{// select appropriate +}$ 404 // constraint type, 0 and + 405 forall(otype T | { void ?{}(T *, zero_t); T ?+?(T, T); }) 406 T sum(T a[ ], size_t size) { 407 T total = 0; $\C{// construct T from 0}$ 408 for(size_t i = 0; i < size; i++) 409 total = total + a[i]; $\C{// select appropriate +}$ 477 410 return total; 478 411 } 412 479 413 S sa[5]; 480 int i = sum( sa, 5 ); $\C{// use S's 0 construction and +}$ 481 \end{cfa} 482 483 \CFA provides \newterm{traits} to name a group of type assertions, where the trait name allows specifying the same set of assertions in multiple locations, preventing repetition mistakes at each function declaration: 484 \begin{cfa} 485 trait `sumable`( otype T ) { 486 void `?{}`( T &, zero_t ); $\C{// 0 literal constructor}$ 487 T `?+?`( T, T ); $\C{// assortment of additions}$ 488 T ?+=?( T &, T ); 489 T ++?( T & ); 490 T ?++( T & ); 414 int i = sum(sa, 5); $\C{// use S's 0 construction and +}$ 415 \end{cfa} 416 417 Since writing constraints on types can become cumbersome for more constrained functions, \CFA also has the concept of traits. 418 Traits are named collection of constraints that can be used both instead and in addition to regular constraints: 419 \begin{cfa} 420 trait summable( otype T ) { 421 void ?{}(T *, zero_t); $\C{// constructor from 0 literal}$ 422 T ?+?(T, T); $\C{// assortment of additions}$ 423 T ?+=?(T *, T); 424 T ++?(T *); 425 T ?++(T *); 491 426 }; 492 forall( otype T `| sumable( T )` ) $\C{// use trait}$ 493 T sum( T a[$\,$], size_t size ); 494 \end{cfa} 495 496 Assertions can be @otype@ or @dtype@. 497 @otype@ refers to a ``complete'' object, \ie an object has a size, default constructor, copy constructor, destructor and an assignment operator. 498 @dtype@ only guarantees an object has a size and alignment. 499 500 Using the return type for discrimination, it is possible to write a type-safe @alloc@ based on the C @malloc@: 501 \begin{cfa} 502 forall( dtype T | sized(T) ) T * alloc( void ) { return (T *)malloc( sizeof(T) ); } 503 int * ip = alloc(); $\C{// select type and size from left-hand side}$ 504 double * dp = alloc(); 505 struct S {...} * sp = alloc(); 506 \end{cfa} 507 where the return type supplies the type/size of the allocation, which is impossible in most type systems. 508 509 510 \subsection{Constructors / Destructors} 511 512 Object lifetime is a challenge in non-managed programming languages. 513 \CFA responds with \CC-like constructors and destructors: 514 \begin{cfa} 515 struct VLA { int len, * data; }; $\C{// variable length array of integers}$ 516 void ?{}( VLA & vla ) with ( vla ) { len = 10; data = alloc( len ); } // default constructor 517 void ?{}( VLA & vla, int size, char fill ) with ( vla ) { len = size; data = alloc( len, fill ); } // initialization 518 void ?{}( VLA & vla, VLA other ) { vla.len = other.len; vla.data = other.data; } // copy, shallow 519 void ^?{}( VLA & vla ) with ( vla ) { free( data ); } $\C{// destructor}$ 520 { 521 VLA x, y = { 20, 0x01 }, z = y; $\C{// z points to y}$ 522 // ?{}( x ); ?{}( y, 20, 0x01 ); ?{}( z, y ); 523 ^x{}; $\C{// deallocate x}$ 524 x{}; $\C{// reallocate x}$ 525 z{ 5, 0xff }; $\C{// reallocate z, not pointing to y}$ 526 ^y{}; $\C{// deallocate y}$ 527 y{ x }; $\C{// reallocate y, points to x}$ 528 x{}; $\C{// reallocate x, not pointing to y}$ 529 // ^?{}(z); ^?{}(y); ^?{}(x); 530 } 531 \end{cfa} 532 Like \CC, construction is implicit on allocation (stack/heap) and destruction is implicit on deallocation. 533 The object and all their fields are constructed/destructed. 534 \CFA also provides @new@ and @delete@, which behave like @malloc@ and @free@, in addition to constructing and destructing objects: 535 \begin{cfa} 536 { struct S s = {10}; $\C{// allocation, call constructor}$ 537 ... 538 } $\C{// deallocation, call destructor}$ 539 struct S * s = new(); $\C{// allocation, call constructor}$ 540 ... 541 delete( s ); $\C{// deallocation, call destructor}$ 542 \end{cfa} 543 \CFA concurrency uses object lifetime as a means of synchronization and/or mutual exclusion. 544 545 427 forall( otype T | summable(T) ) $\C{// use trait}$ 428 T sum(T a[], size_t size); 429 \end{cfa} 430 431 Note that the type use for assertions can be either an @otype@ or a @dtype@. 432 Types declared as @otype@ refer to ``complete'' objects, \ie objects with a size, a default constructor, a copy constructor, a destructor and an assignment operator. 433 Using @dtype@, on the other hand, has none of these assumptions but is extremely restrictive, it only guarantees the object is addressable. 434 435 % ====================================================================== 436 \subsection{with Clause/Statement} 437 Since \CFA lacks the concept of a receiver, certain functions end up needing to repeat variable names often. 438 To remove this inconvenience, \CFA provides the @with@ statement, which opens an aggregate scope making its fields directly accessible (like Pascal). 439 \begin{cfa} 440 struct S { int i, j; }; 441 int mem(S & this) with (this) $\C{// with clause}$ 442 i = 1; $\C{// this->i}$ 443 j = 2; $\C{// this->j}$ 444 } 445 int foo() { 446 struct S1 { ... } s1; 447 struct S2 { ... } s2; 448 with (s1) $\C{// with statement}$ 449 { 450 // access fields of s1 without qualification 451 with (s2) $\C{// nesting}$ 452 { 453 // access fields of s1 and s2 without qualification 454 } 455 } 456 with (s1, s2) $\C{// scopes open in parallel}$ 457 { 458 // access fields of s1 and s2 without qualification 459 } 460 } 461 \end{cfa} 462 463 For more information on \CFA see \cite{cforall-ug,Schluntz17,www-cfa}. 464 465 % ====================================================================== 466 % ====================================================================== 546 467 \section{Concurrency Basics}\label{basics} 547 548 At its core, concurrency is based on multiple call-stacks and scheduling among threads executing on these stacks. 468 % ====================================================================== 469 % ====================================================================== 470 471 At its core, concurrency is based on having multiple call-stacks and scheduling among threads of execution executing on these stacks. 549 472 Multiple call stacks (or contexts) and a single thread of execution does \emph{not} imply concurrency. 550 473 Execution with a single thread and multiple stacks where the thread is deterministically self-scheduling across the stacks is called \newterm{coroutining}; … … 662 585 \begin{lstlisting}[aboveskip=0pt,belowskip=0pt] 663 586 `coroutine` Fib { int fn; }; 664 void ?{}( Fibonacci & fib ) with( fib ) { fn = 0; }665 587 void main( Fib & f ) with( f ) { 666 588 int f1, f2;
Note:
See TracChangeset
for help on using the changeset viewer.