Initial Structured Concurrency - #54
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What type of PR is this? (check all applicable)
Description
This PR introduces a new, higher-level
fibersfacade, refactors the existing fiber runtime into a clearer layering, and wires in a structured concurrency model based on scopes. It also updates all examples and tests to use the new API and directory layout.High-level summary:
Introduce
src/fibers.luaas the top-level public API, wrapping:fibers.runtime(scheduler and raw fibers)fibers.op(CML-style event engine)fibers.scope(structured concurrency)sleep,channel, etc.)Replace the old
fibers.fibermodule with a newfibers.runtimemodule.Add scope-aware event performance via
fibers.performer.Move library sources under
src/and update examples/tests accordingly.Add dedicated tests for the new scope and performer semantics and adjust existing tests for the revised
opbehaviour.More detailed breakdown:
New top-level facade and runtime
Add
src/fibers.lua:fibers.run(main_fn, ...)as the preferred entry point.fibers.spawn(fn, ...)which spawns a fiber under the current scope.fibers.sleep,fibers.channel, and CML combinators (choice,guard,with_nack,always,never).fibers.performas the scope-aware event performer.fibers.nowas a thin wrapper over the runtime clock.Introduce
src/fibers/runtime.lua:Encapsulates the global scheduler (
sched.new()), current fiber tracking, and basic operations:spawn,suspend,yield,now,stop,main.Retains the low-level
Fiberclass and error handling (with traceback capture and process exit on unhandled fiber error).Exposes
current_schedulerandcurrent_fiber()for internal consumers (e.g. scopes, cond, context).Structured concurrency and performer
Add
src/fibers/scope.lua:Implements a tree of
Scopeobjects with:scope.root()andscope.current().scope.run(body_fn, ...)to run a function in a fresh child scope, wait for child fibers, run defers, and then either:Scope:spawn(fn, ...)to spawn child fibers attached to the scope, with fail-fast semantics (child exceptions cause the scope to fail and cancel).Scope status management:
"running" | "ok" | "failed" | "cancelled", including primary error and additional failures.Scope defers (
Scope:defer(handler)), executed in LIFO order at scope exit.Cancellation and join events:
Scope:done_ev()(fires on cancellation/failure, returns reason).Scope:join_ev()(fires on terminal status, returns(status, error)).Event wrapping:
Scope:run_ev(ev)wraps an event so that scope cancellation races againstev.Scope:sync(ev)performs the wrapped event under this policy.Maintains a weak mapping from
Fiberobjects to their currentScope, plus a process-wideglobal_scopefor non-fiber contexts.Add
src/fibers/performer.lua:Provides
performer.perform(ev)as the scope-aware synchronisation primitive:s:sync(ev).op.perform_raw(ev)when no scope is active.Used throughout the primitives instead of calling
op.performdirectly.Add
scope.with_ev(build_ev):Exposes scopes as first-class events.
Returns an
Eventthat:Creates a child scope of
scope.current().Installs it as current (per fiber or globally) for
build_ev(child)and the lifetime of the event.Ensures, via
op.bracket, that on commit, abort, or error:join_evis signalled.Propagates the inner event’s result or error to the caller.
Event engine (
fibers.op) changesRename and clarify primitive event construction:
new_base_opwithnew_primitive(wrap_fn, try_fn, block_fn)and update callers.new_primitiveandperform_rawin the module API.Extend and refine event semantics:
Unified
Eventtype with kinds:prim,choice,guard,with_nack,wrap,abort,wrap_handler.always(...)now creates a primitive event that is always ready, returning arbitrary values.never()returns a primitive event that never becomes ready.Event:wrap(f)andEvent:on_abort(f)remain as combinators over the event AST.Introduce
Event:wrap_handler(handler)andEvent:finally(cleanup)to support CML-style exception handling and “finally” semantics on post-synchronisation actions.with_nackandabortnow share a unified condition (new_cond) mechanism, with:wait_op) for nack/on_abort paths;signal()waking waiters in the current turn and running optional abort handlers.Rework compilation and commit logic:
compile_event(ev)compiles the AST into primitive “leaves”, each capturing:try_fn,block_fn, a composedwrap(withwrap_handlerchain embedded), and a list of active nacks.perform(ev)now:try_ready;runtime.suspend) withblock_choice_op;wrapfunction identity;trigger_nacks.Introduce
Event:or_else(fallback_thunk):selfonce; if no leaf is ready, fires all nacks forselfand evaluatesfallback_thunk()as the result.guardandalways(...)to ensure clear “try once and decide” semantics.Primitives updated to the new runtime and performer
All primitives and ancillary modules are updated to depend on
fibers.runtimeandfibers.performerrather than the removedfibers.fiberand olderop.perform/new_base_op:src/fibers/alarm.lua:runtime.current_schedulerfor scheduling absolute and buffered wakeups.op.new_primitiveand performs them viafibers.performer.src/fibers/channel.lua:put_opandget_opnow useop.new_primitive.performer.perform.src/fibers/cond.lua:performer.performforCond:wait().src/fibers/context.lua:fiberusage withruntimeandperformer.base_context:done_op()now usesop.new_primitive.runtime.current_scheduler:schedule_at_time.cancel_context:done()convenience method that performsdone_op().src/fibers/pollio.lua:runtime.current_scheduleras a task source.op.new_primitive.performer.perform.src/fibers/sleep.lua:op.new_primitivewithruntime.now()and scheduler integration.sleep.sleepusesperformer.perform.src/fibers/stream.luaandsrc/fibers/stream/file.lua:op.new_primitiveevents.performer.perform.src/fibers/waitgroup.lua:op.new_primitiveandperformer.perform.Other modules (
epoll,timer,queue, syscall helpers, etc.) are moved undersrc/fibers/...with minimal or no behavioural changes, aside from imports.Examples updated to use
fibersfacadeAll example programmes have been updated to:
package.pathat../../src/?.luainstead of the old top-levelfibers/directory.local fibers = require 'fibers'instead offibers.fiber.fibers.run(main)as the top-level entry point rather than manual combinations offiber.spawn,fiber.main, andfiber.stop.fibers.spawninstead offiber.spawn.fibers.perform(orrequire 'fibers.performer'.perform) instead ofop.performwhere appropriate.This affects:
examples/1-basic-usage/1-fibers.luathrough10-luacat.luaexamples/2-lua-http/cq-http-fiber-test.luaWhere relevant, examples that previously called
fiber.yield()have been moved to usesleep.sleep(0)to maintain non-blocking behaviour under the new facade.Tests updated and extended
The test suite has been updated to reflect the new module layout and semantics:
All
package.pathentries now target../src/?.lua.Imports of
fibers.fiberare replaced withfibers.runtimeorfibersas appropriate:tests/test_runtime.lua(renamed fromtest_fiber.lua) now testsfibers.runtimedirectly.perform,choice) now import fromfibersorfibers.performer.tests/test_op.lua:Updated to use
performer.performandruntimerather thanfiber.Adjusts expectations around
Event:or_elseto reflect the new “single non-blocking attempt” semantics:tests/test_cond.lua:runtime.spawnandruntime.current_scheduler:run.task:run()).tests/test_context.lua:fibersfacade andperformer.perform.fiber.yield.tests/test_alarm.lua,tests/test_channel.lua,tests/test_pollio.lua,tests/test_stream-file.lua,tests/test_waitgroup.lua, etc.:fibers,fibers.runtime,fibers.performer).fibers.run(main)for tests that rely on the full scheduler/fiber environment.New
tests/test_scope.lua:Adds comprehensive coverage for scope behaviour, including:
scope.current()behaviour inside and outside fibers.children()invariants.scope.runsuccess, failure, and explicit cancellation.Scope:deferordering, including on failure.Scope:sync, event failures, and scope status.join_evanddone_evon failed and cancelled scopes.tests/test.lua:Module list updated to cover new layout and components:
fiberwithruntime, and addsscope.Screenshots/Recordings
Not applicable. No user-facing visual changes.
Manual test
Manual test description
Not performed as part of this change description. The intended manual verification is:
Run the full test suite under
tests/against the newsrc/layout.Run the example programmes under
examples/1-basic-usageandexamples/2-lua-httpto confirm that:fibers.run(main)starts and stops the scheduler as expected.Added tests?
New tests:
tests/test_scope.luafor the structured concurrency API.Existing tests have been updated to:
fibersfacade,fibers.runtime, andfibers.performer;fibers.opsemantics, including the newor_elsebehaviour and nack handling.Added to documentation?
No documentation files were updated in this PR. A follow-up may be appropriate to:
fibersfacade as the primary public entry point;scope.run,Scope:spawn,Scope:sync,scope.with_ev);fibers.performer.performversusop.perform_raw.[optional] Are there any post-deployment tasks we need to perform?
Update any external consumers that:
fibers.fiberdirectly, to move to eitherfibers(preferred) orfibers.runtimefor low-level access;fibers/*.lualayout, to usesrc/fibers/...or the packaged module path.Review and, if necessary, update any external documentation and example code to use
fibers.run/fibers.spawnand the scope-aware performer.