2026-01-26 05:12:14 +01:00
{# retoor < retoor @ molodetz . nl > #}
{% extends 'page.html' %}
2026-01-24 23:40:22 +01:00
2026-01-26 05:12:14 +01:00
{% set page_title = "Fibers" %}
{% set breadcrumb = [{"url": "language/index.html", "title": "Language"}, {"title": "Fibers"}] %}
{% set prev_page = {"url": "language/control-flow.html", "title": "Control Flow"} %}
{% set next_page = {"url": "language/modules.html", "title": "Modules"} %}
{% block article %}
2026-01-24 23:40:22 +01:00
< h1 > Fibers</ h1 >
< p > Fibers are Wren's mechanism for cooperative concurrency. They are lightweight threads of execution that you explicitly control. Unlike OS threads, only one fiber runs at a time, and switching between them is explicit.</ p >
< h2 > Creating Fibers</ h2 >
< p > Create a fiber with < code > Fiber.new</ code > :</ p >
< pre >< code > var fiber = Fiber.new {
System.print("Inside fiber")
}</ code ></ pre >
< p > The fiber does not run immediately. It is suspended until you start it.</ p >
< h2 > Running Fibers</ h2 >
< h3 > call()</ h3 >
< p > Start a fiber and wait for it to complete or yield:</ p >
< pre >< code > var fiber = Fiber.new {
System.print("Running")
}
fiber.call() // Prints "Running"</ code ></ pre >
< h3 > try()</ h3 >
< p > Start a fiber and catch any runtime errors:</ p >
< pre >< code > var fiber = Fiber.new {
Fiber.abort("Something went wrong")
}
var error = fiber.try()
System.print("Error: %(error)") // Error: Something went wrong</ code ></ pre >
< h2 > Yielding</ h2 >
< p > Fibers can pause execution and return control to the caller:</ p >
< pre >< code > var fiber = Fiber.new {
System.print("First")
Fiber.yield()
System.print("Second")
Fiber.yield()
System.print("Third")
}
fiber.call() // Prints "First"
fiber.call() // Prints "Second"
fiber.call() // Prints "Third"</ code ></ pre >
< h3 > Yielding Values</ h3 >
< pre >< code > var counter = Fiber.new {
Fiber.yield(1)
Fiber.yield(2)
Fiber.yield(3)
}
System.print(counter.call()) // 1
System.print(counter.call()) // 2
System.print(counter.call()) // 3</ code ></ pre >
< h3 > Passing Values In</ h3 >
< pre >< code > var adder = Fiber.new {
var total = 0
while (true) {
var value = Fiber.yield(total)
total = total + value
}
}
adder.call() // Start the fiber
System.print(adder.call(5)) // 5
System.print(adder.call(10)) // 15
System.print(adder.call(3)) // 18</ code ></ pre >
< h2 > Fiber State</ h2 >
< p > Check the state of a fiber:</ p >
< pre >< code > var fiber = Fiber.new {
Fiber.yield()
}
System.print(fiber.isDone) // false
fiber.call()
System.print(fiber.isDone) // false (yielded)
fiber.call()
System.print(fiber.isDone) // true (completed)</ code ></ pre >
< h2 > Error Handling</ h2 >
< p > Fibers can abort with an error:</ p >
< pre >< code > Fiber.abort("Error message")</ code ></ pre >
< p > Use < code > try()</ code > to catch errors:</ p >
< pre >< code > var fiber = Fiber.new {
var x = 1 / 0 // Will cause infinity, not error
[1, 2, 3][10] // This will cause an error
}
var error = fiber.try()
if (error != null) {
System.print("Caught: %(error)")
}</ code ></ pre >
< h2 > Current Fiber</ h2 >
< p > Get the currently executing fiber:</ p >
< pre >< code > var current = Fiber.current
System.print(current) // Fiber instance</ code ></ pre >
< h2 > Generator Pattern</ h2 >
< p > Fibers naturally implement generators:</ p >
< pre >< code > var range = Fn.new { |start, end|
return Fiber.new {
var i = start
while (i < = end) {
Fiber.yield(i)
i = i + 1
}
}
}
var nums = range.call(1, 5)
while (!nums.isDone) {
var value = nums.call()
if (value != null) System.print(value)
}</ code ></ pre >
< h2 > Coroutine Pattern</ h2 >
< p > Two fibers can communicate back and forth:</ p >
< pre >< code > var producer = Fiber.new {
for (i in 1..5) {
System.print("Producing %(i)")
Fiber.yield(i)
}
}
var consumer = Fiber.new {
while (!producer.isDone) {
var value = producer.call()
if (value != null) {
System.print("Consuming %(value)")
}
}
}
consumer.call()</ code ></ pre >
< h2 > Async Operations with Scheduler</ h2 >
< p > Wren-CLI uses fibers for async I/O. The scheduler suspends fibers during I/O and resumes them when the operation completes:</ p >
< pre >< code > import "timer" for Timer
System.print("Before sleep")
Timer.sleep(1000) // Fiber suspends here
System.print("After sleep")</ code ></ pre >
< p > The scheduler pattern internally looks like:</ p >
< pre >< code > import "scheduler" for Scheduler
Scheduler.await_ {
Timer.sleep_(1000, Fiber.current)
}</ code ></ pre >
< h2 > Fiber Methods</ h2 >
< h3 > Static Methods</ h3 >
< table >
< tr >
< th > Method</ th >
< th > Description</ th >
</ tr >
< tr >
< td >< code > Fiber.new { block }</ code ></ td >
< td > Create a new fiber</ td >
</ tr >
< tr >
< td >< code > Fiber.current</ code ></ td >
< td > Get the current fiber</ td >
</ tr >
< tr >
< td >< code > Fiber.yield()</ code ></ td >
< td > Pause and return null</ td >
</ tr >
< tr >
< td >< code > Fiber.yield(value)</ code ></ td >
< td > Pause and return value</ td >
</ tr >
< tr >
< td >< code > Fiber.abort(message)</ code ></ td >
< td > Abort with error</ td >
</ tr >
< tr >
< td >< code > Fiber.suspend()</ code ></ td >
< td > Suspend the current fiber</ td >
</ tr >
</ table >
< h3 > Instance Methods</ h3 >
< table >
< tr >
< th > Method</ th >
< th > Description</ th >
</ tr >
< tr >
< td >< code > fiber.call()</ code ></ td >
< td > Run fiber, wait for yield/complete</ td >
</ tr >
< tr >
< td >< code > fiber.call(value)</ code ></ td >
< td > Run with value passed to yield</ td >
</ tr >
< tr >
< td >< code > fiber.try()</ code ></ td >
< td > Run and catch errors</ td >
</ tr >
< tr >
< td >< code > fiber.isDone</ code ></ td >
< td > True if completed</ td >
</ tr >
< tr >
< td >< code > fiber.error</ code ></ td >
< td > Error message if aborted</ td >
</ tr >
< tr >
< td >< code > fiber.transfer()</ code ></ td >
< td > Switch to this fiber</ td >
</ tr >
< tr >
< td >< code > fiber.transfer(value)</ code ></ td >
< td > Switch with value</ td >
</ tr >
< tr >
< td >< code > fiber.transferError(msg)</ code ></ td >
< td > Switch and raise error</ td >
</ tr >
</ table >
< h2 > Transfer vs Call</ h2 >
< p >< code > call()</ code > maintains a call stack and returns when the fiber yields:</ p >
< pre >< code > var a = Fiber.new {
System.print("a: before yield")
Fiber.yield()
System.print("a: after yield")
}
a.call()
System.print("back in main")
a.call()
// Output:
// a: before yield
// back in main
// a: after yield</ code ></ pre >
< p >< code > transfer()</ code > does not maintain a call stack:</ p >
< pre >< code > var main = Fiber.current
var a = null
var b = null
a = Fiber.new {
System.print("in a")
b.transfer()
System.print("back in a")
main.transfer()
}
b = Fiber.new {
System.print("in b")
a.transfer()
System.print("back in b")
}
a.transfer()
System.print("done")
// Output:
// in a
// in b
// back in a
// done</ code ></ pre >
2026-01-26 05:12:14 +01:00
{% endblock %}