Written from a line-by-line source review; every example output is from a real run.
Introduction
The callbacklistener plugin is the master of the callback_* family: a generic, network-protocol-agnostic async listener that dispatches arbitrary blob-based requests to a script-level callback on a background thread. The plugin supports the full asyncmeta vocabulary (Capabilities = 4095 = ProfileRouteService — every bit), and introduces the three-layer model: subscription (the callback registration), route (= subscription, the asyncmeta-vocabulary name), and job (a concrete request). The callback signature is `callback i32 NAME(ref blob $Request)`; the callback may modify the request blob in place, and that becomes the response.
The three-layer model
Subscription: the channel created by `Open(callback, timeoutMs)` — a script-callback registration with a timeout. Multiple subscriptions may live simultaneously, each with a different callback. Route: same as the subscription, just the asyncmeta-vocabulary name. The `RouteState`, `RouteCallback`, `RouteTimeout`, `RouteKnown`, `RouteMeta`, `RouteInfo`, `RoutePending`, `RouteQueued`, `RouteRunning`, `RouteDrained`, `WaitRouteDrained`, `RouteCloseAccepted`, `RouteClosedQueued`, `RouteCloseInfo`, `RouteClose` all operate on the subscription under another name — pick the code style you prefer (Subscription* is the direct form, Route* is the asyncmeta-compatible form). Job: a request scheduled by `Send`/`SendTo`/`SendBorrowedText`/`SendBorrowedTextTo`. Every job belongs to exactly one subscription (queryable via `JobSubscription` / `JobRoute`).
Send vs SendTo
`Send(request)` and `SendBorrowedText(text)` only work when EXACTLY ONE subscription is open — otherwise they fail with “callbacklistener.Send requires exactly one open subscription”. With multiple subscriptions, `SendTo(subscriptionId, request)` and `SendBorrowedTextTo(subscriptionId, text)` are the explicit forms. `Send` is a legacy convenience for the single-subscription case.
Send vs SendBorrowedText
`Send` takes a blob (the request's binary data); `SendBorrowedText` takes a string passed to the callback as a borrowed blob — fewer memory copies than building an explicit blob. The two forms are semantically equivalent: the callback always gets a ref blob parameter, and the modified bytes become the response.
Two wait styles
The plugin offers two wait functions. `WaitComplete(jobId, timeoutMs)` waits for one SPECIFIC job to finish, returning 1/0 (did it complete in time). `WaitDrained(subscriptionId, timeoutMs)` and `WaitRouteDrained(subscriptionId, timeoutMs)` wait for a SUBSCRIPTION to fully drain (every job belonging to it to finish). Important: in the parameter table for WaitComplete the argument is registered as `subscriptionId`, but the source treats it as a `jobId` — the parameter-table name only conveys the count/type, not semantics; semantics come from the source.
Taking job results
A completed job's response blob is collected by `Take(jobId)` or `TakeBlob(jobId)` — these are synonyms. `Take/TakeBlob` destroys the job; the jobId is invalid afterward. `JobHasBlobResult(jobId)` and `JobBlobResultSize(jobId)` allow defensive pre-checks: if Status is non-zero or the job is not finished, Take raises a runtime error.
Two-step shutdown
Clean shutdown of a single subscription: (1) `RouteClose(subscriptionId)` or `CloseSubscription(subscriptionId)` — same handler; the return value is the count of queued jobs canceled by the close (0 = clean close with nothing pending). (2) `WaitRouteDrained(subscriptionId, timeoutMs)` — wait for the in-flight callbacks to finish. (3) Plugin-wide shutdown: `Close()` — closes every still-open subscription and stops the background thread. Observation: `RouteCloseAccepted` is a LATCH — once it's 1 it does not reset when reopening.
Loading the plugin
plugin "../plugins/print/PrintPlugin"; plugin "../plugins/async_meta/AsyncMetaPlugin"; plugin "../plugins/callback_listener/CallbackListenerPlugin";
A typical flow — one subscription, Send + WaitComplete + Take
callback i32 Echo(ref blob $Request)
{
// the callback may modify the request bytes in place;
// those become the response blob bytes
return(1);
}
i32 main()
{
i64 $sub;
i64 $jobId;
blob $req[3];
blob $resp[8];
$req[0] = 65;
$req[1] = 66;
$req[2] = 67;
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
callbacklistener.WaitComplete($jobId, 5000);
$resp = callbacklistener.TakeBlob($jobId);
callbacklistener.Close();
return(0);
}What to know about every function (the basics)
Capabilities is always ProfileRouteService = 4095 = every bit (full asyncmeta profile).
Subscription/Route equivalence: same thing, two names. `JobSubscription == JobRoute`, `SubscriptionPending == RoutePending`, etc.
Send / SendBorrowedText only work with ONE open subscription; with several subs, SendTo / SendBorrowedTextTo is required.
WaitComplete waits on a JOB (jobId), NOT a subscription — the parameter name in the registration is misleading.
WaitDrained and WaitRouteDrained wait on a SUBSCRIPTION (subscriptionId): completion of every job belonging to it.
Take and TakeBlob are synonyms with identical behavior, and both destroy the job.
RouteClose and CloseSubscription are the same handler — two names, same behavior. The return is the count of canceled queued jobs.
Cancel takes a jobId and only removes jobs that have not yet started.
OpenCount is the plugin-wide, global open-subscription count.
An error (stopping the script) is caused by a wrong argument count or type, an out-of-range timeoutMs, an invalid jobId/subscriptionId, an already-closed subscription, and touching an already-closed plugin.
How to read the signatures
callback is the script-level callback name (`callback i32 NAME(ref blob $X)`). timeoutMs is the callback's run-time limit in ms; -1 = infinite. subscriptionId is the positive id given by Open. jobId is the positive id given by Send*. metaKey is one of the asyncmeta.RouteMeta* or JobMeta* values. The type after the -> arrow is the return type.
Lifecycle
Plugin capability query, opening a subscription, and plugin-wide shutdown.
callbacklistener.Capabilities
callbacklistener.Capabilities() -> int
Returns the plugin's capability bitmask matching the asyncmeta constants.
This function takes no arguments.
asyncmeta.ProfileRouteService (=4095): every asyncmeta bit (the entire vocabulary supported).
printf("%d\n", callbacklistener.Capabilities());
printf("%d\n", (callbacklistener.Capabilities() ==
asyncmeta.ProfileRouteService()));4095 1
For checking against another asyncmeta-compatible backend. callback_listener supports the FULL vocabulary — the Supports* helpers return 1 for everything.
callbacklistener.Open
callbacklistener.Open(callback, timeoutMs) -> int
Opens a subscription (callback registration) and returns the subscriptionId.
| Parameter | Type | Description |
|---|---|---|
| callback | string | Script-level callback name to call. Signature: `callback i32 NAME(ref blob $X)`. |
| timeoutMs | int | Callback run-time limit in ms. -1 = infinite; otherwise 0..2147483647. |
The subscriptionId (positive i64) to pass to other operations. Bad arguments or a closed plugin raises a runtime error.
callback i32 Echo(ref blob $X) { return(1); }
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", ($sub > 0));1
Create an async callback channel. You can open multiple subscriptions — one with an echo callback, another with a transform callback; SendTo targets the right channel.
callbacklistener.OpenCount
callbacklistener.OpenCount() -> int
Returns the count of currently open subscriptions (plugin-wide, global view).
This function takes no arguments.
The number of open subscriptions (0 if none).
printf("%d\n", callbacklistener.OpenCount());
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.OpenCount());0 1
For resource control and diagnostics. If it does not return to 0 after Close calls, that signals a resource leak.
callbacklistener.Close
callbacklistener.Close() -> int
Plugin-wide shutdown: close every subscription and stop the background thread.
This function takes no arguments.
0 (always).
callbacklistener.Close();
printf("Close done\n");Close done
At the script's end, for clean resource shutdown. A currently running callback may finish, but no new job will start.
Job submission
Schedule async jobs to an open subscription. All four verbs return immediately with the jobId.
callbacklistener.Send
callbacklistener.Send(request) -> int
Schedules an async job on the single open subscription (legacy: fails with multiple subs).
| Parameter | Type | Description |
|---|---|---|
| request | blob | The input blob passed to the callback. The callback may modify it in place; the modification is visible in the response blob. |
The jobId (positive i64). Multiple subs or no open sub raises a runtime error.
i64 $sub;
i64 $jobId;
blob $req[3];
$req[0] = 65;
$req[1] = 66;
$req[2] = 67;
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
printf("%d\n", ($jobId > 0));1
Simple, single-subscription scenarios: more convenient than SendTo since no explicit subscriptionId is needed. Use SendTo with multiple subs.
callbacklistener.SendTo
callbacklistener.SendTo(subscriptionId, request) -> int
Schedules an async job to a SPECIFIC subscription.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The target subscription id. |
| request | blob | The input blob passed to the callback. |
The jobId (positive i64). An invalid subscriptionId or closed subscription raises a runtime error.
i64 $sub;
i64 $jobId;
blob $req[3];
$req[0] = 65;
$req[1] = 66;
$req[2] = 67;
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.SendTo($sub, $req);
printf("%d\n", ($jobId > 0));1
With multiple subscriptions (different callbacks): SendTo targets the right channel. You can build a router-style pattern — different processing callback per subscription, code dispatches to the right one.
callbacklistener.SendBorrowedText
callbacklistener.SendBorrowedText(text) -> int
Schedules an async job with text input to the single open subscription.
| Parameter | Type | Description |
|---|---|---|
| text | string | The text passed to the callback (as a borrowed blob). |
The jobId (positive i64). Multiple subs or no open sub raises a runtime error.
i64 $sub;
i64 $jobId;
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.SendBorrowedText("hi");
printf("%d\n", ($jobId > 0));1
For text inputs (commands, JSON strings): fewer memory copies than explicit blob construction.
callbacklistener.SendBorrowedTextTo
callbacklistener.SendBorrowedTextTo(subscriptionId, text) -> int
Schedules an async job with text input to a SPECIFIC subscription.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The target subscription id. |
| text | string | The text passed to the callback. |
The jobId (positive i64).
i64 $sub;
i64 $jobId;
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.SendBorrowedTextTo($sub, "world");
printf("%d\n", ($jobId > 0));1
For multiple subscriptions + text input. The borrowed-text minimizes memory allocation.
Wait and take
Wait for the job to finish, take the response blob, and cancel queued jobs.
callbacklistener.WaitComplete
callbacklistener.WaitComplete(jobId, timeoutMs) -> int
Blocks until the job completes (jobId!), but does NOT destroy the job — status and result remain queryable.
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The JOB id (NOT subscriptionId — the registration parameter table is misleading). |
| timeoutMs | int | Maximum wait in milliseconds; -1 = infinite. |
1 if the job completed; 0 if the timeout expired and the job is still running.
i64 $sub;
i64 $jobId;
blob $req[3];
$req[0] = 65;
$req[1] = 66;
$req[2] = 67;
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
printf("%d\n", callbacklistener.WaitComplete($jobId, 5000));1
First step of the two-step pattern: wait for completion, then verify success via Status / JobMeta, finally Take/TakeBlob the response. IMPORTANT: the parameter name in the registration is subscriptionId, but the source treats it as `JobId` — pass the JOB id.
callbacklistener.Take
callbacklistener.Take(jobId) -> blob
Returns the response blob of an already-completed job and destroys the job. Synonym for TakeBlob.
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
The response blob (the callback-modified request bytes). If the job has not completed, a runtime error.
i64 $sub;
i64 $jobId;
blob $req[3];
blob $resp[8];
$req[0] = 65;
$req[1] = 66;
$req[2] = 67;
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
callbacklistener.WaitComplete($jobId, 5000);
$resp = callbacklistener.Take($jobId);
printf("%d\n", $resp.Length);3
Stylistic choice when the result is conceptually “taken” rather than emphasizing the blob type. Behavior is identical to TakeBlob — same handler.
callbacklistener.TakeBlob
callbacklistener.TakeBlob(jobId) -> blob
Returns the response blob of an already-completed job and destroys the job. Synonym for Take.
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
The response blob.
i64 $sub;
i64 $jobId;
blob $req[3];
blob $resp[8];
$req[0] = 65;
$req[1] = 66;
$req[2] = 67;
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
callbacklistener.WaitComplete($jobId, 5000);
$resp = callbacklistener.TakeBlob($jobId);
printf("%d %d %d\n", $resp[0], $resp[1], $resp[2]);65 66 67
Emphasizes the taken result is a blob. Identical semantics to Take — the choice is stylistic.
callbacklistener.Cancel
callbacklistener.Cancel(jobId) -> int
Attempts to remove a queued (not yet started) job.
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
1 if the job was removed from the queue; 0 if it is already running or has completed.
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
printf("%d\n", callbacklistener.Cancel($jobId));0
Only queued jobs can be canceled — running ones must self-terminate via callback timeoutMs. The 0 return is normal if the job has already started.
Subscription-level state
Current state of a subscription: open/closed, callback name, timeout, meta fields. The Subscription*/Route* names are synonyms — they query the same subscription.
callbacklistener.SubscriptionState
callbacklistener.SubscriptionState(subscriptionId) -> int
Returns the subscription's state (open/closed).
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
1 (open, asyncmeta.RouteStateOpen), 0 (closed, RouteStateClosed).
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.SubscriptionState($sub));1
For programmatic state checking. Synonym of RouteState — same return under a different name.
callbacklistener.RouteState
callbacklistener.RouteState(subscriptionId) -> int
Synonym of SubscriptionState: returns the subscription state under the asyncmeta vocabulary.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
asyncmeta.RouteStateOpen (=1) or RouteStateClosed (=0).
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.RouteState($sub));1
If your code shares other asyncmeta-compatible backends, use this for consistency.
callbacklistener.SubscriptionStateText
callbacklistener.SubscriptionStateText(subscriptionId) -> string
Human-readable text of the state.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
“open” or “closed”.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%s\n", callbacklistener.SubscriptionStateText($sub));open
For logging or UI display.
callbacklistener.RouteStateText
callbacklistener.RouteStateText(subscriptionId) -> string
Synonym of SubscriptionStateText.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
“open” or “closed”.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%s\n", callbacklistener.RouteStateText($sub));open
For logging, under the asyncmeta vocabulary.
callbacklistener.SubscriptionCallback
callbacklistener.SubscriptionCallback(subscriptionId) -> string
Returns the callback name passed at Open.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
The callback name as text.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%s\n", callbacklistener.SubscriptionCallback($sub));Echo
For reflectivity and diagnostics: confirms which callback is bound to the subscription.
callbacklistener.RouteCallback
callbacklistener.RouteCallback(subscriptionId) -> string
Synonym of SubscriptionCallback.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
The callback name as text.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%s\n", callbacklistener.RouteCallback($sub));Echo
For logging, under the asyncmeta vocabulary.
callbacklistener.SubscriptionTimeout
callbacklistener.SubscriptionTimeout(subscriptionId) -> int
Returns the timeout value passed at Open, in ms.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
The timeoutMs value; -1 for infinite.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.SubscriptionTimeout($sub));5000
Reflective query of subscription configuration.
callbacklistener.RouteTimeout
callbacklistener.RouteTimeout(subscriptionId) -> int
Synonym of SubscriptionTimeout.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
The timeoutMs value.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.RouteTimeout($sub));5000
For logging, under the asyncmeta vocabulary.
callbacklistener.RouteKnown
callbacklistener.RouteKnown(subscriptionId) -> int
Tells whether the subscriptionId is known (still open in the plugin).
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
1 if known (open); 0 if not.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.RouteKnown($sub));1
Defensive check. If 0, the subscription is already closed or unknown.
callbacklistener.RouteMeta
callbacklistener.RouteMeta(routeId, metaKey) -> int
Returns a specific meta field of the subscription by asyncmeta.RouteMeta* keys.
| Parameter | Type | Description |
|---|---|---|
| routeId | int | The subscription id (the parameter name follows the asyncmeta vocabulary). |
| metaKey | int | An asyncmeta.RouteMeta* value (State, Timeout, Pending, Queued, Running, Drained, Known, CloseAccepted, ClosedQueued). |
The value for the field.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.RouteMeta($sub,
asyncmeta.RouteMetaState()));
printf("%d\n", callbacklistener.RouteMeta($sub,
asyncmeta.RouteMetaDrained()));1 1
Uniform asyncmeta query of subscription state. For portable code across backends, this is the portable form.
callbacklistener.SubscriptionInfo
callbacklistener.SubscriptionInfo(subscriptionId) -> string
Returns the subscription state as a key=value diagnostic string.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
A space-separated key=value list.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", (str.Len(callbacklistener.SubscriptionInfo($sub)) >
0));1
Detailed diagnostics in a single call.
callbacklistener.RouteInfo
callbacklistener.RouteInfo(subscriptionId) -> string
Synonym of SubscriptionInfo.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
A key=value diagnostic string.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", (str.Len(callbacklistener.RouteInfo($sub)) >
0));1
Under the asyncmeta vocabulary.
Subscription-level traffic
Per-subscription job traffic metrics: pending, queued, running, drained. The Subscription*/Route* names are synonyms here as well.
callbacklistener.SubscriptionPending
callbacklistener.SubscriptionPending(subscriptionId) -> int
Returns the number of pending (not yet started) jobs on the subscription.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
The number of pending jobs.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.SubscriptionPending($sub));0
Early signal of congestion.
callbacklistener.RoutePending
callbacklistener.RoutePending(subscriptionId) -> int
Synonym of SubscriptionPending.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
The number of pending jobs.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.RoutePending($sub));0
For logging, under the asyncmeta vocabulary.
callbacklistener.SubscriptionQueued
callbacklistener.SubscriptionQueued(subscriptionId) -> int
Returns the number of queued jobs on the subscription.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
The number of queued jobs.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.SubscriptionQueued($sub));0
For monitoring the internal queue length.
callbacklistener.RouteQueued
callbacklistener.RouteQueued(subscriptionId) -> int
Synonym of SubscriptionQueued.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
The number of queued jobs.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.RouteQueued($sub));0
For logging, under the asyncmeta vocabulary.
callbacklistener.SubscriptionRunning
callbacklistener.SubscriptionRunning(subscriptionId) -> int
Returns the number of currently running jobs on the subscription.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
The number of running jobs.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.SubscriptionRunning($sub));0
For monitoring parallelism.
callbacklistener.RouteRunning
callbacklistener.RouteRunning(subscriptionId) -> int
Synonym of SubscriptionRunning.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
The number of running jobs.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.RouteRunning($sub));0
For logging, under the asyncmeta vocabulary.
callbacklistener.SubscriptionDrained
callbacklistener.SubscriptionDrained(subscriptionId) -> int
Tells whether the subscription is drained (pending = queued = running = 0).
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
1 if drained; 0 if something is in progress.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.SubscriptionDrained($sub));1
Before clean shutdown: ensure Drained=1 before calling Close.
callbacklistener.RouteDrained
callbacklistener.RouteDrained(subscriptionId) -> int
Synonym of SubscriptionDrained.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
1 if drained; 0 if not.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.RouteDrained($sub));1
For logging, under the asyncmeta vocabulary.
callbacklistener.WaitDrained
callbacklistener.WaitDrained(subscriptionId, timeoutMs) -> int
Blocks until the subscription is drained, or timeoutMs expires.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
| timeoutMs | int | Maximum wait in ms; -1 = infinite. |
1 if drain occurred before timeoutMs expired; 0 if it expired.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.WaitDrained($sub, 50));1
In the two-step shutdown pattern: AFTER RouteClose, wait for full drain so every in-flight job can finish.
callbacklistener.WaitRouteDrained
callbacklistener.WaitRouteDrained(subscriptionId, timeoutMs) -> int
Synonym of WaitDrained.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
| timeoutMs | int | Max wait in ms. |
1 if drain occurred; 0 if expired.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.WaitRouteDrained($sub, 50));1
For logging, under the asyncmeta vocabulary.
Job-level state and metadata
Current state (phase, status, error) and metadata (timeout, owning subscription, blob result info) of a specific job. Take preconditions are checked here.
callbacklistener.State
callbacklistener.State(jobId) -> int
Returns a unified state code for the job (a shorthand of phase or completion-with-error).
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
A state code (a combination of Phase + Status).
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
callbacklistener.WaitComplete($jobId, 5000);
printf("%d\n", callbacklistener.State($jobId));2
Makes the job state visible in one query; a simplified summary of Phase + Status.
callbacklistener.Phase
callbacklistener.Phase(jobId) -> int
Returns the job phase from the asyncmeta.Phase* constants.
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
asyncmeta.PhaseQueued (=0), PhaseRunning (=1), or PhaseCompleted (=2).
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
callbacklistener.WaitComplete($jobId, 5000);
printf("%d\n", callbacklistener.Phase($jobId));2
For programmatic phase checks: compare with the asyncmeta constants.
callbacklistener.PhaseText
callbacklistener.PhaseText(jobId) -> string
Returns the phase as text.
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
“queued”, “running”, or “completed”.
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
callbacklistener.WaitComplete($jobId, 5000);
printf("%s\n", callbacklistener.PhaseText($jobId));completed
For logging or UI display.
callbacklistener.Status
callbacklistener.Status(jobId) -> int
Returns the job's status: 0 = success, non-zero = error.
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
0 if the job ran successfully; non-zero is the error code.
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
callbacklistener.WaitComplete($jobId, 5000);
printf("%d\n", callbacklistener.Status($jobId));0
After WaitComplete, check this first: if 0, go ahead with Take; if non-0, ErrorText is more precise.
callbacklistener.StatusText
callbacklistener.StatusText(jobId) -> string
Textual counterpart of the status.
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
“success” on a successful job, other text depending on the error type.
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
callbacklistener.WaitComplete($jobId, 5000);
printf("%s\n", callbacklistener.StatusText($jobId));success
For logging or user feedback.
callbacklistener.ErrorText
callbacklistener.ErrorText(jobId) -> string
Returns the job error message (empty on success).
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
The error message text; empty string on a successful job.
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
callbacklistener.WaitComplete($jobId, 5000);
printf("%d\n", str.Len(callbacklistener.ErrorText($jobId)));0
If Status is non-0, this reveals the cause of the error.
callbacklistener.JobInfo
callbacklistener.JobInfo(jobId) -> string
Returns the job state as a key=value diagnostic string.
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
A space-separated key=value list.
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
callbacklistener.WaitComplete($jobId, 5000);
printf("%d\n", (str.Len(callbacklistener.JobInfo($jobId)) >
0));1
Detailed job diagnostics in a single call.
callbacklistener.JobSubscription
callbacklistener.JobSubscription(jobId) -> int
Returns the id of the subscription the job belongs to.
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
The subscriptionId.
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
printf("%d\n", (callbacklistener.JobSubscription($jobId) ==
$sub));1
Reflective query of the job-owner relation.
callbacklistener.JobRoute
callbacklistener.JobRoute(jobId) -> int
Synonym of JobSubscription — returns the owning subscription/route id.
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
The subscriptionId (= routeId).
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
printf("%d\n", (callbacklistener.JobRoute($jobId) == $sub));1
Under the asyncmeta vocabulary (route = subscription).
callbacklistener.JobTimeout
callbacklistener.JobTimeout(jobId) -> int
Returns the job's configured callback timeout in ms (inherited from the subscription).
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
The timeoutMs value.
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
printf("%d\n", callbacklistener.JobTimeout($jobId));5000
For logging or configuration checks.
callbacklistener.JobKnown
callbacklistener.JobKnown(jobId) -> int
Tells whether the jobId is known to the plugin.
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
1 if known; 0 if not (e.g. already destroyed by Take).
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
printf("%d\n", callbacklistener.JobKnown($jobId));1
Defensive check: if 0, the job no longer exists.
callbacklistener.JobMeta
callbacklistener.JobMeta(jobId, metaKey) -> int
Returns a specific meta field of the job by asyncmeta.JobMeta* keys.
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
| metaKey | int | An asyncmeta.JobMeta* value (Phase=1, Status=2, Route=3, Timeout=4, Known=5, HasBlobResult=6, BlobResultSize=7). |
The value matching the field. JobMetaKnown returns 0 on an unknown jobId.
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
callbacklistener.WaitComplete($jobId, 5000);
printf("%d\n", callbacklistener.JobMeta($jobId,
asyncmeta.JobMetaPhase()));2
Uniform asyncmeta query on the job state — for portable code across backends.
callbacklistener.JobHasBlobResult
callbacklistener.JobHasBlobResult(jobId) -> int
Tells whether the job has a blob result (completed and successful).
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
1 if yes; 0 if no.
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
callbacklistener.WaitComplete($jobId, 5000);
printf("%d\n", callbacklistener.JobHasBlobResult($jobId));1
Take precondition: if 0, Take raises a runtime error.
callbacklistener.JobBlobResultSize
callbacklistener.JobBlobResultSize(jobId) -> int
Returns the response blob size in bytes (only for a completed, successful job).
| Parameter | Type | Description |
|---|---|---|
| jobId | int | The jobId. |
The blob size in bytes. If the job did not complete successfully, a runtime error.
i64 $sub;
i64 $jobId;
blob $req[3];
$sub = callbacklistener.Open("Echo", 5000);
$jobId = callbacklistener.Send($req);
callbacklistener.WaitComplete($jobId, 5000);
printf("%d\n", callbacklistener.JobBlobResultSize($jobId));3
For memory pre-allocation: you know how big a blob to allocate for Take.
Subscription shutdown
Shutdown of a single subscription (independent of the plugin-wide Close). RouteClose and CloseSubscription are synonyms — same handler.
callbacklistener.CloseSubscription
callbacklistener.CloseSubscription(subscriptionId) -> int
Closes the given subscription and cancels its queued jobs.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
The count of queued jobs canceled by the close (0 = clean close with nothing pending).
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.CloseSubscription($sub));0
For a clean shutdown of a single subscription (without tearing down the whole plugin). The currently running callback may finish; no new job will run.
callbacklistener.RouteClose
callbacklistener.RouteClose(subscriptionId) -> int
Synonym of CloseSubscription — exact same handler.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
The count of canceled queued jobs.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", callbacklistener.RouteClose($sub));0
Under the asyncmeta vocabulary. Choosing between RouteClose and CloseSubscription is stylistic.
callbacklistener.RouteCloseAccepted
callbacklistener.RouteCloseAccepted(subscriptionId) -> int
Tells whether a close request has been accepted on the subscription.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
1 if yes; 0 if no RouteClose / CloseSubscription has happened yet.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
callbacklistener.RouteClose($sub);
printf("%d\n", callbacklistener.RouteCloseAccepted($sub));1
LATCH: once 1, it does not reset on reopen. Value persists for the subscription's entire life.
callbacklistener.RouteClosedQueued
callbacklistener.RouteClosedQueued(subscriptionId) -> int
Returns the count of queued jobs canceled by the most recent RouteClose / CloseSubscription.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
The count of canceled queued jobs.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
callbacklistener.RouteClose($sub);
printf("%d\n", callbacklistener.RouteClosedQueued($sub));0
For diagnostics and logging: how many jobs were lost during shutdown.
callbacklistener.RouteCloseInfo
callbacklistener.RouteCloseInfo(subscriptionId) -> string
Returns the subscription shutdown process as a key=value diagnostic string.
| Parameter | Type | Description |
|---|---|---|
| subscriptionId | int | The subscription id. |
A space-separated key=value list.
i64 $sub;
$sub = callbacklistener.Open("Echo", 5000);
printf("%d\n", (str.Len(callbacklistener.RouteCloseInfo($sub)) >
0));1
Detailed shutdown diagnostics in a single call.
Practical notes
What the callbacklistener plugin is good for
Generic async callback channel without network protocol: the code controls when and what request to send to the callback.
Parallel management of multiple channels: each subscription serves its own callback with separate state/traffic metrics.
asyncmeta compatibility with the full vocabulary: Phase, Status, JobMeta, RouteMeta, WaitDrained, RouteClose — every bit supported (Capabilities = 4095).
The subscription/route synonym pair allows stylistic choice in code — Subscription* names are direct, Route* names follow the asyncmeta convention.
Send vs SendTo usage
If your code opens only one subscription, `Send(request)` is simpler — no explicit subscriptionId needed. But if you ever open a second subscription (or some library calls Open without your knowledge), Send will fail. For defensive programming `SendTo(subscriptionId, request)` is always preferred — explicit, portable, and clearer in code about which channel you're sending to.
WaitComplete — a crucial detail
The `WaitComplete(jobId, timeoutMs)` parameter table in the registration is `gSubscriptionWaitParams` — which may suggest it takes a subscriptionId. The source code is unambiguous: `JobId = Args[0]` — it takes the JOB id. The parameter table here only encodes the argument TYPE and COUNT (one int, one int), not semantics. This is an important detail: if you accidentally pass a subscriptionId, the error message will be “job not found”. Always pass a jobId to WaitComplete.
RouteClose / CloseSubscription return value
The two calls (`RouteClose` and `CloseSubscription`) are the SAME handler — two names, identical behavior. The return value is NOT a 1/0 success flag, but the COUNT of queued jobs canceled by the close. Typically 0 (clean close with no pending jobs). A positive value: that many queued jobs were lost. A subscription closed once cannot be reopened — a second call raises a runtime error with “subscription is not open”.
Two-step shutdown pattern
(1) `RouteClose(subscriptionId)` (or `CloseSubscription`): signals the subscription is to be closed. Queued jobs are canceled; the running callback may finish. (2) `WaitRouteDrained(subscriptionId, timeoutMs)`: waits until every in-flight job is done. (3) `Close()` (plugin-wide): full plugin shutdown, including every subscription. The `RouteCloseAccepted` LATCH flips to 1 on the first RouteClose and stays — useful for diagnostics.
Error handling
An invalid jobId, subscriptionId, wrong argument count or type, out-of-range timeoutMs, touching an already-closed subscription, and touching an already-closed plugin are reported by the runtime as a runtime error, and the script stops. A WaitComplete wait-timeout is `DOMIN_STATUS_TIMEOUT`; a Close-style shutdown is `DOMIN_STATUS_SHUTDOWN`. Take/TakeBlob only accept completed (Phase=Completed) jobs.
The callbacktcp TCP listener plugin
Loopback TCP server + client in one, optional TLS, sync/async request patterns, full asyncmeta vocabulary — complete function reference