Changeset fe84230 for doc/proposals/concurrency
- Timestamp:
- Nov 7, 2016, 10:47:32 AM (8 years ago)
- Branches:
- ADT, aaron-thesis, arm-eh, ast-experimental, cleanup-dtors, deferred_resn, demangler, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, new-env, no_list, persistent-indexer, pthread-emulation, qualifiedEnum, resolv-new, with_gc
- Children:
- 84118d8
- Parents:
- 9a8dfcc
- Location:
- doc/proposals/concurrency
- Files:
-
- 1 added
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/proposals/concurrency/concurrency.tex
r9a8dfcc rfe84230 1 1 % requires tex packages: texlive-base texlive-latex-base tex-common texlive-humanities texlive-latex-extra texlive-fonts-recommended 2 2 3 % inline code ©...©(copyright symbol) emacs: C-q M-)4 % red highlighting ®...®(registered trademark symbol) emacs: C-q M-.5 % blue highlighting ß...ß(sharp s symbol) emacs: C-q M-_6 % green highlighting ¢...¢(cent symbol) emacs: C-q M-"7 % LaTex escape §...§(section symbol) emacs: C-q M-'8 % keyword escape ¶...¶(pilcrow symbol) emacs: C-q M-^3 % inline code �...� (copyright symbol) emacs: C-q M-) 4 % red highlighting �...� (registered trademark symbol) emacs: C-q M-. 5 % blue highlighting �...� (sharp s symbol) emacs: C-q M-_ 6 % green highlighting �...� (cent symbol) emacs: C-q M-" 7 % LaTex escape �...� (section symbol) emacs: C-q M-' 8 % keyword escape �...� (pilcrow symbol) emacs: C-q M-^ 9 9 % math escape $...$ (dollar symbol) 10 10 … … 35 35 \usepackage{fancyhdr} 36 36 \renewcommand{\linenumberfont}{\scriptsize\sffamily} 37 \input{ common} % bespoke macros used in the document37 \input{style} % bespoke macros used in the document 38 38 \usepackage[dvips,plainpages=false,pdfpagelabels,pdfpagemode=UseNone,colorlinks=true,pagebackref=true,linkcolor=blue,citecolor=blue,urlcolor=blue,pagebackref=true,breaklinks=true]{hyperref} 39 39 \usepackage{breakurl} … … 153 153 void ?{}(counter_t & nomutex this); 154 154 size_t ++?(counter_t & mutex this); 155 void ?{}(size_t * this, counter_t & mutex cnt); //need for mutex is platform dependent here 155 156 //need for mutex is platform dependent here 157 void ?{}(size_t * this, counter_t & mutex cnt); 156 158 \end{lstlisting} 157 159 *semantics of the declaration of \code{mutex struct counter_t} are discussed in details in section \ref{data} … … 195 197 } 196 198 197 void ?{}(int * this, counter_t & mutex cnt) { //need for mutex is platform dependent here 199 //need for mutex is platform dependent here 200 void ?{}(int * this, counter_t & mutex cnt) { 198 201 *this = (int)cnt; 199 202 } … … 224 227 \end{lstlisting} 225 228 226 This code acquires both locks before entering the critical section . In practice, writing multi-locking routines that can not lead to deadlocks can be tricky. Having language support for such a feature is therefore a significant asset for \CFA. In the case presented above, \CFA guarantees that the order of aquisition will be consistent across calls to routines using the same monitors as arguments. However, since \CFA monitors use multi-acquiring locks users can effectively force the acquiring order. For example, notice which routines use \code{mutex}/\code{nomutex} and how this affects aquiring order :229 This code acquires both locks before entering the critical section (Referenced as \gls{group-acquire} from now on). In practice, writing multi-locking routines that can not lead to deadlocks can be tricky. Having language support for such a feature is therefore a significant asset for \CFA. In the case presented above, \CFA guarantees that the order of aquisition will be consistent across calls to routines using the same monitors as arguments. However, since \CFA monitors use multi-acquiring locks users can effectively force the acquiring order. For example, notice which routines use \code{mutex}/\code{nomutex} and how this affects aquiring order : 227 230 \begin{lstlisting} 228 231 void foo(A & mutex a, B & mutex a) { … … 282 285 283 286 \subsection{Internal scheduling} \label{insched} 284 Monitors should also be able to schedule what threads access it as a mean of synchronization. Internal scheduling is one of the simple examples of such a feature. It allows users to declare condition variables and wait for them to be signaled. Here is a simple example of such a technique :287 Monitors also need to schedule waiting threads within it as a mean of synchronization. Internal scheduling is one of the simple examples of such a feature. It allows users to declare condition variables and have threads wait and signaled from them. Here is a simple example of such a technique : 285 288 286 289 \begin{lstlisting} … … 300 303 \end{lstlisting} 301 304 302 Here routine \code{foo} waits on the \code{signal} from \code{bar} before making further progress, effectively ensuring a basic ordering. This semantic can easily be extended to multi-monitor calls by offering the same guarantee. 303 305 Here routine \code{foo} waits for the \code{signal} from \code{bar} before making further progress, effectively ensuring a basic ordering. This semantic can easily be extended to multi-monitor calls by offering the same guarantee. 304 306 \begin{center} 305 307 \begin{tabular}{ c @{\hskip 0.65in} c } … … 307 309 \begin{lstlisting} 308 310 void foo(monitor & mutex a, 309 monitor & mutex b) {311 monitor & mutex b) { 310 312 //... 311 313 wait(a.e); … … 316 318 \end{lstlisting} &\begin{lstlisting} 317 319 void bar(monitor & mutex a, 318 monitor & mutex b) {320 monitor & mutex b) { 319 321 signal(a.e); 320 322 } … … 326 328 \end{tabular} 327 329 \end{center} 328 329 A direct extension of the single monitor semantics would be to release all locks when waiting and transferring ownership of all locks when signalling. However, for the purpose of synchronization it may be usefull to only release some of the locks but keep others. On the technical side, partially releasing lock is feasible but from the user perspective a choice must be made for the syntax of this feature. It is possible to do without any extra syntax by relying on order of acquisition (Note that here the use of helper routines is irrelevant, only routines the acquire mutual exclusion have an impact on internal scheduling): 330 A direct extension of the single monitor semantics is to release all locks when waiting and transferring ownership of all locks when signalling. However, for the purpose of synchronization it may be usefull to only release some of the locks but keep others. It is possible to support internal scheduling and \gls{group-acquire} without any extra syntax by relying on order of acquisition. Here is an example of the different contexts in which internal scheduling can be used. (Note that here the use of helper routines is irrelevant, only routines acquire mutual exclusion have an impact on internal scheduling): 330 331 331 332 \begin{center} … … 337 338 338 339 void foo(monitor & mutex a, 339 monitor & mutex b) { 340 monitor & mutex b) { 341 340 342 wait(e); 341 343 } … … 351 353 352 354 void bar(monitor & mutex a, 353 monitor & nomutex b) {355 monitor & nomutex b) { 354 356 foo(a,b); 355 357 } 356 358 357 359 void foo(monitor & mutex a, 358 monitor & mutex b) {360 monitor & mutex b) { 359 361 wait(e); 360 362 } … … 365 367 366 368 void bar(monitor & mutex a, 367 monitor & nomutex b) {368 foo(a,b);369 monitor & nomutex b) { 370 baz(a,b); 369 371 } 370 372 371 373 void baz(monitor & nomutex a, 372 monitor & mutex b) {374 monitor & mutex b) { 373 375 wait(e); 374 376 } … … 379 381 \end{center} 380 382 381 This can be interpreted in two different ways : 382 \begin{flushleft} 383 \begin{enumerate} 384 \item \code{wait} atomically releases the monitors acquired by the inner-most routine, \underline{ignoring} nested calls. 385 \item \code{wait} atomically releases the monitors acquired by the inner-most routine, \underline{considering} nested calls. 386 \end{enumerate} 387 \end{flushleft} 388 While the difference between these two is subtle, it has a significant impact. In the first case it means that the calls to \code{foo} would behave the same in Context 1 and 2. This semantic would also mean that the call to \code{wait} in routine \code{baz} would only release \code{monitor b}. While this may seem intuitive with these examples, it does have one significant implication, it creates a strong distinction between acquiring multiple monitors in sequence and acquiring the same monitors simulatenously, i.e. : 383 Note that in \CFA, \code{condition} have no particular need to be stored inside a monitor, beyond any software engineering reasons. Context 1 is the simplest way of acquiring more than one monitor (\gls{group-acquire}), using a routine wiht multiple parameters having the \code{mutex} keyword. Context 2 also uses \gls{group-acquire} as well in routine \code{foo}. However, the routine is called by routine \code{bar} which only acquires monitor \code{a}. Since monitors can be acquired multiple times this will not cause a deadlock by itself but it does force the acquiring order to \code{a} then \code{b}. Context 3 also forces the acquiring order to be \code{a} then \code{b} but does not use \gls{group-acquire}. The previous example tries to illustrate the semantics that must be established to support releasing monitors in a \code{wait} statement. In all cases the behavior of the wait statment is to release all the locks that were acquired my the inner-most monitor call. That is \code{a & b} in context 1 and 2 and \code{b} only in context 3. Here are a few other examples of this behavior. 384 389 385 390 386 \begin{center} 391 \begin{tabular}{c @{\hskip 0.35in} c @{\hskip 0.35in} c} 392 \begin{lstlisting} 393 enterMonitor(a); 394 enterMonitor(b); 395 // do stuff 396 leaveMonitor(b); 397 leaveMonitor(a); 398 \end{lstlisting} & != &\begin{lstlisting} 399 enterMonitor(a); 400 enterMonitor(a, b); 401 // do stuff 402 leaveMonitor(a, b); 403 leaveMonitor(a); 387 \begin{tabular}{|c|c|c|} 388 \begin{lstlisting} 389 condition e; 390 391 //acquire a 392 void foo(monitor & nomutex a, 393 monitor & mutex b) { 394 bar(a,b); 395 } 396 397 //acquire a 398 void bar(monitor & mutex a, 399 monitor & nomutex b) { 400 401 //release a 402 //keep b 403 wait(e); 404 } 405 406 foo(a, b); 407 \end{lstlisting} &\begin{lstlisting} 408 condition e; 409 410 //acquire a & b 411 void foo(monitor & mutex a, 412 monitor & mutex b) { 413 bar(a,b); 414 } 415 416 //acquire b 417 void bar(monitor & mutex a, 418 monitor & nomutex b) { 419 420 //release b 421 //keep a 422 wait(e); 423 } 424 425 foo(a, b); 426 \end{lstlisting} &\begin{lstlisting} 427 condition e; 428 429 //acquire a & b 430 void foo(monitor & mutex a, 431 monitor & mutex b) { 432 bar(a,b); 433 } 434 435 //acquire none 436 void bar(monitor & nomutex a, 437 monitor & nomutex b) { 438 439 //release a & b 440 //keep none 441 wait(e); 442 } 443 444 foo(a, b); 404 445 \end{lstlisting} 405 446 \end{tabular} 406 447 \end{center} 407 408 This is not intuitive because even if both methods display the same monitors state both inside and outside the critical section respectively, the behavior is different. Furthermore, the actual acquiring order will be exaclty the same since acquiring a monitor from inside its mutual exclusion is a no-op. This means that even if the data and the actual control flow are the same using both methods, the behavior of the \code{wait} will be different. The alternative is option 2, that is releasing acquired monitors, \underline{considering} nesting. This solves the issue of having the two acquiring method differ at the cost of making routine \code{foo} behave differently depending on from which context it is called (Context 1 or 2). Indeed in Context 2, routine \code{foo} actually behaves like routine \code{baz} rather than having the same behavior than in Context 1. The fact that both implicit approaches can be unintuitive depending on the perspective may be a sign that the explicit approach is superior. For this reason this \CFA does not support implicit monitor releasing and uses explicit semantics. 409 \\ 410 411 The following examples shows three alternatives of explicit wait semantics : 412 \\ 413 414 \begin{center} 415 \begin{tabular}{|c|c|c|} 416 Case 1 & Case 2 & Case 3 \\ 417 Branding on construction & Explicit release list & Explicit ignore list \\ 418 \hline 419 \begin{lstlisting} 420 void foo(monitor & mutex a, 421 monitor & mutex b, 422 condition & c) 423 { 424 // Releases monitors 425 // branded in ctor 426 wait(c); 427 } 428 429 monitor a; 430 monitor b; 431 condition1 c1 = {a}; 432 condition2 c2 = {a, b}; 433 434 //Will release only a 435 foo(a,b,c1); 436 437 //Will release a and b 438 foo(a,b,c2); 439 \end{lstlisting} &\begin{lstlisting} 440 void foo(monitor & mutex a, 441 monitor & mutex b, 442 condition & c) 443 { 444 // Releases monitor a 445 // Holds monitor b 446 waitRelease(c, [a]); 447 } 448 449 monitor a; 450 monitor b; 451 condition c; 452 453 454 455 foo(a,b,c); 456 457 458 459 \end{lstlisting} &\begin{lstlisting} 460 void foo(monitor & mutex a, 461 monitor & mutex b, 462 condition & c) 463 { 464 // Releases monitor a 465 // Holds monitor b 466 waitHold(c, [b]); 467 } 468 469 monitor a; 470 monitor b; 471 condition c; 472 473 474 475 foo(a,b,c); 476 477 478 479 \end{lstlisting} 480 \end{tabular} 481 \end{center} 482 (Note : Case 2 and 3 use tuple semantics to pass a variable length list of elements.) 483 \\ 484 485 All these cases have their pros and cons. Case 1 is more distinct because it means programmers need to be carefull about where the condition is initialized as well as where it is used. On the other hand, it is very clear and explicitly states which monitor is released and which monitor stays acquired. This is similar to Case 2, which releases only the monitors explictly listed. However, in Case 2, calling the \code{wait} routine instead of the \code{waitRelease} routine releases all the acquired monitor. The Case 3 is an improvement on that since it releases all the monitors except those specified. The result is that the \code{wait} routine can be written as follows : 486 \begin{lstlisting} 487 void wait(condition & cond) { 488 waitHold(cond, []); 489 } 490 \end{lstlisting} 491 This alternative offers nice and consistent behavior between \code{wait} and \code{waitHold}. However, one large pitfall is that mutual exclusion can now be violated by calls to library code. Indeed, even if the following example seems benign there is one significant problem : 492 \begin{lstlisting} 493 monitor global; 494 495 extern void doStuff(); //uses global 496 497 void foo(monitor & mutex m) { 498 //... 499 doStuff(); //warning can release monitor m 500 //... 501 } 502 503 foo(global); 504 \end{lstlisting} 505 506 Indeed, if Case 2 or 3 are chosen it any code can violate the mutual exclusion of the calling code by issuing calls to \code{wait} or \code{waitHold} in a nested monitor context. Case 2 can be salvaged by removing the \code{wait} routine from the API but Case 3 cannot prevent users from calling \code{waitHold(someCondition, [])}. For this reason the syntax proposed in Case 3 is rejected. Note that the syntax proposed in case 1 and 2 are not exclusive. Indeed, by supporting two types of condition both cases can be supported : 507 \begin{lstlisting} 508 struct condition { /*...*/ }; 509 510 // Second argument is a variable length tuple. 511 void wait(condition & cond, [...] monitorsToRelease); 512 void signal(condition & cond); 513 514 struct conditionN { /*...*/ }; 515 516 void ?{}(conditionN* this, /*list of N monitors to release*/); 517 void wait(conditionN & cond); 518 void signal(conditionN & cond); 519 \end{lstlisting} 520 521 Regardless of the option chosen for wait semantics, signal must be symmetrical. In all cases, signal only needs a single parameter, the condition variable that needs to be signalled. But \code{signal} needs to be called from the same monitor(s) that call to \code{wait}. Otherwise, mutual exclusion cannot be properly transferred back to the waiting monitor. 522 523 Finally, an additionnal semantic which can be very usefull is the \code{signalBlock} routine. This routine behaves like signal for all of the semantics discussed above, but with the subtelty that mutual exclusion is transferred to the waiting task immediately rather than wating for the end of the critical section. 448 Note the right-most example which uses a helper routine and therefore is not relevant to find which monitors will be released. 449 450 These semantics imply that in order to release of subset of the monitors currently held, users must write (and name) a routine that only acquires the desired subset and simply calls wait. While users can use this method, \CFA offers the \code{wait_release}\footnote{Not sure if an overload of \code{wait} would work...} which will release only the specified monitors. 451 452 Regardless of the context in which the \code{wait} statement is used, \code{signal} must used holding the same set of monitors. In all cases, signal only needs a single parameter, the condition variable that needs to be signalled. But \code{signal} needs to be called from the same monitor(s) that call to \code{wait}. Otherwise, mutual exclusion cannot be properly transferred back to the waiting monitor. 453 454 Finally, an additional semantic which can be very usefull is the \code{signal_block} routine. This routine behaves like signal for all of the semantics discussed above, but with the subtelty that mutual exclusion is transferred to the waiting task immediately rather than wating for the end of the critical section. 524 455 \\ 525 456 … … 531 462 % # # # # ### # # # # # # # # # 532 463 % ####### # # # ### ##### ##### # # ####### ###### 533 464 \newpage 534 465 \subsection{External scheduling} \label{extsched} 535 466 As one might expect, the alternative to Internal scheduling is to use External scheduling instead. This method is somewhat more robust to deadlocks since one of the threads keeps a relatively tight control on scheduling. Indeed, as the following examples will demonstrate, external scheduling allows users to wait for events from other threads without the concern of unrelated events occuring. External scheduling can generally be done either in terms of control flow (ex: \uC) or in terms of data (ex: Go). Of course, both of these paradigms have their own strenghts and weaknesses but for this project control flow semantics where chosen to stay consistent with the rest of the languages semantics. Two challenges specific to \CFA arise when trying to add external scheduling with loose object definitions and multi-monitor routines. The following example shows what a simple use \code{accept} versus \code{wait}/\code{signal} and its advantages. -
doc/proposals/concurrency/glossary.tex
r9a8dfcc rfe84230 11 11 { 12 12 Locking done by the called routine. With this technique, a monitor routine called by another routine will aquire the monitor \emph{after} entering the routine body but prior to any other code. 13 } 14 15 \longnewglossaryentry{group-acquire} 16 {name={grouped acquiring}} 17 { 18 Implicitly acquiring several monitors when entering a monitor. 13 19 } 14 20 -
doc/proposals/concurrency/version
r9a8dfcc rfe84230 1 0.5.1 161 0.5.146
Note: See TracChangeset
for help on using the changeset viewer.