|
316 | 316 | "Asynchronous I/O",
|
317 | 317 | "Felix is a coroutine",
|
318 | 318 | "Running subsystems",
|
319 |
| -"The concept of fibration" |
| 319 | +"The concept of fibration", |
| 320 | +"Operations", |
| 321 | +"write", |
| 322 | +"read", |
| 323 | +"suicide", |
| 324 | +"spawn", |
| 325 | +"Scheduler operation", |
| 326 | +"Communication Sequential Coroutines", |
| 327 | +"No Deadlocks" |
320 | 328 | ];
|
321 | 329 | function expand_all(dummy)
|
322 | 330 | {
|
|
394 | 402 | </div>
|
395 | 403 | <div class=m1 onclick="mshow('menu4','#The concept of fibration_h')"> <a href="#The_concept_of_fibration_h">The concept of fibration</a></div>
|
396 | 404 | <div class=sm id=menu4>
|
| 405 | + <div class=m2><a href="#Operations_h">Operations</a></div> |
| 406 | + <div class=m2><a href="#Communication_Sequential_Coroutines_h">Communication Sequential Coroutines</a></div> |
397 | 407 | </div>
|
398 |
| - <script>counter_max=4;</script> |
| 408 | + <div class=m1 onclick="mshow('menu5','#No Deadlocks_h')"> <a href="#No_Deadlocks_h">No Deadlocks</a></div> |
| 409 | + <div class=sm id=menu5> |
| 410 | + </div> |
| 411 | + <script>counter_max=5;</script> |
399 | 412 | </div>
|
400 | 413 | <!--End Left Margin Toc-->
|
401 | 414 |
|
@@ -681,40 +694,78 @@ <h1 id='A_low_level_example._h'><img src='/share/src/web/images/minus.gif' id='A
|
681 | 694 | the fibre is not technically dead because it no longer exists.
|
682 | 695 | </p><p>A <em>channel</em> is an object associated with which
|
683 | 696 | set of fibres all waiting to read or all waiting to write.
|
684 |
| -</p><p>When a write operation is performed on a channel by the running fibre, |
| 697 | +</p><h2 id='Operations_h'><img src='/share/src/web/images/minus.gif' id='Operations' onclick='toggle(this,"Operations_d")' alt='+'/> 4.1 Operations</h2><div id='Operations_d' style='display:block'> |
| 698 | +<h3 id='write_h'><img src='/share/src/web/images/minus.gif' id='write' onclick='toggle(this,"write_d")' alt='+'/> 4.1.1 write</h3><div id='write_d' style='display:block'> |
| 699 | +<p>When a write operation is performed on a channel by the running fibre, |
685 | 700 | if there are fibres on the channel which are waiting to read
|
686 | 701 | then one is selected, the data to be written is tranfered from the writer to the reader,
|
687 | 702 | the fibre is removed from association with the channel,
|
688 | 703 | and both the running and reader fibre are made active.
|
689 | 704 | Otherwise if there is no readers waiting on the channel, the writer is
|
690 | 705 | suspended and put on the channel.
|
691 |
| -</p><p>When a read operation is performed on a channel by the running fibre, |
| 706 | +</p></div><h3 id='read_h'><img src='/share/src/web/images/minus.gif' id='read' onclick='toggle(this,"read_d")' alt='+'/> 4.1.2 read</h3><div id='read_d' style='display:block'> |
| 707 | +<p>When a read operation is performed on a channel by the running fibre, |
692 | 708 | if there are fibres on the channel which are waiting to write
|
693 | 709 | then one is selected, the data to be written is transfered from the writer to the reader,
|
694 | 710 | fibre, the fibre is removed from association with the channel,
|
695 | 711 | and both the running and reader fibre are made active.
|
696 | 712 | Otherwise if there is no writers waiting on the channel, the writer is
|
697 | 713 | suspended and put on the channel.
|
698 |
| -</p><p>When a fibre commits suicide, it is removed as the running fibre. |
699 |
| -</p><p>When a new fibre is spawned, both the spawner and spawnee are made active. |
700 |
| -</p><p>When there is no running fibre, then if an active fibre exists, |
| 714 | +</p></div><h3 id='suicide_h'><img src='/share/src/web/images/minus.gif' id='suicide' onclick='toggle(this,"suicide_d")' alt='+'/> 4.1.3 suicide</h3><div id='suicide_d' style='display:block'> |
| 715 | +<p>When a fibre commits suicide, it is removed as the running fibre. |
| 716 | +</p></div><h3 id='spawn_h'><img src='/share/src/web/images/minus.gif' id='spawn' onclick='toggle(this,"spawn_d")' alt='+'/> 4.1.4 spawn</h3><div id='spawn_d' style='display:block'> |
| 717 | +<p>When a new fibre is spawned, both the spawner and spawnee are made active. |
| 718 | +</p></div><h3 id='Scheduler_operation_h'><img src='/share/src/web/images/minus.gif' id='Scheduler operation' onclick='toggle(this,"Scheduler_operation_d")' alt='+'/> 4.1.5 Scheduler operation</h3><div id='Scheduler_operation_d' style='display:block'> |
| 719 | +<p>When there is no running fibre, then if an active fibre exists, |
701 | 720 | an active fibre is selected and promoted to running state
|
702 | 721 | and the thread of control begins executing it.
|
703 | 722 | </p><p>When there is no running fibre, and no active fibres,
|
704 | 723 | the whole system terminates. Any fibres waiting on I/O become dead
|
705 |
| -</p><p>The description above is a complete account of the abstract semantics |
| 724 | +</p></div></div><h2 id='Communication_Sequential_Coroutines_h'><img src='/share/src/web/images/minus.gif' id='Communication Sequential Coroutines' onclick='toggle(this,"Communication_Sequential_Coroutines_d")' alt='+'/> 4.2 Communication Sequential Coroutines</h2><div id='Communication_Sequential_Coroutines_d' style='display:block'> |
| 725 | +<p>The description above is a complete account of the abstract semantics |
706 | 726 | known as <em>Communicating Sequential Coroutines</em> or CSC, it is a
|
707 | 727 | sub system obtain from Tony Hoare's <em>Communicating Sequenmtial Processes</em>
|
708 | 728 | or CSP, in which concurrency is replaced by an indeterminate total ordering.
|
709 | 729 | </p><p>To be precise in CSC all events are totally ordered, however the exact ordering
|
710 | 730 | is not determinate. This is because a scheduler running the system is free
|
711 |
| -to pick any active fibre to run when one is needed. |
| 731 | +to pick any active fibre to run when one is needed; similarly |
| 732 | +a read or write operation can match with any dual operation on a channel. |
712 | 733 | </p><p>In the Felix implementation, a spawned fibre runs immediately
|
713 | 734 | if the <code>spawn_fthread</code> procedure is used to launch it, or the
|
714 | 735 | running fibre continues instead, of <code>schedule_fthread</code> is used instead.
|
715 | 736 | </p><p>In addition, I/O transfers always result in the reader proceeding,
|
716 | 737 | to give it a chance to actually fetch the data before the write
|
717 | 738 | might modify it.
|
| 739 | +</p></div></div><h1 id='No_Deadlocks_h'><img src='/share/src/web/images/minus.gif' id='No Deadlocks' onclick='toggle(this,"No_Deadlocks_d")' alt='+'/> 5 No Deadlocks</h1><div id='No_Deadlocks_d' style='display:block'> |
| 740 | +<p>Most threading systems can deadlock. Here is the classical situation: |
| 741 | +</p><pre class='inclusion'> |
| 742 | +examples/ex5.flx</pre> |
| 743 | +<p><pre class='flxbg'><span class="lineno" id=line1></span> <span class="big_keyword" title="defines a coroutine using chip idiom">chip</span> D <span class="small_keyword" title="the parameter of a chip">connector</span> io <span class="small_keyword" title="a field of the chip parameter">pin</span> inp: %<<span class="library" title="binding of C int type">int</span> <span class="small_keyword" title="a field of the chip parameter">pin</span> out: %><span class="library" title="binding of C int type">int</span> |
| 744 | +<span class="lineno" id=line2></span> { |
| 745 | +<span class="lineno" id=line3></span> <span class="small_keyword" title="while loop">while</span> <span class="library" title="truth value">true</span> <span class="small_keyword" title="imperative code begins">do</span> |
| 746 | +<span class="lineno" id=line4></span> <span class="big_keyword" title="Define a mutable variable">var</span> x = read io.inp; |
| 747 | +<span class="lineno" id=line5></span> <span class="library" title="Print a string to a stream">write</span> (io.out, x); |
| 748 | +<span class="lineno" id=line6></span> <span class="small_keyword" title="end of body">done</span> |
| 749 | +<span class="lineno" id=line7></span> } |
| 750 | +<span class="lineno" id=line8></span> <span class="small_keyword" title="a synonym for var to suit the chip idiom">device</span> A = D; |
| 751 | +<span class="lineno" id=line9></span> <span class="small_keyword" title="a synonym for var to suit the chip idiom">device</span> B = D; |
| 752 | +<span class="lineno" id=line10></span> <span class="small_keyword" title="defines the topology of chip connections">circuit</span> |
| 753 | +<span class="lineno" id=line11></span> <span class="small_keyword" title="connects two pins with a channel">connect</span> A.out, B.inp |
| 754 | +<span class="lineno" id=line12></span> <span class="small_keyword" title="connects two pins with a channel">connect</span> B.out, A.inp |
| 755 | +<span class="lineno" id=line13></span> <span class="small_keyword" title="defines the topology of chip connections">endcircuit</span> |
| 756 | +<span class="lineno" id=line14></span> <span class="library" title="Print a string to standard output with newline appended">println</span>$ <span class="fstring">"Done"</span>; |
| 757 | +</pre></p><p>Both devices start by reading, so there's nothing to read because |
| 758 | +neither can proceed to writing. So the system hangs, right! |
| 759 | +</p><p>Er .. NO! Both chips are suspended, the mainline completes, |
| 760 | +and the scheduler terminates the program because there is no work to do. |
| 761 | +</p><p>Let me repeat in bold letters: |
| 762 | +</p><p><bf style="color:red">FELIX COROUTINES CANNOT DEADLOCK</bf> |
| 763 | +</p><p>However they can <em>livelock</em>, but only if you mismanage |
| 764 | +manual circuit construction. Here's an example: |
| 765 | +</p><p><pre class="prefmtbg">var inp,out = mk_ioschannel_pair[int](); |
| 766 | +spawn_fthread { write (out, 42); }; |
| 767 | +</pre></p><p>This is a livelock because the mainline <em>could</em> read |
| 768 | +the written data on the channel endpoint <code>inp</code> but doesn't. |
718 | 769 | </p></div><!--Main Content Body End-->
|
719 | 770 |
|
720 | 771 | </div> <!-- rightpanel contents end -->
|
|
0 commit comments