nupp.suspension

Waiting without blocking the thread.

A suspension-aware call parks the frame it is in rather than stopping the process, and whoever installed a handler decides what runs meanwhile -- an event loop, a poll, the next frame.

local suspension = require("nupp.suspension")

local function readLine(reader: Reader): string
    return suspension.suspend("example.readLine", function(
        resume: function(string),
        context: suspension.Context
    ): function()
        const subscription = reader:onLine(resume)

        return function(): nil
            subscription:release()
        end
    end)
end

suspend parks on something that will be ready later, source registers the pump that makes it ready, and poll drives every registered pump once. handled says whether a handler is listening; without one a park drives those pumps on this thread and blocks after all. canSuspend returns whether a park would be permitted here at all, and create makes a coroutine that inherits the handler.

Installing a handler, and driving the parks it accepts, is the other side of this: nupp.suspension.host. A library needs none of it.

This is the layer underneath. Somewhere to put children discovered over time, with one answer for failure and cancellation, is nupp.tasks; real parallelism is nupp.workers. Come here directly to make a library waitable, or to write a combinator neither of those has.

An arbitrary blocking Lua or C call still blocks the thread. A library opts in by parking through this module; nothing intercepts calls that do not.

See Suspension.

Submodules

ModuleDescription
nupp.suspension.cancellationCanonical task-cancellation identity within one Lua state.
nupp.suspension.hostDeciding what runs while something waits.
nupp.suspension.spiShared interfaces for scheduler integration.

Module contents

Types

TypeKindDescription
ContextrecordWhat a library is handed while it subscribes.
SourcerecordA readiness pump, and the handle that owns its lifetime.

Types#

Contextrecord#

record Context ...

What a library is handed while it subscribes.

Members

NameKindDescription
sourcemethodRegisters a source associated with this subscription, as nupp.suspension.spi.Provider.source registers one outside any.
usesmethodAssociates an existing source with this wait without changing its identity.

sourcemethod#

source: function(
    self: Context,
    name: string,
    order: integer,
    poll: function(): integer,
    wait: (function(integer): integer)?,
    active: (function(source: Source): boolean)?
): Source

Registers a source associated with this subscription, as nupp.suspension.spi.Provider.source registers one outside any.

Arguments
NameTypeDescription
selfContext
namestring
orderinteger
pollfunction(): integer
wait(function(integer): integer)?
active(function(source: Source): boolean)?
Returns
TypeDescription
Source

usesmethod#

uses: function(self: Context, source: Source): nil

Associates an existing source with this wait without changing its identity.

Arguments
NameTypeDescription
selfContext
sourceSource
Returns
TypeDescription
nil

Sourcerecord#

record Source ...

A readiness pump, and the handle that owns its lifetime.

Members

NameKindDescription
releasemethodUnregisters this source without suspending; repeated calls are harmless.
namefieldDiagnostic identity of the readiness source.
orderfieldWhere in a poll pass this source runs; smaller orders are polled first.
waitfieldOptional bounded blocking waiter receiving milliseconds and returning progress.
activefieldOptional predicate deciding whether the source has outstanding work.

releasemethod#

release: @nosuspend function(self: Source): nil

Unregisters this source without suspending; repeated calls are harmless. The provider supplies it with the registration.

Arguments
NameTypeDescription
selfSource
Returns
TypeDescription
nil

namefield#

name: string

Diagnostic identity of the readiness source.

orderfield#

order: integer

Where in a poll pass this source runs; smaller orders are polled first.

waitfield#

wait: (function(integer): integer)?

Optional bounded blocking waiter receiving milliseconds and returning progress.

activefield#

active: (function(source: Source): boolean)?

Optional predicate deciding whether the source has outstanding work.