<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Mu - 073wait.cc</title>
<meta name="Generator" content="Vim/7.4">
<meta name="plugin-version" content="vim7.4_v2">
<meta name="syntax" content="cpp">
<meta name="settings" content="use_css,pre_wrap,no_foldcolumn,expand_tabs,prevent_copy=">
<meta name="colorscheme" content="minimal">
<style type="text/css">
<!--
pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; }
body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color: #080808; }
* { font-size: 12pt; font-size: 1em; }
.Constant { color: #00a0a0; }
.cSpecial { color: #008000; }
.muRecipe { color: #ff8700; }
.SalientComment { color: #00ffff; }
.Comment { color: #9090ff; }
.Delimiter { color: #800080; }
.Special { color: #c00000; }
.traceContains { color: #008000; }
.Normal { color: #eeeeee; background-color: #080808; padding-bottom: 1px; }
.muData { color: #ffff00; }
.Identifier { color: #ecf32c; }
-->
</style>
<script type='text/javascript'>
<!--
-->
</script>
</head>
<body>
<pre id='vimCodeElement'>
<span class="Comment">//: Routines can be put in a 'waiting' state, from which it will be ready to</span>
<span class="Comment">//: run again when a specific memory location changes its value. This is mu's</span>
<span class="Comment">//: basic technique for orchestrating the order in which different routines</span>
<span class="Comment">//: operate.</span>
<span class="Delimiter">:(scenario wait_for_location)</span>
<span class="muRecipe">def</span> f1 [
<span class="Constant">10</span>:num<span class="Special"> <- </span>copy <span class="Constant">34</span>
start-running f2
<span class="Constant">20</span>:location<span class="Special"> <- </span>copy <span class="Constant">10</span>/unsafe
wait-<span class="Normal">for</span>-reset-then-set <span class="Constant">20</span>:location
<span class="Comment"># wait for f2 to run and reset location 1</span>
<span class="Constant">30</span>:num<span class="Special"> <- </span>copy <span class="Constant">10</span>:num
]
<span class="muRecipe">def</span> f2 [
<span class="Constant">10</span>:location<span class="Special"> <- </span>copy <span class="Constant">0</span>/unsafe
]
<span class="traceContains">+schedule: f1</span>
<span class="traceContains">+run: waiting for location 10 to reset</span>
<span class="traceContains">+schedule: f2</span>
<span class="traceContains">+schedule: waking up routine 1</span>
<span class="traceContains">+schedule: f1</span>
<span class="traceContains">+mem: storing 1 in location 30</span>
<span class="Comment">//: define the new state that all routines can be in</span>
<span class="Delimiter">:(before "End routine States")</span>
WAITING<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End routine Fields")</span>
<span class="Comment">// only if state == WAITING</span>
<span class="Normal">int</span> waiting_on_location<span class="Delimiter">;</span>
<span class="Delimiter">:(before "End routine Constructor")</span>
waiting_on_location = <span class="Constant">0</span><span class="Delimiter">;</span>
<span class="Delimiter">:(before "End Mu Test Teardown")</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Passed && any_routines_waiting<span class="Delimiter">())</span>
raise << Current_scenario<span class="Delimiter">-></span>name << <span class="Constant">": deadlock!</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Delimiter">:(before "End Run Routine")</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>any_routines_waiting<span class="Delimiter">())</span> <span class="Delimiter">{</span>
raise << <span class="Constant">"deadlock!</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
dump_waiting_routines<span class="Delimiter">();</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Test Teardown")</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Passed && any_routines_with_error<span class="Delimiter">())</span>
raise << <span class="Constant">"some routines died with errors</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Delimiter">:(code)</span>
<span class="Normal">bool</span> any_routines_waiting<span class="Delimiter">()</span> <span class="Delimiter">{</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < SIZE<span class="Delimiter">(</span>Routines<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>state == WAITING<span class="Delimiter">)</span>
<span class="Identifier">return</span> <span class="Constant">true</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">void</span> dump_waiting_routines<span class="Delimiter">()</span> <span class="Delimiter">{</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < SIZE<span class="Delimiter">(</span>Routines<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>state == WAITING<span class="Delimiter">)</span>
cerr << i << <span class="Constant">": "</span> << routine_label<span class="Delimiter">(</span>Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">))</span> << <span class="cSpecial">'\n'</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(scenario wait_for_location_can_deadlock)</span>
<span class="Special">% Hide_errors = true;</span>
<span class="muRecipe">def</span> main [
<span class="Constant">10</span>:num<span class="Special"> <- </span>copy <span class="Constant">1</span>
<span class="Constant">20</span>:location<span class="Special"> <- </span>copy <span class="Constant">10</span>/unsafe
wait-<span class="Normal">for</span>-reset-then-set <span class="Constant">20</span>:location
]
<span class="traceContains">+error: deadlock!</span>
<span class="Comment">//: Primitive recipe to put routines in that state.</span>
<span class="Comment">//: This primitive is also known elsewhere as compare-and-set (CAS). Used to</span>
<span class="Comment">//: build locks.</span>
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
WAIT_FOR_RESET_THEN_SET<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"wait-for-reset-then-set"</span><span class="Delimiter">,</span> WAIT_FOR_RESET_THEN_SET<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> WAIT_FOR_RESET_THEN_SET: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> != <span class="Constant">1</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'wait-for-reset-then-set' requires exactly one ingredient, but got '"</span> << inst<span class="Delimiter">.</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!is_mu_location<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'wait-for-reset-then-set' requires a location ingredient, but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Delimiter">}</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> WAIT_FOR_RESET_THEN_SET: <span class="Delimiter">{</span>
<span class="Normal">int</span> loc = <span class="Normal">static_cast</span><<span class="Normal">int</span>><span class="Delimiter">(</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
trace<span class="Delimiter">(</span><span class="Constant">9998</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"wait: *"</span> << loc << <span class="Constant">" = "</span> << get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">)</span> << end<span class="Delimiter">();</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">)</span> == <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
trace<span class="Delimiter">(</span><span class="Constant">9998</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"location "</span> << loc << <span class="Constant">" is already 0; setting"</span> << end<span class="Delimiter">();</span>
put<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">,</span> <span class="Constant">1</span><span class="Delimiter">);</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
trace<span class="Delimiter">(</span><span class="Constant">9998</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"waiting for location "</span> << loc << <span class="Constant">" to reset"</span> << end<span class="Delimiter">();</span>
Current_routine<span class="Delimiter">-></span>state = WAITING<span class="Delimiter">;</span>
Current_routine<span class="Delimiter">-></span>waiting_on_location = loc<span class="Delimiter">;</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Comment">//: Counterpart to unlock a lock.</span>
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
RESET<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"reset"</span><span class="Delimiter">,</span> RESET<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> RESET: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> != <span class="Constant">1</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'reset' requires exactly one ingredient, but got '"</span> << inst<span class="Delimiter">.</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!is_mu_location<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'reset' requires a location ingredient, but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Delimiter">}</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> RESET: <span class="Delimiter">{</span>
<span class="Normal">int</span> loc = <span class="Normal">static_cast</span><<span class="Normal">int</span>><span class="Delimiter">(</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
put<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">,</span> <span class="Constant">0</span><span class="Delimiter">);</span>
trace<span class="Delimiter">(</span><span class="Constant">9998</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"reset: *"</span> << loc << <span class="Constant">" = "</span> << get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">)</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Comment">//: scheduler tweak to get routines out of that state</span>
<span class="Delimiter">:(before "End Scheduler State Transitions")</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < SIZE<span class="Delimiter">(</span>Routines<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>state != WAITING<span class="Delimiter">)</span> <span class="Identifier">continue</span><span class="Delimiter">;</span>
<span class="Normal">int</span> loc = Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>waiting_on_location<span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>loc && get_or_insert<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">)</span> == <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">"schedule"</span><span class="Delimiter">)</span> << <span class="Constant">"waking up routine "</span> << Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>id << end<span class="Delimiter">();</span>
put<span class="Delimiter">(</span>Memory<span class="Delimiter">,</span> loc<span class="Delimiter">,</span> <span class="Constant">1</span><span class="Delimiter">);</span>
Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>state = RUNNING<span class="Delimiter">;</span>
Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>waiting_on_location = <span class="Constant">0</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Comment">//: Primitive to help compute locations to wait on.</span>
<span class="Comment">//: Only supports elements immediately inside containers; no arrays or</span>
<span class="Comment">//: containers within containers yet.</span>
<span class="Delimiter">:(scenario get_location)</span>
<span class="muRecipe">def</span> main [
<span class="Constant">12</span>:num<span class="Special"> <- </span>copy <span class="Constant">34</span>
<span class="Constant">13</span>:num<span class="Special"> <- </span>copy <span class="Constant">35</span>
<span class="Constant">15</span>:location<span class="Special"> <- </span>get-location <span class="Constant">12</span>:point<span class="Delimiter">,</span> <span class="Constant">1:offset</span>
]
<span class="traceContains">+mem: storing 13 in location 15</span>
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
GET_LOCATION<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"get-location"</span><span class="Delimiter">,</span> GET_LOCATION<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> GET_LOCATION: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> != <span class="Constant">2</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'get-location' expects exactly 2 ingredients in '"</span> << inst<span class="Delimiter">.</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
reagent<span class="Comment">/*</span><span class="Comment">copy</span><span class="Comment">*/</span> base = inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!canonize_type<span class="Delimiter">(</span>base<span class="Delimiter">))</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!base<span class="Delimiter">.</span>type<span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"first ingredient of 'get-location' should be a container, but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">const</span> type_tree* base_root_type = base<span class="Delimiter">.</span>type<span class="Delimiter">-></span>atom ? base<span class="Delimiter">.</span>type : base<span class="Delimiter">.</span>type<span class="Delimiter">-></span>left<span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!base_root_type<span class="Delimiter">-></span>atom || base_root_type<span class="Delimiter">-></span>value == <span class="Constant">0</span> || !contains_key<span class="Delimiter">(</span>Type<span class="Delimiter">,</span> base_root_type<span class="Delimiter">-></span>value<span class="Delimiter">)</span> || get<span class="Delimiter">(</span>Type<span class="Delimiter">,</span> base_root_type<span class="Delimiter">-></span>value<span class="Delimiter">).</span>kind != CONTAINER<span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"first ingredient of 'get-location' should be a container, but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
type_ordinal base_type = base<span class="Delimiter">.</span>type<span class="Delimiter">-></span>value<span class="Delimiter">;</span>
<span class="Normal">const</span> reagent& offset = inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!is_literal<span class="Delimiter">(</span>offset<span class="Delimiter">)</span> || !is_mu_scalar<span class="Delimiter">(</span>offset<span class="Delimiter">))</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"second ingredient of 'get-location' should have type 'offset', but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">int</span> offset_value = <span class="Constant">0</span><span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>is_integer<span class="Delimiter">(</span>offset<span class="Delimiter">.</span>name<span class="Delimiter">))</span> <span class="Delimiter">{</span> <span class="Comment">// later layers permit non-integer offsets</span>
offset_value = to_integer<span class="Delimiter">(</span>offset<span class="Delimiter">.</span>name<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>offset_value < <span class="Constant">0</span> || offset_value >= SIZE<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Type<span class="Delimiter">,</span> base_type<span class="Delimiter">).</span>elements<span class="Delimiter">))</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"invalid offset "</span> << offset_value << <span class="Constant">" for '"</span> << get<span class="Delimiter">(</span>Type<span class="Delimiter">,</span> base_type<span class="Delimiter">).</span>name << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Normal">else</span> <span class="Delimiter">{</span>
offset_value = offset<span class="Delimiter">.</span>value<span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>inst<span class="Delimiter">.</span>products<span class="Delimiter">.</span>empty<span class="Delimiter">())</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!is_mu_location<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'get-location "</span> << base<span class="Delimiter">.</span>original_string << <span class="Constant">", "</span> << offset<span class="Delimiter">.</span>original_string << <span class="Constant">"' should write to type location but '"</span> << inst<span class="Delimiter">.</span>products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>name << <span class="Constant">"' has type '"</span> << names_to_string_without_quotes<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>type<span class="Delimiter">)</span> << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> GET_LOCATION: <span class="Delimiter">{</span>
reagent<span class="Comment">/*</span><span class="Comment">copy</span><span class="Comment">*/</span> base = current_instruction<span class="Delimiter">().</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
canonize<span class="Delimiter">(</span>base<span class="Delimiter">);</span>
<span class="Normal">int</span> base_address = base<span class="Delimiter">.</span>value<span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>base_address == <span class="Constant">0</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>current_recipe_name<span class="Delimiter">())</span> << <span class="Constant">"tried to access location 0 in '"</span> << to_original_string<span class="Delimiter">(</span>current_instruction<span class="Delimiter">())</span> << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">const</span> type_tree* base_root_type = root_type<span class="Delimiter">(</span>base<span class="Delimiter">.</span>type<span class="Delimiter">);</span>
<span class="Normal">int</span> offset = ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>offset < <span class="Constant">0</span> || offset >= SIZE<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Type<span class="Delimiter">,</span> base_root_type<span class="Delimiter">-></span>value<span class="Delimiter">).</span>elements<span class="Delimiter">))</span> <span class="Identifier">break</span><span class="Delimiter">;</span> <span class="Comment">// copied from Check above</span>
<span class="Normal">int</span> result = base_address<span class="Delimiter">;</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < offset<span class="Delimiter">;</span> ++i<span class="Delimiter">)</span>
result += size_of<span class="Delimiter">(</span>element_type<span class="Delimiter">(</span>base<span class="Delimiter">.</span>type<span class="Delimiter">,</span> i<span class="Delimiter">));</span>
trace<span class="Delimiter">(</span><span class="Constant">9998</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"address to copy is "</span> << result << end<span class="Delimiter">();</span>
products<span class="Delimiter">.</span>resize<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">);</span>
products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>push_back<span class="Delimiter">(</span>result<span class="Delimiter">);</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(code)</span>
<span class="Normal">bool</span> is_mu_location<span class="Delimiter">(</span>reagent<span class="Comment">/*</span><span class="Comment">copy</span><span class="Comment">*/</span> x<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!canonize_type<span class="Delimiter">(</span>x<span class="Delimiter">))</span> <span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!x<span class="Delimiter">.</span>type<span class="Delimiter">)</span> <span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>x<span class="Delimiter">.</span>type<span class="Delimiter">-></span>right<span class="Delimiter">)</span> <span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Identifier">return</span> x<span class="Delimiter">.</span>type<span class="Delimiter">-></span>value == get<span class="Delimiter">(</span>Type_ordinal<span class="Delimiter">,</span> <span class="Constant">"location"</span><span class="Delimiter">);</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(scenario get_location_out_of_bounds)</span>
<span class="Special">% Hide_errors = true;</span>
<span class="muRecipe">def</span> main [
<span class="Constant">12</span>:num<span class="Special"> <- </span>copy <span class="Constant">34</span>
<span class="Constant">13</span>:num<span class="Special"> <- </span>copy <span class="Constant">35</span>
<span class="Constant">14</span>:num<span class="Special"> <- </span>copy <span class="Constant">36</span>
get-location <span class="Constant">12</span>:point-number/<span class="Special">raw</span><span class="Delimiter">,</span> <span class="Constant">2:offset</span> <span class="Comment"># point-number occupies 3 locations but has only 2 fields; out of bounds</span>
]
<span class="traceContains">+error: main: invalid offset 2 for 'point-number'</span>
<span class="Delimiter">:(scenario get_location_out_of_bounds_2)</span>
<span class="Special">% Hide_errors = true;</span>
<span class="muRecipe">def</span> main [
<span class="Constant">12</span>:num<span class="Special"> <- </span>copy <span class="Constant">34</span>
<span class="Constant">13</span>:num<span class="Special"> <- </span>copy <span class="Constant">35</span>
<span class="Constant">14</span>:num<span class="Special"> <- </span>copy <span class="Constant">36</span>
get-location <span class="Constant">12</span>:point-number/<span class="Special">raw</span><span class="Delimiter">,</span> <span class="Constant">-1:offset</span>
]
<span class="traceContains">+error: main: invalid offset -1 for 'point-number'</span>
<span class="Delimiter">:(scenario get_location_product_type_mismatch)</span>
<span class="Special">% Hide_errors = true;</span>
<span class="muData">container</span> boolbool [
<span class="Normal">x</span>:<span class="Normal">bool</span>
<span class="Normal">y</span>:<span class="Normal">bool</span>
]
<span class="muRecipe">def</span> main [
<span class="Constant">12</span>:<span class="Normal">bool</span><span class="Special"> <- </span>copy <span class="Constant">1</span>
<span class="Constant">13</span>:<span class="Normal">bool</span><span class="Special"> <- </span>copy <span class="Constant">0</span>
<span class="Constant">15</span>:<span class="Normal">bool</span><span class="Special"> <- </span>get-location <span class="Constant">12</span>:boolbool<span class="Delimiter">,</span> <span class="Constant">1:offset</span>
]
<span class="traceContains">+error: main: 'get-location 12:boolbool, 1:offset' should write to type location but '15' has type 'boolean'</span>
<span class="Delimiter">:(scenario get_location_indirect)</span>
<span class="Comment"># 'get-location' can read from container address</span>
<span class="muRecipe">def</span> main [
<span class="Constant">1</span>:num<span class="Special"> <- </span>copy <span class="Constant">10</span>
<span class="Comment"># 10 reserved for refcount</span>
<span class="Constant">11</span>:num<span class="Special"> <- </span>copy <span class="Constant">34</span>
<span class="Constant">12</span>:num<span class="Special"> <- </span>copy <span class="Constant">35</span>
<span class="Constant">4</span>:location<span class="Special"> <- </span>get-location <span class="Constant">1</span>:&:point/lookup<span class="Delimiter">,</span> <span class="Constant">0:offset</span>
]
<span class="traceContains">+mem: storing 11 in location 4</span>
<span class="Delimiter">:(scenario get_location_indirect_2)</span>
<span class="muRecipe">def</span> main [
<span class="Constant">1</span>:num<span class="Special"> <- </span>copy <span class="Constant">10</span>
<span class="Comment"># 10 reserved for refcount</span>
<span class="Constant">11</span>:num<span class="Special"> <- </span>copy <span class="Constant">34</span>
<span class="Constant">12</span>:num<span class="Special"> <- </span>copy <span class="Constant">35</span>
<span class="Constant">4</span>:&:num<span class="Special"> <- </span>copy <span class="Constant">20</span>/unsafe
<span class="Constant">4</span>:&:location/lookup<span class="Special"> <- </span>get-location <span class="Constant">1</span>:&:point/lookup<span class="Delimiter">,</span> <span class="Constant">0:offset</span>
]
<span class="traceContains">+mem: storing 11 in location 21</span>
<span class="Comment">//: allow waiting on a routine to complete</span>
<span class="Delimiter">:(scenario wait_for_routine)</span>
<span class="muRecipe">def</span> f1 [
<span class="Comment"># add a few routines to run</span>
<span class="Constant">1</span>:num/routine<span class="Special"> <- </span>start-running f2
<span class="Constant">2</span>:num/routine<span class="Special"> <- </span>start-running f3
wait-<span class="Normal">for</span>-routine <span class="Constant">1</span>:num/routine
<span class="Comment"># now wait for f2 to *complete* and modify location 13 before using its value</span>
<span class="Constant">20</span>:num<span class="Special"> <- </span>copy <span class="Constant">13</span>:num
]
<span class="muRecipe">def</span> f2 [
<span class="Constant">10</span>:num<span class="Special"> <- </span>copy <span class="Constant">0</span> <span class="Comment"># just padding</span>
<span class="Normal">switch</span> <span class="Comment"># simulate a block; routine f1 shouldn't restart at this point</span>
<span class="Constant">13</span>:num<span class="Special"> <- </span>copy <span class="Constant">34</span>
]
<span class="muRecipe">def</span> f3 [
<span class="Comment"># padding routine just to help simulate the block in f2 using 'switch'</span>
<span class="Constant">11</span>:num<span class="Special"> <- </span>copy <span class="Constant">0</span>
<span class="Constant">12</span>:num<span class="Special"> <- </span>copy <span class="Constant">0</span>
]
<span class="traceContains">+schedule: f1</span>
<span class="traceContains">+run: waiting for routine 2</span>
<span class="traceContains">+schedule: f2</span>
<span class="traceContains">+schedule: f3</span>
<span class="traceContains">+schedule: f2</span>
<span class="traceContains">+schedule: waking up routine 1</span>
<span class="traceContains">+schedule: f1</span>
<span class="Comment"># if we got the synchronization wrong we'd be storing 0 in location 20</span>
<span class="traceContains">+mem: storing 34 in location 20</span>
<span class="Delimiter">:(before "End routine Fields")</span>
<span class="Comment">// only if state == WAITING</span>
<span class="Normal">int</span> waiting_on_routine<span class="Delimiter">;</span>
<span class="Delimiter">:(before "End routine Constructor")</span>
waiting_on_routine = <span class="Constant">0</span><span class="Delimiter">;</span>
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
WAIT_FOR_ROUTINE<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"wait-for-routine"</span><span class="Delimiter">,</span> WAIT_FOR_ROUTINE<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> WAIT_FOR_ROUTINE: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> != <span class="Constant">1</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'wait-for-routine' requires exactly one ingredient, but got '"</span> << inst<span class="Delimiter">.</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!is_mu_number<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"first ingredient of 'wait-for-routine' should be a routine id generated by 'start-running', but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> WAIT_FOR_ROUTINE: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)</span> == Current_routine<span class="Delimiter">-></span>id<span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>current_recipe_name<span class="Delimiter">())</span> << <span class="Constant">"routine can't wait for itself! '"</span> << to_original_string<span class="Delimiter">(</span>current_instruction<span class="Delimiter">())</span> << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
Current_routine<span class="Delimiter">-></span>state = WAITING<span class="Delimiter">;</span>
Current_routine<span class="Delimiter">-></span>waiting_on_routine = ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
trace<span class="Delimiter">(</span><span class="Constant">9998</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"waiting for routine "</span> << ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Scheduler State Transitions")</span>
<span class="Comment">// Wake up any routines waiting for other routines to complete.</span>
<span class="Comment">// Important: this must come after the scheduler loop above giving routines</span>
<span class="Comment">// waiting for locations to change a chance to wake up.</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < SIZE<span class="Delimiter">(</span>Routines<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>state != WAITING<span class="Delimiter">)</span> <span class="Identifier">continue</span><span class="Delimiter">;</span>
routine* waiter = Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!waiter<span class="Delimiter">-></span>waiting_on_routine<span class="Delimiter">)</span> <span class="Identifier">continue</span><span class="Delimiter">;</span>
<span class="Normal">int</span> id = waiter<span class="Delimiter">-></span>waiting_on_routine<span class="Delimiter">;</span>
assert<span class="Delimiter">(</span>id != waiter<span class="Delimiter">-></span>id<span class="Delimiter">);</span> <span class="Comment">// routine can't wait on itself</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> j = <span class="Constant">0</span><span class="Delimiter">;</span> j < SIZE<span class="Delimiter">(</span>Routines<span class="Delimiter">);</span> ++j<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">const</span> routine* waitee = Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>j<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>waitee<span class="Delimiter">-></span>id == id && waitee<span class="Delimiter">-></span>state != RUNNING && waitee<span class="Delimiter">-></span>state != WAITING<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Comment">// routine is COMPLETED or DISCONTINUED</span>
trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">"schedule"</span><span class="Delimiter">)</span> << <span class="Constant">"waking up routine "</span> << waiter<span class="Delimiter">-></span>id << end<span class="Delimiter">();</span>
waiter<span class="Delimiter">-></span>state = RUNNING<span class="Delimiter">;</span>
waiter<span class="Delimiter">-></span>waiting_on_routine = <span class="Constant">0</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Comment">//: yield voluntarily to let some other routine run</span>
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
SWITCH<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"switch"</span><span class="Delimiter">,</span> SWITCH<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> SWITCH: <span class="Delimiter">{</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> SWITCH: <span class="Delimiter">{</span>
++current_step_index<span class="Delimiter">();</span>
<span class="Identifier">goto</span> stop_running_current_routine<span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(scenario switch_preempts_current_routine)</span>
<span class="muRecipe">def</span> f1 [
start-running f2
<span class="Constant">1</span>:num<span class="Special"> <- </span>copy <span class="Constant">34</span>
<span class="Normal">switch</span>
<span class="Constant">3</span>:num<span class="Special"> <- </span>copy <span class="Constant">36</span>
]
<span class="muRecipe">def</span> f2 [
<span class="Constant">2</span>:num<span class="Special"> <- </span>copy <span class="Constant">35</span>
]
<span class="traceContains">+mem: storing 34 in location 1</span>
<span class="Comment"># context switch</span>
<span class="traceContains">+mem: storing 35 in location 2</span>
<span class="Comment"># back to original thread</span>
<span class="traceContains">+mem: storing 36 in location 3</span>
<span class="SalientComment">//:: helpers for manipulating routines in tests</span>
<span class="Comment">//:</span>
<span class="Comment">//: Managing arbitrary scenarios requires the ability to:</span>
<span class="Comment">//: a) check if a routine is blocked</span>
<span class="Comment">//: b) restart a blocked routine (`restart`)</span>
<span class="Comment">//:</span>
<span class="Comment">//: A routine is blocked either if it's waiting or if it explicitly signals</span>
<span class="Comment">//: that it's blocked (even as it periodically wakes up and polls for some</span>
<span class="Comment">//: event).</span>
<span class="Comment">//:</span>
<span class="Comment">//: Signalling blockedness might well be a huge hack. But Mu doesn't have Unix</span>
<span class="Comment">//: signals to avoid polling with, because signals are also pretty hacky.</span>
<span class="Delimiter">:(before "End routine Fields")</span>
<span class="Normal">bool</span> blocked<span class="Delimiter">;</span>
<span class="Delimiter">:(before "End routine Constructor")</span>
blocked = <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
CURRENT_ROUTINE_IS_BLOCKED<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"current-routine-is-blocked"</span><span class="Delimiter">,</span> CURRENT_ROUTINE_IS_BLOCKED<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> CURRENT_ROUTINE_IS_BLOCKED: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>empty<span class="Delimiter">())</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'current-routine-is-blocked' should have no ingredients, but got '"</span> << inst<span class="Delimiter">.</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> CURRENT_ROUTINE_IS_BLOCKED: <span class="Delimiter">{</span>
Current_routine<span class="Delimiter">-></span>blocked = <span class="Constant">true</span><span class="Delimiter">;</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
CURRENT_ROUTINE_IS_UNBLOCKED<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"current-routine-is-unblocked"</span><span class="Delimiter">,</span> CURRENT_ROUTINE_IS_UNBLOCKED<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> CURRENT_ROUTINE_IS_UNBLOCKED: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>empty<span class="Delimiter">())</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'current-routine-is-unblocked' should have no ingredients, but got '"</span> << inst<span class="Delimiter">.</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> CURRENT_ROUTINE_IS_UNBLOCKED: <span class="Delimiter">{</span>
Current_routine<span class="Delimiter">-></span>blocked = <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Comment">//: also allow waiting on a routine to block</span>
<span class="Comment">//: (just for tests; use wait_for_routine above wherever possible)</span>
<span class="Delimiter">:(scenario wait_for_routine_to_block)</span>
<span class="muRecipe">def</span> f1 [
<span class="Constant">1</span>:num/routine<span class="Special"> <- </span>start-running f2
wait-<span class="Normal">for</span>-routine-to-block <span class="Constant">1</span>:num/routine
<span class="Comment"># now wait for f2 to run and modify location 10 before using its value</span>
<span class="Constant">11</span>:num<span class="Special"> <- </span>copy <span class="Constant">10</span>:num
]
<span class="muRecipe">def</span> f2 [
<span class="Constant">10</span>:num<span class="Special"> <- </span>copy <span class="Constant">34</span>
]
<span class="traceContains">+schedule: f1</span>
<span class="traceContains">+run: waiting for routine 2 to block</span>
<span class="traceContains">+schedule: f2</span>
<span class="traceContains">+schedule: waking up routine 1 because routine 2 is blocked</span>
<span class="traceContains">+schedule: f1</span>
<span class="Comment"># if we got the synchronization wrong we'd be storing 0 in location 11</span>
<span class="traceContains">+mem: storing 34 in location 11</span>
<span class="Delimiter">:(before "End routine Fields")</span>
<span class="Comment">// only if state == WAITING</span>
<span class="Normal">int</span> waiting_on_routine_to_block<span class="Delimiter">;</span>
<span class="Delimiter">:(before "End routine Constructor")</span>
waiting_on_routine_to_block = <span class="Constant">0</span><span class="Delimiter">;</span>
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
WAIT_FOR_ROUTINE_TO_BLOCK<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"wait-for-routine-to-block"</span><span class="Delimiter">,</span> WAIT_FOR_ROUTINE_TO_BLOCK<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> WAIT_FOR_ROUTINE_TO_BLOCK: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> != <span class="Constant">1</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'wait-for-routine-to-block' requires exactly one ingredient, but got '"</span> << inst<span class="Delimiter">.</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!is_mu_number<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"first ingredient of 'wait-for-routine-to-block' should be a routine id generated by 'start-running', but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> WAIT_FOR_ROUTINE_TO_BLOCK: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)</span> == Current_routine<span class="Delimiter">-></span>id<span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>current_recipe_name<span class="Delimiter">())</span> << <span class="Constant">"routine can't wait for itself! '"</span> << to_original_string<span class="Delimiter">(</span>current_instruction<span class="Delimiter">())</span> << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
Current_routine<span class="Delimiter">-></span>state = WAITING<span class="Delimiter">;</span>
Current_routine<span class="Delimiter">-></span>waiting_on_routine_to_block = ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
trace<span class="Delimiter">(</span><span class="Constant">9998</span><span class="Delimiter">,</span> <span class="Constant">"run"</span><span class="Delimiter">)</span> << <span class="Constant">"waiting for routine "</span> << ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)</span> << <span class="Constant">" to block"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Scheduler State Transitions")</span>
<span class="Comment">// Wake up any routines waiting for other routines to stop running.</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < SIZE<span class="Delimiter">(</span>Routines<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>state != WAITING<span class="Delimiter">)</span> <span class="Identifier">continue</span><span class="Delimiter">;</span>
routine* waiter = Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!waiter<span class="Delimiter">-></span>waiting_on_routine_to_block<span class="Delimiter">)</span> <span class="Identifier">continue</span><span class="Delimiter">;</span>
<span class="Normal">int</span> id = waiter<span class="Delimiter">-></span>waiting_on_routine_to_block<span class="Delimiter">;</span>
assert<span class="Delimiter">(</span>id != waiter<span class="Delimiter">-></span>id<span class="Delimiter">);</span> <span class="Comment">// routine can't wait on itself</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> j = <span class="Constant">0</span><span class="Delimiter">;</span> j < SIZE<span class="Delimiter">(</span>Routines<span class="Delimiter">);</span> ++j<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">const</span> routine* waitee = Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>j<span class="Delimiter">);</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>waitee<span class="Delimiter">-></span>id != id<span class="Delimiter">)</span> <span class="Identifier">continue</span><span class="Delimiter">;</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>waitee<span class="Delimiter">-></span>state != RUNNING || waitee<span class="Delimiter">-></span>blocked<span class="Delimiter">)</span> <span class="Delimiter">{</span>
trace<span class="Delimiter">(</span><span class="Constant">9999</span><span class="Delimiter">,</span> <span class="Constant">"schedule"</span><span class="Delimiter">)</span> << <span class="Constant">"waking up routine "</span> << waiter<span class="Delimiter">-></span>id << <span class="Constant">" because routine "</span> << waitee<span class="Delimiter">-></span>id << <span class="Constant">" is blocked"</span> << end<span class="Delimiter">();</span>
waiter<span class="Delimiter">-></span>state = RUNNING<span class="Delimiter">;</span>
waiter<span class="Delimiter">-></span>waiting_on_routine_to_block = <span class="Constant">0</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Comment">//: helper for restarting blocking routines in tests</span>
<span class="Delimiter">:(before "End Primitive Recipe Declarations")</span>
RESTART<span class="Delimiter">,</span>
<span class="Delimiter">:(before "End Primitive Recipe Numbers")</span>
put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">"restart"</span><span class="Delimiter">,</span> RESTART<span class="Delimiter">);</span>
<span class="Delimiter">:(before "End Primitive Recipe Checks")</span>
<span class="Normal">case</span> RESTART: <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> != <span class="Constant">1</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"'restart' requires exactly one ingredient, but got '"</span> << inst<span class="Delimiter">.</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>!is_mu_number<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
raise << maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> << <span class="Constant">"first ingredient of 'restart' should be a routine id generated by 'start-running', but got '"</span> << inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>original_string << <span class="Constant">"'</span><span class="cSpecial">\n</span><span class="Constant">"</span> << end<span class="Delimiter">();</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(before "End Primitive Recipe Implementations")</span>
<span class="Normal">case</span> RESTART: <span class="Delimiter">{</span>
<span class="Normal">int</span> id = ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
<span class="Normal">for</span> <span class="Delimiter">(</span><span class="Normal">int</span> i = <span class="Constant">0</span><span class="Delimiter">;</span> i < SIZE<span class="Delimiter">(</span>Routines<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>id == id<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span class="Normal">if</span> <span class="Delimiter">(</span>Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>state == WAITING<span class="Delimiter">)</span>
Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>state = RUNNING<span class="Delimiter">;</span>
Routines<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)-></span>blocked = <span class="Constant">false</span><span class="Delimiter">;</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">}</span>
<span class="Identifier">break</span><span class="Delimiter">;</span>
<span class="Delimiter">}</span>
<span class="Delimiter">:(scenario cannot_restart_completed_routine)</span>
<span class="Special">% Scheduling_interval = 1;</span>
<span class="muRecipe">def</span> main [
local-scope
<span class="Normal">r</span>:num/routine-id<span class="Special"> <- </span>start-running f
<span class="Normal">x</span>:num<span class="Special"> <- </span>copy <span class="Constant">0</span> <span class="Comment"># wait for f to be scheduled</span>
<span class="Comment"># r is COMPLETED by this point</span>
restart r <span class="Comment"># should have no effect</span>
<span class="Normal">x</span>:num<span class="Special"> <- </span>copy <span class="Constant">0</span> <span class="Comment"># give f time to be scheduled (though it shouldn't be)</span>
]
<span class="muRecipe">def</span> f [
<span class="Constant">1</span>:num/<span class="Special">raw <- </span>copy <span class="Constant">1</span>
]
<span class="Comment"># shouldn't crash</span>
<span class="Delimiter">:(scenario restart_blocked_routine)</span>
<span class="Special">% Scheduling_interval = 1;</span>
<span class="muRecipe">def</span> main [
local-scope
<span class="Normal">r</span>:num/routine-id<span class="Special"> <- </span>start-running f
wait-<span class="Normal">for</span>-routine-to-block r <span class="Comment"># get past the block in f below</span>
restart r
wait-<span class="Normal">for</span>-routine-to-block r <span class="Comment"># should run f to completion</span>
]
<span class="Comment"># function with one block</span>
<span class="muRecipe">def</span> f [
current-routine-is-blocked
<span class="Comment"># 8 instructions of padding, many more than 'main' above</span>
<span class="Constant">1</span>:num<span class="Special"> <- </span>add <span class="Constant">1</span>:num<span class="Delimiter">,</span> <span class="Constant">1</span>
<span class="Constant">1</span>:num<span class="Special"> <- </span>add <span class="Constant">1</span>:num<span class="Delimiter">,</span> <span class="Constant">1</span>
<span class="Constant">1</span>:num<span class="Special"> <- </span>add <span class="Constant">1</span>:num<span class="Delimiter">,</span> <span class="Constant">1</span>
<span class="Constant">1</span>:num<span class="Special"> <- </span>add <span class="Constant">1</span>:num<span class="Delimiter">,</span> <span class="Constant">1</span>
<span class="Constant">1</span>:num<span class="Special"> <- </span>add <span class="Constant">1</span>:num<span class="Delimiter">,</span> <span class="Constant">1</span>
<span class="Constant">1</span>:num<span class="Special"> <- </span>add <span class="Constant">1</span>:num<span class="Delimiter">,</span> <span class="Constant">1</span>
<span class="Constant">1</span>:num<span class="Special"> <- </span>add <span class="Constant">1</span>:num<span class="Delimiter">,</span> <span class="Constant">1</span>
<span class="Constant">1</span>:num<span class="Special"> <- </span>add <span class="Constant">1</span>:num<span class="Delimiter">,</span> <span class="Constant">1</span>
<span class="Constant">1</span>:num<span class="Special"> <- </span>add <span class="Constant">1</span>:num<span class="Delimiter">,</span> <span class="Constant">1</span>
]
<span class="Comment"># make sure all of f ran</span>
<span class="traceContains">+mem: storing 8 in location 1</span>
</pre>
</body>
</html>
<!-- vim: set foldmethod=manual : -->