Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 19 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ local function fibonacci(c, quit)
local x, y = 0, 1
local done = false
repeat
op.choice(
c:put_op(x):wrap(function(value)
x, y = y, x+y
end),
quit:get_op():wrap(function(value)
print("quit")
done = true
end)
):perform()
perform(
choice(
c:put_op(x):wrap(function(value)
x, y = y, x+y
end),
quit:get_op():wrap(function(value)
print("quit")
done = true
end)
)
)
until done
end

Expand All @@ -35,7 +37,7 @@ end)
fiber.main()
```

Ported from the Snabb Project's [`fibers`](https://github.com/snabbco/snabb/tree/master/src/lib/fibers) and [`streams`](https://github.com/snabbco/snabb/tree/master/src/lib/stream) libraries, written by
Ported from the Snabb Project's [`fibers`](https://github.com/snabbco/snabb/tree/master/src/lib/fibers) and [`streams`](https://github.com/snabbco/snabb/tree/master/src/lib/stream) libraries, written by
Andy Wingo as an implementation of Reppy et al's Concurrent ML(CML), and a fibers-based reimplementation of Lua's streams enabling smooth non-blocking access to files and sockets.

Inspired by Andy's [blog post](https://wingolog.org/archives/2018/05/16/lightweight-concurrency-in-lua) introducing fibers on Lua
Expand All @@ -55,7 +57,7 @@ with the following points to bear in mind:
## Installation

This is a pure Lua (with FFI) module with the following dependencies:
- lua-posix (for micro/nano timing and sleeping options, for forking and
- lua-posix (for micro/nano timing and sleeping options, for forking and
other syscall operations)
- libffi and lua-cffi (if not using LuaJIT)
- lua-bit32 (if not using LuaJIT)
Expand All @@ -76,7 +78,7 @@ These dependencies will be installed in a VScode devcontainer automatically. To

### Installation with LuaJIT on OpenWRT

This is the simplest set up for running on OpenWRT.
This is the simplest set up for running on OpenWRT.

`opkg update; opkg install luajit; opkg install luaposix`

Expand Down Expand Up @@ -111,15 +113,15 @@ This library implements a simple version of Concurrent ML(CML), and provides pri

## Progress

All of the Snabb 'fibers' modules the following have so far been ported and
tested. All of Snabb's 'stream' module has also been ported, which can do
non-blocking reads and writes from file descriptors using familiar `line`
and `all` approaches.
All of the Snabb 'fibers' modules the following have so far been ported and
tested. All of Snabb's 'stream' module has also been ported, which can do
non-blocking reads and writes from file descriptors using familiar `line`
and `all` approaches.

We use the `cffi` module to port Wingo's `luajit` C ffi based
buffers implementation in a way that will work across multiple architectures, as
Lua versions of these buffers would be inefficient and lead to allocation and
garbage collection without a substantial investment of time.
garbage collection without a substantial investment of time.

## Structured concurrency

Expand Down
9 changes: 6 additions & 3 deletions examples/1-basic-usage/4-choices.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,22 @@ local fiber = require 'fibers.fiber'
local channel = require 'fibers.channel'
local op = require 'fibers.op'

local perform, choice = op.perform, op.choice

local function fibonacci(c, quit)
local x, y = 0, 1
local done = false
repeat
op.choice(
local task = choice(
c:put_op(x):wrap(function()
x, y = y, x+y
end),
quit:get_op():wrap(function()
print("quit")
done = true
end)
):perform()
)
perform(task)
until done
end

Expand All @@ -41,4 +44,4 @@ fiber.spawn(function()
fiber.stop()
end)

fiber.main()
fiber.main()
9 changes: 6 additions & 3 deletions examples/1-basic-usage/5-choices-alt.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ local channel = require 'fibers.channel'
local sleep = require 'fibers.sleep'
local op = require 'fibers.op'

local perform, choice = op.perform, op.choice

-- time.After() is a Go library function
local function after(t)
local chan = channel.new()
Expand Down Expand Up @@ -38,21 +40,22 @@ fiber.spawn(function()
local boom = after(0.5)
local done = false
repeat
op.choice(
local task = choice(
ticker:get_op():wrap(function()
print("tick.")
end),
boom:get_op():wrap(function()
print("BOOM!")
done = true
end)
):perform_alt(function()
):or_else(function()
print(" .")
sleep.sleep(0.05)
end)
perform(task)
until done

fiber.stop()
end)

fiber.main()
fiber.main()
11 changes: 7 additions & 4 deletions examples/1-basic-usage/6-choices-adv.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ local cond = require 'fibers.cond'
local op = require 'fibers.op'
local sc = require 'fibers.utils.syscall'

local perform, choice = op.perform, op.choice

require("fibers.pollio").install_poll_io_handler()

-- Set up the queues, channel and condition variable
Expand Down Expand Up @@ -60,8 +62,8 @@ fiber.spawn(function()
socket.socket(sc.AF_UNIX, sc.SOCK_STREAM, 0):listen_unix(sockname)
local sock = socket.connect_unix(sockname)
while true do
-- Use op.choice to handle multiple potentially blocking actions
op.choice(
-- Use choice to handle multiple potentially blocking actions
local task = choice(
data_q:get_op():wrap(function(value)
print("data received - writing to socket")
sock:write(value .. "\n")
Expand All @@ -79,8 +81,9 @@ fiber.spawn(function()
sleep.sleep_op(0.5):wrap(function()
print("yawn - nothing happening")
end)
):perform()
)
perform(task)
end
end)

fiber.main()
fiber.main()
8 changes: 4 additions & 4 deletions examples/1-basic-usage/context-examples.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ local context = require 'fibers.context'
local sleep = require 'fibers.sleep'
local op = require 'fibers.op'

local perform = op.perform

-- Simulated work function
local function do_work(task_name, duration)
print(task_name .. " started")
Expand All @@ -20,8 +22,7 @@ local function sub_task_1(ctx)
cancel("work_completed") -- Cancel the context (optional, as it will timeout)
end)

local done_op = deadline_ctx:done_op()
op.choice(done_op):perform() -- Wait for the context to be done
perform(deadline_ctx:done_op()) -- Wait for the context to be done
print("Sub-Task 1 status: " .. (deadline_ctx:err() or "completed"))
end

Expand All @@ -33,8 +34,7 @@ local function sub_task_2(ctx)
cancel("work_completed") -- Cancel the context when work is done
end)

local done_op = cancel_ctx:done_op()
op.choice(done_op):perform() -- Wait for the context to be done
perform(cancel_ctx:done_op()) -- Wait for the context to be done
print("Sub-Task 2 status: " .. (cancel_ctx:err() or "completed"))
end

Expand Down
17 changes: 10 additions & 7 deletions examples/2-lua-http/cq-http-fiber-test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ local sleep = require "fibers.sleep"
local cqueues = require "cqueues"
local exec = require "fibers.exec"

local perform, choice = op.perform, op.choice

print("installing poll handler")
pollio.install_poll_io_handler()

Expand All @@ -32,14 +34,15 @@ local old_step; old_step = cqueues.interpose("step", function(self, timeout)
local events = self:events()
-- messy
if events == 'r' then
pollio.fd_readable_op(self:pollfd()):perform()
perform(pollio.fd_readable_op(self:pollfd()))
elseif events == 'w' then
pollio.fd_writable_op(self:pollfd()):perform()
perform(pollio.fd_writable_op(self:pollfd()))
elseif events == 'rw' then
op.choice(
pollio.fd_readable_op(self:pollfd()),
pollio.fd_writable_op(self:pollfd())
):perform()
perform(
choice(
pollio.fd_readable_op(self:pollfd()),
pollio.fd_writable_op(self:pollfd())
))
end
return old_step(self, 0.0)
end
Expand Down Expand Up @@ -135,4 +138,4 @@ fiber.spawn(function()
end)

print("starting fibers")
fiber.main()
fiber.main()
8 changes: 5 additions & 3 deletions fibers/alarm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ local fiber = require 'fibers.fiber'
local timer = require 'fibers.timer'
local sc = require 'fibers.utils.syscall'

local perform = op.perform

local function days_in_year(y)
return y % 4 == 0 and (y % 100 ~= 0 or y % 400 == 0) and 366 or 365
end
Expand Down Expand Up @@ -240,7 +242,7 @@ end
-- Wrapper for `absolute_op` that immediately performs the operation.
-- @param t The absolute time (epoch) for the alarm.
local function wait_absolute(t)
return wait_absolute_op(t):perform()
return perform(wait_absolute_op(t))
end

--- Creates an operation for a next (relative) alarm.
Expand All @@ -260,7 +262,7 @@ end
-- @return An error if the time table is invalid.
local function wait_next(t)
local _, err = validate_next_table(t)
return err or assert(installed_alarm_handler):wait_next_op(t):perform()
return err or perform(assert(installed_alarm_handler):wait_next_op(t))
end

-- Public API
Expand All @@ -275,4 +277,4 @@ return {
wait_next = wait_next,
validate_next_table = validate_next_table,
calculate_next = calculate_next
}
}
6 changes: 4 additions & 2 deletions fibers/channel.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
local op = require 'fibers.op'
local fifo = require 'fibers.utils.fifo'

local perform = op.perform

--- Channel class
-- Represents a communication channel between fibers.
-- @type Channel
Expand Down Expand Up @@ -115,7 +117,7 @@ end
-- continue. Otherwise, block until a receiver becomes available.
-- @tparam any message The message to put into the Channel.
function Channel:put(message)
self:put_op(message):perform()
return perform(self:put_op(message))
end

--- Get a message from the Channel.
Expand All @@ -125,7 +127,7 @@ end
-- available.
-- @treturn any The message retrieved from the Channel.
function Channel:get()
return self:get_op():perform()
return perform(self:get_op())
end

--- @export
Expand Down
4 changes: 3 additions & 1 deletion fibers/cond.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

local op = require 'fibers.op'

local perform = op.perform

local Cond = {}
Cond.__index = Cond

Expand All @@ -24,7 +26,7 @@ end

--- Put the fiber into a wait state on the condition variable.
function Cond:wait()
return self:wait_op():perform()
return perform(self:wait_op())
end

--- Wake up all fibers that are waiting on this condition variable.
Expand Down
8 changes: 5 additions & 3 deletions fibers/exec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ local op = require 'fibers.op'
local buffer = require 'string.buffer'
local sc = require 'fibers.utils.syscall'

local perform, choice = op.perform, op.choice

local io_mappings = {
stdin = sc.STDIN_FILENO,
stdout = sc.STDOUT_FILENO,
Expand Down Expand Up @@ -106,7 +108,7 @@ function Cmd:_output_collector(pipes)
ops[#ops + 1] = self.ctx:done_op():wrap(close_pipes)
end

op.choice(unpack(ops)):perform()
perform(choice(unpack(ops)))
end

return buf:tostring(), self:wait()
Expand Down Expand Up @@ -228,11 +230,11 @@ function Cmd:wait()
if self.ctx then
ops[#ops + 1] = self.ctx:done_op():wrap(function()
self:kill()
pollio.fd_readable_op(self.process.pidfd):perform()
perform(pollio.fd_readable_op(self.process.pidfd))
end)
end

op.choice(unpack(ops)):perform()
perform(choice(unpack(ops)))
local _, _, status = sc.waitpid(self.process.pid)
self.process.state = status
sc.close(self.process.pidfd)
Expand Down
Loading