mirror of
https://github.com/rayaman/multi.git
synced 2026-09-05 07:27:35 -04:00
1495 lines
37 KiB
Markdown
1495 lines
37 KiB
Markdown
# multi — Lua Cooperative Multitasking Library
|
|
### Version 16.3.0 · MIT License · by Ryan Ward
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Overview](#overview)
|
|
2. [Core Concepts](#core-concepts)
|
|
3. [Getting Started](#getting-started)
|
|
4. [The Main Loop](#the-main-loop)
|
|
5. [Task Types (Actors)](#task-types-actors)
|
|
- [newLoop](#newloop)
|
|
- [newTLoop](#newtloop)
|
|
- [newAlarm](#newalarm)
|
|
- [newStep](#newstep)
|
|
- [newTStep](#newtstep)
|
|
- [newEvent](#newevent)
|
|
- [newUpdater](#newupdater)
|
|
- [newTimer](#newtimer)
|
|
- [newTimeout](#newtimeout)
|
|
6. [Threads](#threads)
|
|
- [thread.newThread](#threadnewthread)
|
|
- [thread.sleep / thread.hold](#threadsleep--threadhold)
|
|
- [thread.yield / thread.skip](#threadyield--threadskip)
|
|
- [thread.holdFor / thread.holdWithin](#threadholdfor--threadholdwithin)
|
|
- [thread.newISOThread](#threadnewisothreads)
|
|
- [thread.newFunction](#threadnewfunction)
|
|
7. [Connections (Events)](#connections-events)
|
|
- [newConnection](#newconnection)
|
|
- [Fire / Connect / Unconnect](#fire--connect--unconnect)
|
|
- [Connection Operators](#connection-operators)
|
|
- [Destroying Connections](#destroying-connections)
|
|
8. [Processors](#processors)
|
|
- [newProcessor](#newprocessor)
|
|
- [Processor Options](#processor-options)
|
|
- [Running a Processor](#running-a-processor)
|
|
9. [Priority System](#priority-system)
|
|
10. [Services](#services)
|
|
11. [Tasks (Deferred Work)](#tasks-deferred-work)
|
|
12. [Scheduled Jobs](#scheduled-jobs)
|
|
13. [Global Variables & Thread Communication](#global-variables--thread-communication)
|
|
14. [Type System](#type-system)
|
|
15. [Utility Functions](#utility-functions)
|
|
16. [Settings & Initialization](#settings--initialization)
|
|
17. [System Events](#system-events)
|
|
18. [UUID Utilities](#uuid-utilities)
|
|
19. [Advanced Patterns](#advanced-patterns)
|
|
20. [Quick Reference Card](#quick-reference-card)
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
`multi` is a cooperative multitasking library for Lua. It provides a structured event loop, coroutine-based threads, typed connections (event emitters), and a rich set of timer and scheduling primitives — all without requiring OS threads or external dependencies.
|
|
|
|
The library is built around a single shared **main loop** that drives every actor (loop, alarm, step, thread, etc.) in turn. Because Lua is single-threaded, all concurrency is *cooperative*: tasks must yield control to let other tasks run.
|
|
|
|
**Key design principles:**
|
|
|
|
- All objects share a common interface (Pause, Resume, Destroy, setPriority, etc.).
|
|
- Connections decouple event producers from consumers.
|
|
- Threads are coroutines managed by the scheduler; `thread.sleep` and `thread.hold` yield without blocking the loop.
|
|
- Processors are isolated sub-schedulers that can be run inside threads or independently.
|
|
|
|
---
|
|
|
|
## Core Concepts
|
|
|
|
| Concept | Description |
|
|
|---|---|
|
|
| **Actor** | Any object placed in the main loop that has an `Act()` method (loop, alarm, step, etc.). |
|
|
| **Connection** | An event channel. Producers call `:Fire(...)`, consumers call `:Connect(func)`. |
|
|
| **Thread** | A coroutine managed by the scheduler. Uses `thread.sleep` / `thread.hold` to yield. |
|
|
| **Processor** | An isolated scheduler with its own actor list and thread pool. |
|
|
| **Priority** | A numeric weight controlling how often an actor is executed in priority-mode mainloops. |
|
|
| **Task** | A one-shot deferred function queued via `:newTask(func)`. |
|
|
|
|
---
|
|
|
|
## Getting Started
|
|
|
|
```lua
|
|
local multi, thread = require("multi"):init()
|
|
|
|
-- Create a loop that fires every iteration
|
|
multi:newLoop(function(self, elapsed)
|
|
print("Elapsed:", elapsed)
|
|
end)
|
|
|
|
-- Create a timed loop that fires every 1 second
|
|
multi:newTLoop(function(self, ticks)
|
|
print("Tick:", ticks)
|
|
end, 1)
|
|
|
|
-- Start the scheduler (blocks until multi.Stop() is called)
|
|
multi:mainloop()
|
|
```
|
|
|
|
### Initializing with Settings
|
|
|
|
```lua
|
|
local multi, thread = require("multi"):init({
|
|
print = true, -- enable multi.print() output
|
|
warn = true, -- enable multi.warn() output
|
|
debugging = false, -- enable multi.debug() output + debug manager
|
|
error = false, -- if true, hard-errors on runtime errors
|
|
priority = false, -- enable priority-based scheduling
|
|
findopt = false, -- enable optimization hints
|
|
})
|
|
```
|
|
|
|
`init()` returns `multi` and `thread` — always destructure both.
|
|
|
|
---
|
|
|
|
## The Main Loop
|
|
|
|
The main loop drives every actor. There are two variants:
|
|
|
|
### `multi:mainloop()`
|
|
|
|
Standard round-robin scheduler. Every actor is visited once per loop iteration in reverse insertion order.
|
|
|
|
```lua
|
|
multi:mainloop()
|
|
```
|
|
|
|
### `multi:p_mainloop()`
|
|
|
|
Priority-based scheduler. Actors with higher priority are executed more frequently. Enable it via `init({ priority = true })`.
|
|
|
|
### `multi:uManager(dt?)`
|
|
|
|
Runs a **single pass** of the loop manually. Useful when embedding `multi` inside another game loop or framework.
|
|
|
|
```lua
|
|
-- Inside LÖVE2D update callback:
|
|
function love.update(dt)
|
|
multi:uManager(dt)
|
|
end
|
|
```
|
|
|
|
### `multi.Stop()`
|
|
|
|
Stops the main loop.
|
|
|
|
```lua
|
|
multi.Stop()
|
|
```
|
|
|
|
---
|
|
|
|
## Task Types (Actors)
|
|
|
|
All actors are created on `multi` or on a **processor**. Every actor shares these common methods:
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `:Pause()` | Suspends the actor (its `Act()` is replaced with a no-op). |
|
|
| `:Resume()` | Resumes a paused actor. |
|
|
| `:Destroy()` | Removes the actor from the loop permanently. |
|
|
| `:setPriority(s)` | Sets the priority. Accepts string or number (see [Priority System](#priority-system)). |
|
|
| `:setName(name)` | Sets a human-readable name. |
|
|
| `:isPaused()` | Returns `true` if the actor is paused. |
|
|
| `:isActive()` | Returns `true` if the actor is active. |
|
|
|
|
---
|
|
|
|
### newLoop
|
|
|
|
A loop that fires **every scheduler iteration**.
|
|
|
|
```lua
|
|
local loop = multi:newLoop(func?, notime?)
|
|
```
|
|
|
|
| Parameter | Type | Default | Description |
|
|
|---|---|---|---|
|
|
| `func` | function | nil | Connected to `OnLoop` immediately |
|
|
| `notime` | boolean | false | If `true`, `elapsed` is always `nil` |
|
|
|
|
**Callback signature:** `function(self, elapsed, dt)`
|
|
|
|
- `self` — the loop object
|
|
- `elapsed` — seconds since the loop was created (or `nil` if `notime = true`)
|
|
- `dt` — delta time passed from the scheduler
|
|
|
|
**Connections:**
|
|
|
|
| Connection | Fires When |
|
|
|---|---|
|
|
| `OnLoop` | Every scheduler iteration |
|
|
|
|
```lua
|
|
local loop = multi:newLoop(function(self, elapsed, dt)
|
|
if elapsed > 5 then
|
|
print("5 seconds have passed!")
|
|
self:Destroy()
|
|
end
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
### newTLoop
|
|
|
|
A **timed loop** that fires at a fixed interval.
|
|
|
|
```lua
|
|
local tloop = multi:newTLoop(func?, interval?)
|
|
```
|
|
|
|
| Parameter | Type | Default | Description |
|
|
|---|---|---|---|
|
|
| `func` | function | nil | Connected to `OnLoop` immediately |
|
|
| `interval` | number | 0 | Seconds between firings |
|
|
|
|
**Callback signature:** `function(self, ticks, dt)`
|
|
|
|
- `ticks` — total number of times `OnLoop` has fired
|
|
|
|
**Methods:**
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `:Set(n)` | Change the interval |
|
|
| `:Pause()` | Pauses and freezes the internal timer |
|
|
| `:Resume()` | Resumes and unfreezes the internal timer |
|
|
|
|
```lua
|
|
-- Fire every 2 seconds
|
|
multi:newTLoop(function(self, ticks)
|
|
print("Tick #" .. ticks)
|
|
if ticks >= 10 then self:Destroy() end
|
|
end, 2)
|
|
```
|
|
|
|
---
|
|
|
|
### newAlarm
|
|
|
|
A **one-shot timer** that fires after a delay and then pauses itself.
|
|
|
|
```lua
|
|
local alarm = multi:newAlarm(seconds?, func?)
|
|
```
|
|
|
|
| Parameter | Type | Default | Description |
|
|
|---|---|---|---|
|
|
| `seconds` | number | 0 | Delay in seconds |
|
|
| `func` | function | nil | Connected to `OnRing` immediately |
|
|
|
|
**Connections:**
|
|
|
|
| Connection | Fires When |
|
|
|---|---|
|
|
| `OnRing` | When the alarm expires |
|
|
|
|
**Callback signature:** `function(self, dt)`
|
|
|
|
**Methods:**
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `:Reset(n?)` | Restarts the alarm; optionally sets a new duration |
|
|
| `:Pause()` | Pauses (freezes remaining time) |
|
|
| `:Resume()` | Resumes from where it was paused |
|
|
|
|
```lua
|
|
multi:newAlarm(3, function(self)
|
|
print("3 seconds elapsed!")
|
|
self:Reset(3) -- reset for another 3 seconds
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
### newStep
|
|
|
|
A **counter** that steps through a range of values.
|
|
|
|
```lua
|
|
local step = multi:newStep(start?, reset?, count?, skip?)
|
|
```
|
|
|
|
| Parameter | Type | Default | Description |
|
|
|---|---|---|---|
|
|
| `start` | number | 1 | Starting value |
|
|
| `reset` | number | math.huge | Ending value (inclusive) |
|
|
| `count` | number | 1 | Increment per step |
|
|
| `skip` | number | 0 | Number of loop iterations to skip between steps |
|
|
|
|
**Connections:**
|
|
|
|
| Connection | Fires When |
|
|
|---|---|
|
|
| `OnStart` | When the step position is at `start` |
|
|
| `OnStep` | Every step; receives `(self, position, dt)` |
|
|
| `OnEnd` | When `position` reaches `reset` |
|
|
|
|
**Methods:**
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `:Update(start, reset, count, skip)` | Update parameters and resume |
|
|
| `:Count(n)` | Change the step increment |
|
|
| `:Break()` | Hard-stop (sets `Active = nil`) |
|
|
|
|
```lua
|
|
multi:newStep(1, 5, 1):OnStep(function(self, pos, dt)
|
|
print("Step:", pos)
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
### newTStep
|
|
|
|
A **timed step** — same as `newStep` but advances on a time interval rather than every loop iteration.
|
|
|
|
```lua
|
|
local tstep = multi:newTStep(start?, reset?, count?, interval?)
|
|
```
|
|
|
|
| Parameter | Type | Default | Description |
|
|
|---|---|---|---|
|
|
| `interval` | number | 1 | Seconds between steps |
|
|
|
|
**Methods:** Same as `newStep`, plus:
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `:Set(n)` | Change the interval |
|
|
| `:Reset(n?)` | Restart and optionally update interval |
|
|
|
|
```lua
|
|
-- Count from 1 to 10, one step per second
|
|
multi:newTStep(1, 10, 1, 1):OnStep(function(self, pos)
|
|
print("Position:", pos)
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
### newEvent
|
|
|
|
An actor that polls a function and fires when it returns a truthy value.
|
|
|
|
```lua
|
|
local event = multi:newEvent(task?, func?)
|
|
```
|
|
|
|
| Parameter | Type | Description |
|
|
|---|---|---|
|
|
| `task` | function | Polled every iteration; should return a value when "done" |
|
|
| `func` | function | Connected to `OnEvent` immediately |
|
|
|
|
**Connections:**
|
|
|
|
| Connection | Fires When |
|
|
|---|---|
|
|
| `OnEvent` | When `task()` returns a truthy value |
|
|
|
|
**Callback signature:** `function(self, dt)`
|
|
The return value of `task()` is stored in `self.returns`.
|
|
|
|
**Methods:**
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `:SetTask(func)` | Replace the polling function |
|
|
|
|
```lua
|
|
local flag = false
|
|
|
|
multi:newEvent(function() return flag end, function(self)
|
|
print("Flag was set!")
|
|
end)
|
|
|
|
-- Somewhere else in the code:
|
|
flag = true
|
|
```
|
|
|
|
---
|
|
|
|
### newUpdater
|
|
|
|
An actor that fires on every N-th loop iteration (frame skip).
|
|
|
|
```lua
|
|
local updater = multi:newUpdater(skip?, func?)
|
|
```
|
|
|
|
| Parameter | Type | Default | Description |
|
|
|---|---|---|---|
|
|
| `skip` | number | 1 | Fire every `skip` iterations |
|
|
| `func` | function | nil | Connected to `OnUpdate` |
|
|
|
|
**Connections:**
|
|
|
|
| Connection | Fires When |
|
|
|---|---|
|
|
| `OnUpdate` | Every `skip` iterations |
|
|
|
|
**Methods:**
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `:SetSkip(n)` | Change the skip interval |
|
|
|
|
```lua
|
|
-- Fire every 10 iterations
|
|
multi:newUpdater(10, function(self, dt)
|
|
print("Every 10 ticks")
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
### newTimer
|
|
|
|
A **utility timer** object (not an actor — does not run in the main loop).
|
|
|
|
```lua
|
|
local timer = multi:newTimer()
|
|
```
|
|
|
|
**Methods:**
|
|
|
|
| Method | Returns | Description |
|
|
|---|---|---|
|
|
| `:Start()` | self | Start (or restart) the timer |
|
|
| `:Get()` | number | Elapsed seconds |
|
|
| `:Pause()` | self | Freeze elapsed time |
|
|
| `:Resume()` | self | Continue from frozen time |
|
|
| `:isPaused()` | bool | Whether the timer is paused |
|
|
|
|
`:Reset()` is an alias for `:Start()`.
|
|
|
|
```lua
|
|
local t = multi:newTimer()
|
|
t:Start()
|
|
|
|
multi:newLoop(function()
|
|
if t:Get() > 5 then
|
|
print("5 seconds passed")
|
|
t:Stop()
|
|
end
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
### newTimeout
|
|
|
|
Creates a one-shot connection that fires after a delay, then destroys the receiving object.
|
|
|
|
```lua
|
|
local timeout = multi:newTimeout(seconds)
|
|
```
|
|
|
|
Returns a connection-modifier function. Used in combination with connection chaining.
|
|
|
|
```lua
|
|
-- This pattern pauses self after 5 seconds
|
|
multi:newTimeout(5)(function(self)
|
|
print("Timed out!")
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
## Threads
|
|
|
|
Threads are coroutines managed by the scheduler. They are created with `thread:newThread` and run cooperatively alongside all other actors.
|
|
|
|
### thread.newThread
|
|
|
|
```lua
|
|
local th = thread:newThread(name?, func, ...)
|
|
```
|
|
|
|
| Parameter | Type | Description |
|
|
|---|---|---|
|
|
| `name` | string | Optional display name |
|
|
| `func` | function | The coroutine body |
|
|
| `...` | any | Arguments passed to `func` on first resume |
|
|
|
|
**Connections on the returned thread:**
|
|
|
|
| Connection | Fires When |
|
|
|---|---|
|
|
| `OnDeath` | Thread function returns normally; receives return values |
|
|
| `OnError` | Thread function throws an error; receives `(self, errorMsg)` |
|
|
|
|
**Methods:**
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `:Pause()` | Pause the thread at its next yield point |
|
|
| `:Resume()` | Resume a paused thread |
|
|
| `:Kill()` | Kill the thread at its next yield point |
|
|
| `:Sleep(n)` | Request the thread sleep for `n` seconds at next yield |
|
|
| `:Hold(func, opt?)` | Request the thread hold until `func` returns true |
|
|
| `:getName()` | Returns the thread's name |
|
|
| `:isPaused()` | Returns `true` if paused |
|
|
|
|
```lua
|
|
thread:newThread("MyThread", function(a, b)
|
|
print("Got:", a, b)
|
|
thread.sleep(1)
|
|
print("1 second later")
|
|
return "done"
|
|
end, "hello", "world"):OnDeath(function(result)
|
|
print("Result:", result) -- "done"
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
### thread.sleep / thread.hold
|
|
|
|
These are the primary yield mechanisms **inside** threads.
|
|
|
|
#### `thread.sleep(seconds)`
|
|
|
|
Yields the thread for a fixed duration.
|
|
|
|
```lua
|
|
thread:newThread("Waiter", function()
|
|
print("Before sleep")
|
|
thread.sleep(2)
|
|
print("After 2 seconds")
|
|
end)
|
|
```
|
|
|
|
#### `thread.hold(condition, opts?)`
|
|
|
|
Yields until a condition is true. The condition is polled every scheduler pass.
|
|
|
|
```lua
|
|
-- Hold until a flag is set
|
|
local ready = false
|
|
thread:newThread("Holder", function()
|
|
thread.hold(function() return ready end)
|
|
print("Ready!")
|
|
end)
|
|
|
|
-- Hold until a connection fires
|
|
local conn = multi:newConnection()
|
|
thread:newThread("ConnHolder", function()
|
|
local value = thread.hold(conn)
|
|
print("Connection fired with:", value)
|
|
end)
|
|
|
|
-- Hold with a numeric timeout
|
|
thread:newThread("WithTimeout", function()
|
|
local result, timeout = thread.hold(function()
|
|
return someCondition()
|
|
end, { sleep = 5 })
|
|
if multi.isTimeout(timeout) then
|
|
print("Timed out!")
|
|
end
|
|
end)
|
|
```
|
|
|
|
**`opts` table fields:**
|
|
|
|
| Field | Description |
|
|
|---|---|
|
|
| `sleep` | Max seconds to wait before returning `nil, TIMEOUT` |
|
|
| `cycles` | Max loop iterations to wait before returning `nil, TIMEOUT` |
|
|
| `skip` | Skip N iterations between condition checks |
|
|
| `interval` | Minimum seconds between condition checks |
|
|
|
|
---
|
|
|
|
### thread.yield / thread.skip
|
|
|
|
#### `thread.yield()`
|
|
|
|
Yields for exactly one scheduler pass (minimum possible pause).
|
|
|
|
```lua
|
|
thread:newThread("Yielder", function()
|
|
for i = 1, 1000 do
|
|
doSomeWork(i)
|
|
thread.yield() -- give other threads a turn each iteration
|
|
end
|
|
end)
|
|
```
|
|
|
|
#### `thread.skip(n)`
|
|
|
|
Yields for exactly `n` scheduler passes.
|
|
|
|
```lua
|
|
thread.skip(5) -- pause for 5 iterations
|
|
```
|
|
|
|
---
|
|
|
|
### thread.holdFor / thread.holdWithin
|
|
|
|
#### `thread.holdFor(seconds, condition?)`
|
|
|
|
Hold for up to `seconds` seconds, optionally with a condition function.
|
|
|
|
```lua
|
|
thread.holdFor(3, function() return isReady() end)
|
|
```
|
|
|
|
#### `thread.holdWithin(cycles, condition?)`
|
|
|
|
Hold for up to `cycles` iterations with an optional condition.
|
|
|
|
```lua
|
|
thread.holdWithin(100, function() return isReady() end)
|
|
```
|
|
|
|
---
|
|
|
|
### thread.newISOThread
|
|
|
|
Creates an **isolated thread** with its own environment (useful for sandboxing).
|
|
|
|
```lua
|
|
local th = thread:newISOThread(name?, func, env?, ...)
|
|
```
|
|
|
|
| Parameter | Type | Description |
|
|
|---|---|---|
|
|
| `env` | table | The environment table. `thread` and `multi` are injected unless already present. |
|
|
|
|
```lua
|
|
thread:newISOThread("Isolated", function()
|
|
thread.sleep(1)
|
|
print("This runs in isolation")
|
|
end, { print = print })
|
|
```
|
|
|
|
---
|
|
|
|
### thread.newFunction
|
|
|
|
Wraps a function as a **threaded callable** — calling it spawns a thread and optionally waits for its result.
|
|
|
|
```lua
|
|
local tfunc = thread:newFunction(func, holdme?)
|
|
```
|
|
|
|
| Parameter | Type | Description |
|
|
|---|---|---|
|
|
| `func` | function | The function body |
|
|
| `holdme` | boolean | If `true`, calling the TFunc blocks until it returns |
|
|
|
|
**Returns a TFunc object.** Calling the TFunc spawns a thread and returns a handle.
|
|
|
|
**Handle methods:**
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `:wait()` | Block (or hold in a thread) until the function returns |
|
|
| `:connect(func)` | Call `func` with the return values when done |
|
|
| `.OnReturn` | Connection that fires when done |
|
|
| `.OnError` | Connection that fires on error |
|
|
| `.OnStatus` | Connection for `thread.pushStatus(...)` values |
|
|
|
|
```lua
|
|
local fetchData = thread:newFunction(function(url)
|
|
thread.sleep(1) -- simulate async work
|
|
return "data from " .. url
|
|
end)
|
|
|
|
-- Non-blocking call:
|
|
local handle = fetchData("http://example.com")
|
|
handle:connect(function(result)
|
|
print(result)
|
|
end)
|
|
|
|
-- Blocking call (holdme = true):
|
|
local fetchSync = thread:newFunction(function(url)
|
|
thread.sleep(1)
|
|
return "sync data"
|
|
end, true)
|
|
|
|
thread:newThread("caller", function()
|
|
local result = fetchSync("http://example.com")
|
|
print(result)
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
## Connections (Events)
|
|
|
|
Connections are the event system of `multi`. They decouple event producers from consumers.
|
|
|
|
### newConnection
|
|
|
|
```lua
|
|
local conn = obj:newConnection(protect?, func?, kill?)
|
|
```
|
|
|
|
| Parameter | Type | Description |
|
|
|---|---|---|
|
|
| `protect` | boolean | If `true`, each callback is wrapped in `pcall` |
|
|
| `func` | function | Immediately connected callback |
|
|
| `kill` | boolean | If `true`, callbacks are removed after first call (one-shot) |
|
|
|
|
---
|
|
|
|
### Fire / Connect / Unconnect
|
|
|
|
#### `:Fire(...)`
|
|
|
|
Broadcasts values to all connected callbacks.
|
|
|
|
```lua
|
|
local onClick = multi:newConnection()
|
|
onClick:Fire("button1", 42)
|
|
```
|
|
|
|
#### `:Connect(func, name?)`
|
|
|
|
Subscribes a function. Returns a **connection handle**.
|
|
|
|
```lua
|
|
local handle = onClick:Connect(function(button, value)
|
|
print(button, value)
|
|
end)
|
|
```
|
|
|
|
Connections can also be subscribed by calling the connection object directly:
|
|
|
|
```lua
|
|
onClick(function(button, value)
|
|
print(button, value)
|
|
end)
|
|
```
|
|
|
|
**Connection handle methods:**
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `:Unconnect()` | Remove this specific subscription |
|
|
|
|
#### `:Unconnect(handle)`
|
|
|
|
Remove a subscription using its handle.
|
|
|
|
```lua
|
|
local handle = conn:Connect(myFunc)
|
|
-- later:
|
|
conn:Unconnect(handle)
|
|
```
|
|
|
|
#### `:hasConnections()`
|
|
|
|
Returns `true` if there is at least one subscriber.
|
|
|
|
#### `:Lock(conn?)` / `:Unlock(conn?)`
|
|
|
|
Lock/unlock the connection globally (blocking all fires) or per individual subscription handle.
|
|
|
|
```lua
|
|
conn:Lock() -- block all subscribers
|
|
conn:Unlock() -- unblock
|
|
```
|
|
|
|
---
|
|
|
|
### Connection Operators
|
|
|
|
Connections support operator overloads for composing event pipelines.
|
|
|
|
#### `+` — OR (merge two connections)
|
|
|
|
```lua
|
|
local merged = connA + connB
|
|
-- fires whenever either connA or connB fires
|
|
merged(function(...) print("Either fired", ...) end)
|
|
```
|
|
|
|
#### `*` — AND (requires all to fire)
|
|
|
|
```lua
|
|
local both = connA * connB
|
|
-- fires only after both connA AND connB have fired (then resets)
|
|
both(function(...) print("Both fired") end)
|
|
```
|
|
|
|
#### `%` — Map (transform values)
|
|
|
|
```lua
|
|
local mapped = transform_func % sourceConn
|
|
-- fires with transform_func applied to each emission
|
|
mapped(function(result) print(result) end)
|
|
```
|
|
|
|
#### `/` — Filter (conditional forward)
|
|
|
|
```lua
|
|
local filtered = filter_func / sourceConn
|
|
-- fires only when filter_func returns truthy (first return value is the guard)
|
|
filtered(function(...) print("Passed filter", ...) end)
|
|
```
|
|
|
|
#### `..` — Gate / Split
|
|
|
|
```lua
|
|
-- Gate: filter_func .. targetConn
|
|
-- Forwards to targetConn only when filter_func returns true
|
|
local gated = filter_func .. targetConn
|
|
|
|
-- Split: sourceConn .. sideEffect_func
|
|
-- Fires both sourceConn and sideEffect_func; returns modified conn
|
|
local split = sourceConn .. sideEffect_func
|
|
```
|
|
|
|
#### `forwardConnection`
|
|
|
|
Forwards all emissions from one connection to another:
|
|
|
|
```lua
|
|
multi.forwardConnection(source, destination)
|
|
```
|
|
|
|
---
|
|
|
|
### Destroying Connections
|
|
|
|
```lua
|
|
conn:Destroy()
|
|
-- or
|
|
conn:destroy()
|
|
```
|
|
|
|
This:
|
|
- Removes all subscriptions
|
|
- Recursively destroys all child connections created by operators
|
|
- Nulls back-references
|
|
- Replaces methods with no-ops to prevent stale calls
|
|
|
|
---
|
|
|
|
## Processors
|
|
|
|
A processor is an isolated scheduler with its own actor list and thread pool.
|
|
|
|
### newProcessor
|
|
|
|
```lua
|
|
local proc = multi:newProcessor(name?, opts?, priority?)
|
|
```
|
|
|
|
**Simple form:**
|
|
```lua
|
|
local proc = multi:newProcessor("MyProc")
|
|
proc:Start()
|
|
```
|
|
|
|
**With options table:**
|
|
```lua
|
|
local proc = multi:newProcessor("MyProc", {
|
|
Start = false, -- start immediately?
|
|
Priority = nil, -- enable priority scheduling?
|
|
MaxThreads = -1, -- max concurrent threads (-1 = unlimited)
|
|
MaxObjects = -1, -- max actors (-1 = unlimited)
|
|
TaskDelay = 0, -- delay between task handler executions
|
|
Attach = false, -- attach as a loop to parent (auto-driven)
|
|
TaskHandler = true, -- create the built-in task handler thread
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
### Processor Options
|
|
|
|
| Option | Type | Default | Description |
|
|
|---|---|---|---|
|
|
| `Start` | boolean | false | Whether to start the processor immediately |
|
|
| `Priority` | boolean | nil | Enable priority scheduling for this processor |
|
|
| `MaxThreads` | number | -1 | Thread cap (-1 = unlimited) |
|
|
| `MaxObjects` | number | -1 | Actor cap (-1 = unlimited) |
|
|
| `TaskDelay` | number/function | 0 | Delay between deferred task executions |
|
|
| `Attach` | boolean | false | If true, drives itself as a loop inside the parent |
|
|
| `TaskHandler` | boolean | true | Whether to spawn the built-in task handler thread |
|
|
|
|
---
|
|
|
|
### Running a Processor
|
|
|
|
If `Attach = false`, you must drive the processor manually by calling `proc.run(dt?)` each frame, or inside a thread:
|
|
|
|
```lua
|
|
local proc = multi:newProcessor("Worker")
|
|
proc:Start()
|
|
|
|
thread:newThread("ProcDriver", function()
|
|
while true do
|
|
proc.run()
|
|
thread.yield()
|
|
end
|
|
end)
|
|
```
|
|
|
|
If `Attach = true (default)`, a `newLoop` is created on the parent and drives the processor automatically.
|
|
|
|
```lua
|
|
local proc = multi:newProcessor("AutoProc", { Attach = true, Start = true })
|
|
-- No extra thread needed; proc runs as part of the parent loop
|
|
```
|
|
|
|
**Key methods on a processor:**
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `proc:Start()` | Activate the processor |
|
|
| `proc:Stop()` | Deactivate (still exists, just stops ticking) |
|
|
| `proc:Destroy()` | Destroy the processor and its attached loop |
|
|
| `proc:newThread(name, func, ...)` | Spawn a thread inside this processor |
|
|
| `proc:newFunction(func, holdme?)` | Create a TFunc inside this processor |
|
|
| `proc:getThreads()` | Returns the thread list |
|
|
| `proc:getHandler()` | Returns the internal coroutine handler |
|
|
| `proc:setMaxThreads(n)` | Change the thread cap at runtime |
|
|
| `proc:setMaxObjects(n)` | Change the object cap at runtime |
|
|
| `proc:boost(n)` | Run `n` handler passes per `run()` call |
|
|
| `proc:setTaskDelay(n)` | Change the task delay at runtime |
|
|
| `proc:isActive()` | Returns `true` if active |
|
|
| `proc:getFullName()` | Returns `"parent.procName"` |
|
|
|
|
**Creating actors inside a processor:**
|
|
|
|
All standard constructors work on processors:
|
|
|
|
```lua
|
|
proc:newLoop(func)
|
|
proc:newTLoop(func, interval)
|
|
proc:newAlarm(seconds, func)
|
|
-- etc.
|
|
```
|
|
|
|
---
|
|
|
|
## Priority System
|
|
|
|
Priorities control how frequently an actor is run when `priority = true` is set in `init()` or on a processor.
|
|
|
|
### Priority Constants
|
|
|
|
| Constant | Value | Description |
|
|
|---|---|---|
|
|
| `multi.Priority_Core` | 1 | Runs every iteration (highest) |
|
|
| `multi.Priority_Very_High` | 4 | Runs every 4 iterations |
|
|
| `multi.Priority_High` | 16 | Runs every 16 iterations |
|
|
| `multi.Priority_Above_Normal` | 64 | Runs every 64 iterations |
|
|
| `multi.Priority_Normal` | 256 | Default |
|
|
| `multi.Priority_Below_Normal` | 1024 | |
|
|
| `multi.Priority_Low` | 4096 | |
|
|
| `multi.Priority_Very_Low` | 16384 | |
|
|
| `multi.Priority_Idle` | 65536 | Runs very rarely |
|
|
|
|
### Setting Priority
|
|
|
|
```lua
|
|
actor:setPriority("normal") -- by name
|
|
actor:setPriority("high")
|
|
actor:setPriority("core")
|
|
actor:setPriority(multi.Priority_High) -- by constant
|
|
```
|
|
|
|
**Accepted string shortcuts:**
|
|
|
|
`core` / `c`, `very high` / `vh`, `high` / `h`, `above` / `a`, `normal` / `n`, `below` / `b`, `low` / `l`, `very low` / `vl`, `idle` / `i`
|
|
|
|
### Resolving Priority
|
|
|
|
```lua
|
|
print(multi.PriorityResolve[multi.Priority_High]) -- "High"
|
|
```
|
|
|
|
### Resetting Priority
|
|
|
|
```lua
|
|
actor:ResetPriority() -- restore to the value set at creation
|
|
```
|
|
|
|
---
|
|
|
|
## Services
|
|
|
|
A service is a priority-managed background thread designed for long-running tasks.
|
|
|
|
```lua
|
|
local svc = multi:newService(function(self, data)
|
|
-- runs continuously while active
|
|
print("Service running, data:", data)
|
|
end)
|
|
|
|
svc.Start()
|
|
```
|
|
|
|
**Methods:**
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `svc.Start()` | Start the service |
|
|
| `svc.Stop()` | Stop and clear service data |
|
|
| `svc.Pause()` | Pause (freeze timer) |
|
|
| `svc.Resume()` | Resume |
|
|
| `svc.Destroy()` | Kill thread and stop |
|
|
| `svc.GetUpTime()` | Seconds since start |
|
|
| `svc:SetPriority(n)` | Set scheduling priority |
|
|
| `svc:SetScheme(n)` | Change the sleep/skip scheme (1, 2, or 3) |
|
|
|
|
**Connections:**
|
|
|
|
| Connection | Fires When |
|
|
|---|---|
|
|
| `OnStarted` | Service is started |
|
|
| `OnStopped` | Service is stopped |
|
|
| `OnError` | Service thread errors |
|
|
|
|
**Schemes:**
|
|
|
|
| Scheme | Behavior |
|
|
|---|---|
|
|
| 1 (default) | Uses `thread.sleep` with priority-derived delay |
|
|
| 2 | Uses `thread.skip` with priority-derived skip count |
|
|
| 3 | Not yet implemented time based scheme which bases on how long each loop of the service takes
|
|
|
|
---
|
|
|
|
## Tasks (Deferred Work)
|
|
|
|
Tasks are one-shot functions queued for execution by the processor's **Task Handler** thread.
|
|
|
|
```lua
|
|
multi:newTask(function()
|
|
print("I run asynchronously in the task queue")
|
|
end)
|
|
```
|
|
|
|
Or on a specific processor:
|
|
|
|
```lua
|
|
proc:newTask(function()
|
|
print("Task on proc")
|
|
end)
|
|
```
|
|
|
|
Tasks run in FIFO order. The built-in task handler thread processes them one at a time.
|
|
|
|
**Configuring task delay:**
|
|
|
|
```lua
|
|
multi:setTaskDelay(0.1) -- wait 0.1s between tasks
|
|
-- or a function:
|
|
multi:setTaskDelay(function() return someCondition() end)
|
|
```
|
|
|
|
---
|
|
|
|
## Scheduled Jobs
|
|
|
|
Schedule a function to run when a specific time matches. Uses `os.date` patterns.
|
|
|
|
```lua
|
|
multi:scheduleJob(timeTable, func)
|
|
```
|
|
|
|
`timeTable` is a table of `os.date("*t")` fields. The job fires whenever **all** specified fields match the current time.
|
|
|
|
```lua
|
|
-- Run at 14:30:00 every day
|
|
multi:scheduleJob({ hour = 14, min = 30, sec = 0 }, function()
|
|
print("It's 2:30 PM!")
|
|
end)
|
|
```
|
|
|
|
The scheduler checks every second using an internal thread.
|
|
|
|
---
|
|
|
|
## Global Variables & Thread Communication
|
|
|
|
Threads can communicate via a shared global variables table.
|
|
|
|
```lua
|
|
thread.set("myKey", someValue)
|
|
local v = thread.get("myKey")
|
|
```
|
|
|
|
#### `thread.waitFor(name)`
|
|
|
|
Blocks the current thread until a global variable is set.
|
|
|
|
```lua
|
|
thread:newThread("Consumer", function()
|
|
local value = thread.waitFor("resultReady")
|
|
print("Got:", value)
|
|
end)
|
|
|
|
thread:newThread("Producer", function()
|
|
thread.sleep(2)
|
|
thread.set("resultReady", 42)
|
|
end)
|
|
```
|
|
|
|
#### `thread.pushStatus(...)`
|
|
|
|
Fire the `OnStatus` connection of the current thread (or the connection thread that triggered this call).
|
|
|
|
```lua
|
|
thread:newThread("StatusPusher", function()
|
|
thread.sleep(1)
|
|
thread.pushStatus("halfway done")
|
|
thread.sleep(1)
|
|
return "final result"
|
|
end):OnStatus(function(msg)
|
|
print("Status:", msg)
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
## Type System
|
|
|
|
`multi` uses a simple string-based type registry.
|
|
|
|
### Registering Types
|
|
|
|
```lua
|
|
local myType = multi.registerType("myObject", "myObjects")
|
|
-- Returns the type string "myObject"
|
|
-- multi.$MYOBJECT is set as a constant
|
|
```
|
|
|
|
### Checking Types
|
|
|
|
```lua
|
|
obj:isType(multi.registerType("loop")) -- true if obj is a loop
|
|
multi.hasType("loop") -- returns the registered name or nil
|
|
multi.isMulitObj(obj) -- true if obj has a registered Type
|
|
```
|
|
|
|
### Built-in Types
|
|
|
|
| Type String | Access Constant |
|
|
|---|---|
|
|
| `"rootprocess"` | `multi.$ROOTPROCESS` |
|
|
| `"process"` | `multi.$PROCESS` |
|
|
| `"loop"` | `multi.$LOOP` |
|
|
| `"tloop"` | `multi.$TLOOP` |
|
|
| `"alarm"` | `multi.$ALARM` |
|
|
| `"step"` | `multi.$STEP` |
|
|
| `"tstep"` | `multi.$TSTEP` |
|
|
| `"event"` | `multi.$EVENT` |
|
|
| `"updater"` | `multi.$UPDATER` |
|
|
| `"timer"` | `multi.$TIMER` |
|
|
| `"thread"` | `multi.$THREAD` |
|
|
| `"connector"` | `multi.$CONNECTOR` |
|
|
| `"service"` | `multi.$SERVICE` |
|
|
| `"function"` | `multi.$FUNCTION` |
|
|
| `"timemaster"` | `multi.$TIMEMASTER` |
|
|
|
|
### Destroyed Objects
|
|
|
|
When an object is fully destroyed via `multi.setType(obj, multi.DestroyedObj)`, all field accesses return a dead sentinel object that silently absorbs all operations.
|
|
|
|
```lua
|
|
if getmetatable(obj) == multi.DestroyedObj then
|
|
print("Object is destroyed")
|
|
end
|
|
```
|
|
|
|
---
|
|
|
|
## Utility Functions
|
|
|
|
### Logging
|
|
|
|
```lua
|
|
multi.print(...) -- prints INFO (only if settings.print = true)
|
|
multi.warn(...) -- prints WARNING (only if settings.warn = true)
|
|
multi.debug(...) -- prints DEBUG with traceback (only if settings.debugging = true)
|
|
multi.error(self?, msg) -- prints ERROR; hard-errors if settings.error = true
|
|
multi.success(...) -- always prints SUCCESS
|
|
```
|
|
|
|
All use ANSI color codes.
|
|
|
|
### Math & Tables
|
|
|
|
```lua
|
|
multi.Round(num, decimalPlaces?) -- round to N decimal places
|
|
multi.AlignTable(tab) -- format a 2D table as aligned columns
|
|
table.merge(t1, t2) -- deep-merge t2 into t1, returns t1
|
|
```
|
|
|
|
### Misc
|
|
|
|
```lua
|
|
multi.randomString(n) -- returns a random alphanumeric string of length n
|
|
multi.ForEach(tab, func) -- calls func(tab[i]) for each element
|
|
multi.timer(func, ...) -- runs func(...), returns elapsed time and return values
|
|
multi.isTimeout(val) -- true if val is a TIMEOUT sentinel
|
|
multi.isMulitObj(obj) -- true if obj has a registered multi type
|
|
os.getOS() -- returns "windows" or "unix"
|
|
os.sleep(n) -- blocking OS sleep (avoid; prefer thread.sleep)
|
|
```
|
|
|
|
### Benchmarking
|
|
|
|
```lua
|
|
local bench = multi:benchMark(seconds, priority?, label?)
|
|
bench.OnBench(function(time, steps)
|
|
print("Completed", steps, "iterations in", time, "seconds")
|
|
end)
|
|
```
|
|
|
|
### Getting Active Information
|
|
|
|
```lua
|
|
multi.getCurrentProcess() -- returns the currently executing processor
|
|
multi.getCurrentTask() -- returns the currently executing actor
|
|
multi:getChildren() -- returns the Mainloop array of this processor
|
|
multi:getRunners() -- returns non-internal actors in Mainloop
|
|
multi:getThreads() -- returns the thread list
|
|
multi:getProcessors() -- returns all registered sub-processors
|
|
multi:getStats() -- returns a stats table for all processors
|
|
```
|
|
|
|
---
|
|
|
|
## Settings & Initialization
|
|
|
|
```lua
|
|
local multi, thread = require("multi"):init({
|
|
print = false, -- enable multi.print()
|
|
warn = false, -- enable multi.warn()
|
|
debugging = false, -- enable multi.debug() and debugManager
|
|
error = false, -- hard-error on runtime errors
|
|
priority = false, -- priority-based scheduling
|
|
findopt = false, -- anonymize function optimization hints
|
|
})
|
|
```
|
|
|
|
`init()` can be called multiple times but only applies settings on the first call. Subsequent calls just return `multi` and `thread`.
|
|
|
|
### Default Settings
|
|
|
|
```lua
|
|
multi.defaultSettings -- table defining what the default settings are
|
|
```
|
|
|
|
---
|
|
|
|
## System Events
|
|
|
|
These connections are available on the `multi` root object and fire at specific lifecycle points.
|
|
|
|
| Connection | Fires When |
|
|
|---|---|
|
|
| `multi.OnObjectCreated` | Any actor is created via `:create()` |
|
|
| `multi.OnObjectDestroyed` | Any actor is destroyed |
|
|
| `multi.OnLoad` | Just before the main loop starts (or on first thread creation) |
|
|
| `multi.OnPreLoad` | Just before each `uManager` pass |
|
|
| `multi.OnExit` | When `os.exit()` is called |
|
|
| `multi.OnError` | Global error handler |
|
|
| `multi.enableOptimization` | When optimization mode is enabled |
|
|
| `multi.settingsHook` | When `init()` is called with settings |
|
|
|
|
```lua
|
|
multi.OnObjectCreated(function(obj, parent)
|
|
print("Created:", obj.Type, "on", parent.Name)
|
|
end)
|
|
|
|
multi.OnExit(function(code)
|
|
print("Exiting with code:", code)
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
## UUID Utilities
|
|
|
|
`multi` includes a UUID v7 generator (timestamp-based).
|
|
|
|
```lua
|
|
local uuid = multi.generate_uuid7()
|
|
-- e.g. "018fdb62-1a00-7abc-8def-012345678901"
|
|
```
|
|
|
|
### Extracting Timestamps
|
|
|
|
```lua
|
|
local info = multi.extract_uuid7_timestamp(uuid)
|
|
-- info.milliseconds -- Unix timestamp in ms
|
|
-- info.seconds -- Unix timestamp in seconds
|
|
-- info.date -- "YYYY-MM-DD HH:MM:SS"
|
|
-- info.iso8601 -- "YYYY-MM-DDTHH:MM:SS.mmmZ"
|
|
```
|
|
|
|
### Object Creation Timestamps
|
|
|
|
Every actor created via `:create()` receives a UID. The creation timestamp can be retrieved:
|
|
|
|
```lua
|
|
local ts = actor:GetCreationTimestamp()
|
|
-- returns an ISO 8601 string
|
|
```
|
|
|
|
---
|
|
|
|
## Advanced Patterns
|
|
|
|
### Chaining with SetTime / ResolveTimer
|
|
|
|
`SetTime` adds a timeout to any actor. If the actor doesn't call `ResolveTimer` within the specified duration, `OnTimedOut` fires and the actor is paused.
|
|
|
|
```lua
|
|
local myLoop = multi:newLoop(function(self, t)
|
|
if someCondition() then
|
|
self:ResolveTimer("success")
|
|
end
|
|
end)
|
|
|
|
myLoop:SetTime(5)
|
|
myLoop.OnTimedOut(function(self)
|
|
print("Timed out after 5 seconds!")
|
|
end)
|
|
myLoop.OnTimerResolved(function(self, reason)
|
|
print("Resolved with:", reason)
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
### Reallocating Actors Between Processors
|
|
|
|
An actor can be moved from one processor to another at runtime.
|
|
|
|
```lua
|
|
local proc1 = multi:newProcessor("P1"):Start()
|
|
local proc2 = multi:newProcessor("P2"):Start()
|
|
|
|
local loop = proc1:newLoop(function() print("running") end)
|
|
|
|
-- Move to proc2:
|
|
loop:reallocate(proc2)
|
|
```
|
|
|
|
---
|
|
|
|
### `multi.hold()` Outside Threads
|
|
|
|
`multi.hold()` can be called from outside a thread. It spins the main loop internally until the condition resolves.
|
|
|
|
```lua
|
|
-- Block main script execution until a connection fires:
|
|
local result = multi.hold(someConnection)
|
|
```
|
|
|
|
This is useful for top-level async patterns before `mainloop()` is started.
|
|
|
|
---
|
|
|
|
### thread.defer
|
|
|
|
Registers a function to run when the current thread dies or errors.
|
|
|
|
```lua
|
|
thread:newThread("WithCleanup", function()
|
|
thread.defer(function(th)
|
|
print("Thread died, cleaning up")
|
|
end)
|
|
-- do work ...
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
### thread.chain
|
|
|
|
Runs a sequence of hold conditions one after another.
|
|
|
|
```lua
|
|
thread:newThread("Sequencer", function()
|
|
thread.chain(
|
|
function() return conditionA() end,
|
|
connB,
|
|
function() return conditionC() end
|
|
)
|
|
print("All three conditions satisfied in order")
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
### Optimization Detection
|
|
|
|
When `findopt = true` is passed to `init()`, `multi` detects anonymous functions passed repeatedly to `thread.hold` and emits a warning.
|
|
|
|
```lua
|
|
multi.optConn(function(msg)
|
|
print("OPT HINT:", msg)
|
|
end)
|
|
```
|
|
|
|
---
|
|
|
|
## Quick Reference Card
|
|
|
|
```
|
|
ACTORS (created on multi or processor)
|
|
multi:newLoop(func?) -- fires every iteration
|
|
multi:newTLoop(func?, interval?) -- fires every N seconds
|
|
multi:newAlarm(secs?, func?) -- one-shot after N seconds
|
|
multi:newStep(s,e,c?,skip?) -- counter s→e by c
|
|
multi:newTStep(s,e,c?,interval?) -- timed counter
|
|
multi:newEvent(task?, func?) -- fires when task() is truthy
|
|
multi:newUpdater(skip?, func?) -- fires every N iterations
|
|
multi:newTimer() -- utility timer (not an actor)
|
|
|
|
ALL ACTORS SHARE
|
|
:Pause() :Resume() :Destroy()
|
|
:setPriority(s) :setName(s)
|
|
:isPaused() :isActive() :isDone()
|
|
OnBreak OnPriorityChanged
|
|
|
|
CONNECTIONS
|
|
conn = obj:newConnection(protect?, func?, kill?)
|
|
conn:Fire(...) -- emit
|
|
conn:Connect(func) -- subscribe → handle
|
|
conn(func) -- shorthand Connect
|
|
handle:Unconnect() -- unsubscribe
|
|
conn:Destroy() -- teardown
|
|
conn + conn2 -- OR merge
|
|
conn * conn2 -- AND gate
|
|
func % conn -- map transform
|
|
func / conn -- filter
|
|
func .. conn -- gate/split
|
|
|
|
THREADS
|
|
thread:newThread(name?, func, ...)
|
|
thread:newFunction(func, holdme?)
|
|
thread:newISOThread(name?, func, env?, ...)
|
|
-- inside threads:
|
|
thread.sleep(n)
|
|
thread.hold(cond, opts?)
|
|
thread.yield()
|
|
thread.skip(n)
|
|
thread.holdFor(secs, cond?)
|
|
thread.holdWithin(cycles, cond?)
|
|
thread.set(k,v) / thread.get(k) / thread.waitFor(k)
|
|
thread.pushStatus(...)
|
|
thread.isThread()
|
|
|
|
PROCESSORS
|
|
proc = multi:newProcessor(name?, opts?)
|
|
proc:Start() / proc:Stop() / proc:Destroy()
|
|
proc.run(dt?) -- manual tick
|
|
proc:newThread(...) -- spawn thread inside proc
|
|
proc:setMaxThreads(n)
|
|
proc:boost(n)
|
|
|
|
PRIORITIES
|
|
"core"/"c" "very high"/"vh" "high"/"h"
|
|
"above"/"a" "normal"/"n" "below"/"b"
|
|
"low"/"l" "very low"/"vl" "idle"/"i"
|
|
|
|
UTILITIES
|
|
multi.isTimeout(v)
|
|
multi.Round(n, dec?)
|
|
multi.randomString(n)
|
|
multi.timer(func, ...)
|
|
multi:benchMark(secs)
|
|
multi.generate_uuid7()
|
|
multi.extract_uuid7_timestamp(uuid)
|
|
|
|
LOGGING (controlled by init settings)
|
|
multi.print(...) -- INFO (blue)
|
|
multi.warn(...) -- WARNING (yellow)
|
|
multi.debug(...) -- DEBUG (white)
|
|
multi.error(...) -- ERROR (red)
|
|
multi.success(...) -- SUCCESS (green)
|
|
```
|
|
|
|
---
|
|
|
|
*Documentation generated for multi v16.3.0-testing.*
|