IndexNextUpPreviousUrbi SDK 3.0.0

Chapter 10
Parallelism, Concurrent Flow Control

Parallelism is a major feature of urbiscript. So far, all we’ve seen already existed in other languages — although we tried to pick, mix and adapt features and paradigms to create a nice scripting language. Parallelism is one of the corner stones of its paradigm, and what makes it so well suited to high-level scripting of interactive agents, in fields such as robotics or AI.

 10.1 Parallelism operators
 10.2 Detach
 10.3 Tags for parallel control flows
 10.4 timeout, freezeif and stopif
  10.4.1 timeout
  10.4.2 stopif and freezeif
 10.5 Advanced example with parallelism and tags

10.1 Parallelism operators

For now, we’ve separated our different commands with a semicolon (;). There are actually four statement separators in urbiscript:

The example below demonstrates the use of & to launch two functions in parallel.

 
function test(name) 

  echo(name + ": 1"); 
  echo(name + ": 2"); 
  echo(name + ": 3"); 
} |; 
// Serialized executions 
test("left") ; test ("middle"); test ("right"); 
[00000000] *** left: 1 
[00000000] *** left: 2 
[00000000] *** left: 3 
[00000000] *** middle: 1 
[00000000] *** middle: 2 
[00000000] *** middle: 3 
[00000000] *** right: 1 
[00000000] *** right: 2 
[00000000] *** right: 3 
// Parallel execution 
test("left") & test("middle") & test ("right"); 
[00000000] *** left: 1 
[00000000] *** middle: 1 
[00000000] *** right: 1 
[00000000] *** left: 2 
[00000000] *** middle: 2 
[00000000] *** right: 2 
[00000000] *** left: 3 
[00000000] *** middle: 3 
[00000000] *** right: 3  

In this test, we see that the & runs its operands simultaneously.

The difference between “&” and “,” is rather subtle:

 
function test(name) 

  echo(name + ": 1"); 
  echo(name + ": 2"); 
  echo(name + ": 3"); 
}|; 
// Run test and echo("right") in parallel, 
// and wait until both are done before continuing 
test("left") & echo("right"); echo("done"); 
[00000000] *** left: 1 
[00000000] *** right 
[00000000] *** left: 2 
[00000000] *** left: 3 
[00000000] *** done 
// Run test in background, then both echos without waiting. 
test("left") , echo("right"); echo("done"); 
[00000000] *** left: 1 
[00000000] *** right 
[00000000] *** left: 2 
[00000000] *** done 
[00000000] *** left: 3  

That’s about all there is to say about these operators. Although they’re rather simple, they are really powerful and enables you to include parallelism anywhere at no syntactical cost.

10.2 Detach

The Control.detach function backgrounds the execution of its argument. Its behavior is the same as the comma (,) operator, except that the execution is completely detached, and not waited for at the end of the scope.

 
function test() 

  // Wait for one second, and echo "foo". 
  detach({sleep(1s); echo("foo")}); 
}|; 
test(); 
echo("Not blocked"); 
[00000000] Job<shell_15> 
[00000000] *** Not blocked 
sleep(2s); 
echo("End of sleep"); 
[00001000] *** foo 
[00002000] *** End of sleep  

10.3 Tags for parallel control flows

A Tag is a multipurpose code execution control and instrumentation feature. Any chunk of code can be tagged, by preceding it with a tag and a colon (:). Tag can be created with Tag.new(name). Naming tags is optional, yet it’s a good idea since it will be used for many features. The example below illustrates how to tag chunks of code.

 
// Create a new tag 
var mytag = Tag.new("name"); 
[00000000] Tag<name> 
// Tag the evaluation of 42 
mytag: 42; 
[00000000] 42 
// Tag the evaluation of a block. 
mytag: { "foo"; 51 }; 
[00000000] 51 
// Tag a function call. 
mytag: echo("tagged"); 
[00000000] *** tagged  

You can use tags that were not declared previously, they will be created implicitly (see below). However, this is not recommended since tags will be created in a global scope, the Tag object. This feature can be used when inputting test code in the top level to avoid bothering to declare each tag, yet it is considered poor practice in regular code.

 
// Since mytag is not declared, this will first do: 
// var Tag.mytag = Tag.new("mytag"); 
mytag : 42; 
[00000000] 42  

So you can tag code, yet what’s the use? One of the primary purpose of tags is to be able to control the execution of code running in parallel. Tags have a few control methods (see Tag):

freeze
Suspend execution of all tagged code.
unfreeze
Resume execution of previously frozen code.
stop
Stop the execution of the tagged code. The flows of execution that where stopped jump immediately at the end of the tagged block. (if you are familiar with exceptions, imagine that tag.stop() throws an exception in all tasks tagged by tag, that is caught by outermost occurence of tag:).
block
Block the execution of the tagged code, that is:

You can think of block like a permanent stop.

unblock
Stop blocking the tagged code.

The three following examples illustrate these features.

 
// Launch in background (using the comma) code that prints "ping" 
// every second.  Tag it to keep control over it. 
mytag: 
  every (1s) 
    echo("ping"), 
sleep(2.5s); 
[00000000] *** ping 
[00001000] *** ping 
[00002000] *** ping 
// Suspend execution 
mytag.freeze(); 
// No printing anymore 
sleep(1s); 
// Resume execution 
mytag.unfreeze(); 
sleep(1s); 
[00007000] *** ping  

 
// Now, we print out a message when we get out of the tag. 

  mytag: 
    every (1s) 
      echo("ping"); 
  // Execution flow jumps here if mytag is stopped. 
  echo("Background job stopped")| 
}, 
sleep(2.5s); 
[00000000] *** ping 
[00001000] *** ping 
[00002000] *** ping 
// Stop the tag 
mytag.stop(); 
[00002500] *** Background job stopped 
// Our background job finished. 
// Unfreezing the tag has no effect. 
mytag.unfreeze();  

 
// Now, print out a message when we get out of the tag. 
loop 

  echo("ping"); sleep(1s); 
  mytag: { echo("pong"); sleep(1s); }; 
}, 
sleep(3.5s); 
[00000000] *** ping 
[00001000] *** pong 
[00002000] *** ping 
[00003000] *** pong 
 
// Block printing of pong. 
mytag.block(); 
sleep(3s); 
 
// The second half of the while isn’t executed anymore. 
[00004000] *** ping 
[00005000] *** ping 
[00006000] *** ping 
 
// Reactivate pong 
mytag.unblock(); 
sleep(3.5s); 
[00008000] *** pong 
[00009000] *** ping 
[00010000] *** pong 
[00011000] *** ping  

10.4 timeout, freezeif and stopif

timeout, freezeif and stopif are three keywords that take advantage of the primitive parallelism tools we’ve seen above to provide useful constructs.

10.4.1 timeout

 
var countdown = 3|; 
timeout(3.5s) every(1s) { echo(countdown); countdown --;}; echo("stopped"); 
[00000001] *** 3 
[00000001] *** 2 
[00000001] *** 1 
[00000001] *** 0 
[00000001] *** stopped  

As you can see from the above example, timeout stops (in a similar fashion to Tag.stop()) the given code after given duration.

The timeout itself does not detach, so the timeout command lasts for the given duration, or until the command finishes, whichever comes first.

10.4.2 stopif and freezeif

freezeif and stopif use similar construct, and freeze/stop (respectively) the given code when the condition is true.

 
var b = false|; 
timeout(3.2s) detach({ 
  freezeif(b) every(500ms) echo("tick"), 
  freezeif(!b) every(500ms) echo("tack"
  })|; 
sleep(1.2s); b = true; 
[00000001] *** tick 
[00000001] *** tick 
[00000001] *** tick 
[00000001] true 
sleep(1s); b = false; 
[00000001] *** tack 
[00000001] *** tack 
[00000001] *** tack 
[00000001] false 
sleep(1s); echo("done"); 
[00000001] *** tick 
[00000001] *** tick 
[00000001] *** done  

10.5 Advanced example with parallelism and tags

In this section, we implement a more advanced example with parallelism.

The listing below presents how to implement a timeOut function, that takes code to execute and a timeout as arguments. It executes the code, and returns its value. However, if the code execution takes longer than the given timeout, it aborts it, prints "Timeout!" and returns void. In this example, we use:

 
// timeout (Code, Duration). 
function timeOut 

  // In background, launch a timeout job that waits 
  // for the given duration before aborting the function. 
  // call.evalArgAt(1) is the second argument, the duration. 
  { 
    sleep(call.evalArgAt(1)); 
    echo("Timeout!"); 
    return; 
  }, 
  // Run the Code and return its value. 
  return call.evalArgAt(0); 
} |; 
timeOut({sleep(1s); echo("On time"); 42}, 2s); 
[00000000] *** On time 
[00000000] 42 
timeOut({sleep(2s); echo("On time"); 42}, 1s); 
[00000000] *** Timeout!