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.
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.
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
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):
You can think of block like a permanent stop.
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
timeout, freezeif and stopif are three keywords that take advantage of the primitive parallelism tools we’ve seen above to provide useful constructs.
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.
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
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!