Skip to main content

SYNC extension

Server-side synchronization primitives: 64-bit counters that clients can create, change and wait on, alarms that fire events when a counter crosses a threshold, client scheduling priorities, and (since 3.1) fences bound to a drawable's rendering.

X.require('sync', (err, Sync) => {
const counter = X.AllocID();
Sync.CreateCounter(counter, 100);
Sync.QueryCounter(counter, (err, value) => {
// value === 100
});
const alarm = X.AllocID();
Sync.CreateAlarm(alarm, {
counter,
valueType: Sync.ValueType.Absolute,
value: 1000,
testType: Sync.TestType.PositiveComparison,
delta: 1,
events: 1
});
X.on('event', ev => {
// AlarmNotify fires when the counter reaches 1000
});
});

Initialize is issued automatically while requiring (the protocol demands it before any other SYNC request); the negotiated version is available as Sync.major / Sync.minor.

All counter, alarm-value and delta arguments are plain JS numbers. On the wire they are INT64; see Notes for the precision caveat.

Requests

Initialize(clientMajor, clientMinor, cb)

Negotiates the protocol version. cb(err, [major, minor]). Called automatically by X.require with 3.1.

ListSystemCounters(cb)

cb(err, counters) — array of {counter, resolution, name}: the counter XID, its resolution (a number) and its ASCII name. Every server provides at least SERVERTIME.

CreateCounter(id, initialValue)

Creates a counter with a fresh XID id (from X.AllocID()) holding initialValue. No reply.

SetCounter(counter, value)

Sets the counter to value (may be negative). No reply.

ChangeCounter(counter, amount)

Adds amount (may be negative) to the counter. No reply.

QueryCounter(counter, cb)

cb(err, value) — the counter's current value as a number.

DestroyCounter(counter)

Destroys the counter; pending Awaits on it are released and triggered alarms on it become Inactive. No reply.

Await(waitList)

waitList is an array of wait conditions: {counter, valueType, value, testType, eventThreshold} (omitted fields default to 0). The server stops processing further requests from this connection until at least one condition is satisfied — replies to requests sent after Await only arrive once it unblocks. If the counter jumps past value by more than eventThreshold, a CounterNotify event is sent. No reply.

CreateAlarm(id, values)

Creates alarm id (a fresh XID). values is a value-mask style object with any of {counter, valueType, value, testType, delta, events}; omitted attributes keep server defaults. When the trigger condition on counter becomes true the alarm sends AlarmNotify (if events is set) and re-arms itself by adding delta to the trigger value. No reply.

ChangeAlarm(id, values)

Changes alarm attributes; same values object as CreateAlarm. No reply.

QueryAlarm(alarm, cb)

cb(err, {trigger, delta, events, state}) where trigger is {counter, waitType, waitValue, testType}, events is a boolean and state is one of Sync.AlarmState.

DestroyAlarm(alarm)

Destroys the alarm. No reply.

SetPriority(id, priority)

Sets the scheduling priority (INT32, higher runs first) of the client owning XID id; id = 0 means the requesting client. No reply.

GetPriority(id, cb)

cb(err, priority) — the client's current priority. id = 0 for the requesting client.

CreateFence(drawable, fence, initiallyTriggered)

SYNC 3.1. Creates fence fence (a fresh XID) on the screen of drawable, initially triggered when initiallyTriggered is truthy. No reply.

TriggerFence(fence)

Asks the server to set the fence to triggered once all rendering affecting it has completed. No reply.

ResetFence(fence)

Puts a triggered fence back into the untriggered state. No reply.

DestroyFence(fence)

Destroys the fence. No reply.

QueryFence(fence, cb)

cb(err, triggered) — boolean, current fence state.

AwaitFence(fenceList)

fenceList is an array of fence XIDs. Like Await, blocks further request processing for this connection until every listed fence is triggered. No reply.

Events / errors

CounterNotify

Sent when an Await condition is satisfied with the counter overshooting the awaited value by more than the condition's eventThreshold, or when an awaited counter is destroyed. Fields: {type, seq, name: 'CounterNotify', kind, counter, waitValue, counterValue, time, count, destroyed}count is the number of further CounterNotify events following this one, destroyed is a boolean.

AlarmNotify

Sent when an alarm with events enabled triggers or changes state. Fields: {type, seq, name: 'AlarmNotify', kind, alarm, counterValue, alarmValue, time, state}state is one of Sync.AlarmState.

Notes

  • INT64 precision: counter/alarm values travel as 64-bit integers on the wire but are composed into plain JS numbers (no BigInt in the public API). Values with magnitude above 2^53 lose precision; everything up to ±2^53 - 1 round-trips exactly (verified up to 2^40 in the tests).
  • Enums attached to the extension object:
    • Sync.ValueType = {Absolute: 0, Relative: 1}
    • Sync.TestType = {PositiveTransition: 0, NegativeTransition: 1, PositiveComparison: 2, NegativeComparison: 3}
    • Sync.AlarmState = {Active: 0, Inactive: 1, Destroyed: 2}
    • Sync.CA = {Counter: 1, ValueType: 2, Value: 4, TestType: 8, Delta: 16, Events: 32} — the value mask bits; CreateAlarm/ChangeAlarm build the mask automatically from the keys present in values, so CA is only needed for reference.
  • Fence requests require SYNC 3.1; check Sync.minor >= 1 before using them (the tests skip fences on older servers).
  • Await/AwaitFence block on the server side only — the Node process keeps running; it is the replies on this connection that stall. Use a second connection to satisfy the condition (see the Await tests).