mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-10 21:58:48 +01:00
288 lines
12 KiB
HTML
288 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
|
<title>Concurrency – Wren</title>
|
|
<script type="application/javascript" src="prism.js" data-manual></script>
|
|
<script type="application/javascript" src="codejar.js"></script>
|
|
<script type="application/javascript" src="wren.js"></script>
|
|
<link rel="stylesheet" type="text/css" href="prism.css" />
|
|
<link rel="stylesheet" type="text/css" href="style.css" />
|
|
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
|
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
|
the viewport. -->
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
|
</head>
|
|
<body id="top">
|
|
<header>
|
|
<div class="page">
|
|
<div class="main-column">
|
|
<h1><a href="./">wren</a></h1>
|
|
<h2>a classy little scripting language</h2>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
<div class="page">
|
|
<nav class="big">
|
|
<a href="./"><img src="./wren.svg" class="logo"></a>
|
|
<ul>
|
|
<li><a href="getting-started.html">Getting Started</a></li>
|
|
<li><a href="contributing.html">Contributing</a></li>
|
|
<li><a href="blog">Blog</a></li>
|
|
<li><a href="try">Try it!</a></li>
|
|
</ul>
|
|
<section>
|
|
<h2>guides</h2>
|
|
<ul>
|
|
<li><a href="syntax.html">Syntax</a></li>
|
|
<li><a href="values.html">Values</a></li>
|
|
<li><a href="lists.html">Lists</a></li>
|
|
<li><a href="maps.html">Maps</a></li>
|
|
<li><a href="method-calls.html">Method Calls</a></li>
|
|
<li><a href="control-flow.html">Control Flow</a></li>
|
|
<li><a href="variables.html">Variables</a></li>
|
|
<li><a href="classes.html">Classes</a></li>
|
|
<li><a href="functions.html">Functions</a></li>
|
|
<li><a href="concurrency.html">Concurrency</a></li>
|
|
<li><a href="error-handling.html">Error Handling</a></li>
|
|
<li><a href="modularity.html">Modularity</a></li>
|
|
</ul>
|
|
</section>
|
|
<section>
|
|
<h2>API docs</h2>
|
|
<ul>
|
|
<li><a href="modules">Modules</a></li>
|
|
</ul>
|
|
</section>
|
|
<section>
|
|
<h2>reference</h2>
|
|
<ul>
|
|
<li><a href="cli">Wren CLI</a></li>
|
|
<li><a href="embedding">Embedding</a></li>
|
|
<li><a href="performance.html">Performance</a></li>
|
|
<li><a href="qa.html">Q & A</a></li>
|
|
</ul>
|
|
</section>
|
|
</nav>
|
|
<nav class="small">
|
|
<table>
|
|
<tr>
|
|
<div><a href="getting-started.html">Getting Started</a></div>
|
|
<div><a href="contributing.html">Contributing</a></div>
|
|
<div><a href="blog">Blog</a></div>
|
|
<div><a href="try">Try it!</a></div>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="2"><h2>guides</h2></td>
|
|
<td><h2>reference</h2></td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<ul>
|
|
<li><a href="syntax.html">Syntax</a></li>
|
|
<li><a href="values.html">Values</a></li>
|
|
<li><a href="lists.html">Lists</a></li>
|
|
<li><a href="maps.html">Maps</a></li>
|
|
<li><a href="method-calls.html">Method Calls</a></li>
|
|
<li><a href="control-flow.html">Control Flow</a></li>
|
|
</ul>
|
|
</td>
|
|
<td>
|
|
<ul>
|
|
<li><a href="variables.html">Variables</a></li>
|
|
<li><a href="classes.html">Classes</a></li>
|
|
<li><a href="functions.html">Functions</a></li>
|
|
<li><a href="concurrency.html">Concurrency</a></li>
|
|
<li><a href="error-handling.html">Error Handling</a></li>
|
|
<li><a href="modularity.html">Modularity</a></li>
|
|
</ul>
|
|
</td>
|
|
<td>
|
|
<ul>
|
|
<li><a href="modules">API/Modules</a></li>
|
|
<li><a href="embedding">Embedding</a></li>
|
|
<li><a href="performance.html">Performance</a></li>
|
|
<li><a href="qa.html">Q & A</a></li>
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</nav>
|
|
<main>
|
|
<h2>Concurrency</h2>
|
|
<p>Lightweight concurrency is a key feature of Wren and it is expressed using
|
|
<em>fibers</em>. They control how all code is executed, and take the place of
|
|
exceptions in <a href="error-handling.html">error handling</a>.</p>
|
|
<p>Fibers are a bit like threads except they are <em>cooperatively</em> scheduled. That
|
|
means Wren doesn’t pause one fiber and switch to another until you tell it to.
|
|
You don’t have to worry about context switches at random times and all of the
|
|
headaches those cause.</p>
|
|
<p>Wren takes care of all of the fibers in the VM, so they don’t use OS thread
|
|
resources, or require heavyweight context switches. Each just needs a bit of
|
|
memory for its stack. A fiber will get garbage collected like any other object
|
|
when not referenced any more, so you can create them freely.</p>
|
|
<p>They are lightweight enough that you can, for example, have a separate fiber for
|
|
each entity in a game. Wren can handle thousands of them without breaking a
|
|
sweat. For example, when you run Wren in interactive mode, it creates a new
|
|
fiber for every line of code you type in.</p>
|
|
<h2>Creating fibers <a href="#creating-fibers" name="creating-fibers" class="header-anchor">#</a></h2>
|
|
<p>All Wren code runs within the context of a fiber. When you first start a Wren
|
|
script, a main fiber is created for you automatically. You can spawn new fibers
|
|
using the Fiber class’s constructor:</p>
|
|
<pre class="snippet">
|
|
var fiber = Fiber.new {
|
|
System.print("This runs in a separate fiber.")
|
|
}
|
|
</pre>
|
|
|
|
<p>It takes a <a href="functions.html">function</a> containing the code the fiber should execute. The
|
|
function can take zero or one parameter, but no more than that. Creating the
|
|
fiber does not immediately run it. It just wraps the function and sits there,
|
|
waiting to be activated.</p>
|
|
<h2>Invoking fibers <a href="#invoking-fibers" name="invoking-fibers" class="header-anchor">#</a></h2>
|
|
<p>Once you’ve created a fiber, you run it by calling its <code>call()</code> method:</p>
|
|
<pre class="snippet">
|
|
fiber.call()
|
|
</pre>
|
|
|
|
<p>This suspends the current fiber and executes the called one until it reaches the
|
|
end of its body or until it passes control to yet another fiber. If it reaches
|
|
the end of its body, it is considered <em>done</em>:</p>
|
|
<pre class="snippet">
|
|
var fiber = Fiber.new {
|
|
System.print("It's alive!")
|
|
}
|
|
|
|
System.print(fiber.isDone) //> false
|
|
fiber.call() //> It's alive!
|
|
System.print(fiber.isDone) //> true
|
|
</pre>
|
|
|
|
<p>When a called fiber finishes, it automatically passes control <em>back</em> to the
|
|
fiber that called it. It’s a runtime error to try to call a fiber that is
|
|
already done.</p>
|
|
<h2>Yielding <a href="#yielding" name="yielding" class="header-anchor">#</a></h2>
|
|
<p>The main difference between fibers and functions is that a fiber can be
|
|
suspended in the middle of its operation and then resumed later. Calling
|
|
another fiber is one way to suspend a fiber, but that’s more or less the same
|
|
as one function calling another.</p>
|
|
<p>Things get interesting when a fiber <em>yields</em>. A yielded fiber passes control
|
|
<em>back</em> to the fiber that ran it, but <em>remembers where it is</em>. The next time the
|
|
fiber is called, it picks up right where it left off and keeps going.</p>
|
|
<p>You make a fiber yield by calling the static <code>yield()</code> method on Fiber:</p>
|
|
<pre class="snippet">
|
|
var fiber = Fiber.new {
|
|
System.print("Before yield")
|
|
Fiber.yield()
|
|
System.print("Resumed")
|
|
}
|
|
|
|
System.print("Before call") //> Before call
|
|
fiber.call() //> Before yield
|
|
System.print("Calling again") //> Calling again
|
|
fiber.call() //> Resumed
|
|
System.print("All done") //> All done
|
|
</pre>
|
|
|
|
<p>Note that even though this program uses <em>concurrency</em>, it is still
|
|
<em>deterministic</em>. You can reason precisely about what it’s doing and aren’t at
|
|
the mercy of a thread scheduler playing Russian roulette with your code.</p>
|
|
<h2>Passing values <a href="#passing-values" name="passing-values" class="header-anchor">#</a></h2>
|
|
<p>Calling and yielding fibers is used for passing control, but it can also pass
|
|
<em>data</em>. When you call a fiber, you can optionally pass a value to it.</p>
|
|
<p>If you create a fiber using a function that takes a parameter, you can pass a
|
|
value to it through <code>call()</code>:</p>
|
|
<pre class="snippet">
|
|
var fiber = Fiber.new {|param|
|
|
System.print(param)
|
|
}
|
|
|
|
fiber.call("Here you go") //> Here you go
|
|
</pre>
|
|
|
|
<p>If the fiber has yielded and is waiting to resume, the value you pass to call
|
|
becomes the return value of the <code>yield()</code> call when it resumes:</p>
|
|
<pre class="snippet">
|
|
var fiber = Fiber.new {|param|
|
|
System.print(param)
|
|
var result = Fiber.yield()
|
|
System.print(result)
|
|
}
|
|
|
|
fiber.call("First") //> First
|
|
fiber.call("Second") //> Second
|
|
</pre>
|
|
|
|
<p>Fibers can also pass values <em>back</em> when they yield. If you pass an argument to
|
|
<code>yield()</code>, that will become the return value of the <code>call()</code> that was used to
|
|
invoke the fiber:</p>
|
|
<pre class="snippet">
|
|
var fiber = Fiber.new {
|
|
Fiber.yield("Reply")
|
|
}
|
|
|
|
System.print(fiber.call()) //> Reply
|
|
</pre>
|
|
|
|
<p>This is sort of like how a function call may return a value, except that a fiber
|
|
may return a whole sequence of values, one every time it yields.</p>
|
|
<h2>Full coroutines <a href="#full-coroutines" name="full-coroutines" class="header-anchor">#</a></h2>
|
|
<p>What we’ve seen so far is very similar to what you can do with languages like
|
|
Python and C# that have <em>generators</em>. Those let you define a function call that
|
|
you can suspend and resume. When using the function, it appears like a sequence
|
|
you can iterate over.</p>
|
|
<p>Wren’s fibers can do that, but they can do much more. Like Lua, they are full
|
|
<em>coroutines</em>—they can suspend from anywhere in the callstack. The function
|
|
you use to create a fiber can call a method that calls another method that calls
|
|
some third method which finally calls yield. When that happens, <em>all</em> of those
|
|
method calls — the entire callstack — gets suspended. For example:</p>
|
|
<pre class="snippet">
|
|
var fiber = Fiber.new {
|
|
(1..10).each {|i|
|
|
Fiber.yield(i)
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<p>Here, we’re calling <code>yield()</code> from within a <a href="functions.html">function</a> being
|
|
passed to the <code>each()</code> method. This works fine in Wren because that inner
|
|
<code>yield()</code> call will suspend the call to <code>each()</code> and the function passed to it
|
|
as a callback.</p>
|
|
<h2>Transferring control <a href="#transferring-control" name="transferring-control" class="header-anchor">#</a></h2>
|
|
<p>Fibers have one more trick up their sleeves. When you execute a fiber using
|
|
<code>call()</code>, the fiber tracks which fiber it will return to when it yields. This
|
|
lets you build up a chain of fiber calls that will eventually unwind back to
|
|
the main fiber when all of the called ones yield or finish.</p>
|
|
<p>This is usually what you want. But if you’re doing something low level, like
|
|
writing your own scheduler to manage a pool of fibers, you may not want to treat
|
|
them explicitly like a stack.</p>
|
|
<p>For rare cases like that, fibers also have a <code>transfer()</code> method. This switches
|
|
execution to the transferred fiber and “forgets” the fiber that was transferred
|
|
<em>from</em>. The previous one is suspended, leaving it in whatever state it was in.
|
|
You can resume the previous fiber by explicitly transferring back to it, or even
|
|
calling it. If you don’t, execution stops when the last transferred fiber
|
|
returns.</p>
|
|
<p>Where <code>call()</code> and <code>yield()</code> are analogous to calling and returning from
|
|
functions, <code>transfer()</code> works more like an unstructured goto. It lets you freely
|
|
switch control between a number of fibers, all of which act as peers to one
|
|
another.</p>
|
|
<p><br><hr>
|
|
<a class="right" href="error-handling.html">Error Handling →</a>
|
|
<a href="classes.html">← Classes</a></p>
|
|
</main>
|
|
</div>
|
|
<footer>
|
|
<div class="page">
|
|
<div class="main-column">
|
|
<p>Wren lives
|
|
<a href="https://github.com/wren-lang/wren">on GitHub</a>
|
|
— Made with ❤ by
|
|
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
|
<a href="https://github.com/wren-lang/wren/blob/main/AUTHORS">friends</a>.
|
|
</p>
|
|
<div class="main-column">
|
|
</div>
|
|
</footer>
|
|
</body>
|
|
</html>
|