@zlink-systems/framework 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Configuration/EndpointNotation.d.ts +5 -4
- package/dist/contracts/Configuration/EndpointNotation.js +5 -4
- package/dist/contracts/Configuration/InternalDefaults.d.ts +0 -1
- package/dist/contracts/Configuration/InternalDefaults.js +1 -2
- package/dist/contracts/Configuration/RegistrationNormalizers.js +1 -2
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +2 -5
- package/dist/contracts/Configuration/RegistrationValidators.js +0 -1
- package/dist/contracts/Errors/ZLinkFrameworkException.d.ts +6 -7
- package/dist/contracts/Errors/ZLinkFrameworkException.js +6 -7
- package/dist/runtime/actors/actor-client.js +1 -1
- package/dist/runtime/actors/actor-mailbox.js +1 -5
- package/dist/runtime/backend/contracts/index.d.ts +2 -0
- package/dist/runtime/backend/mesh-completion-table.d.ts +1 -3
- package/dist/runtime/backend/mesh-completion-table.js +11 -15
- package/dist/runtime/backend/mesh-dispatch-pump.d.ts +11 -4
- package/dist/runtime/backend/mesh-dispatch-pump.js +201 -134
- package/dist/runtime/backend/node/node-backend-adapter-factory.d.ts +3 -4
- package/dist/runtime/backend/node/node-backend-adapter-factory.js +51 -37
- package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -2
- package/dist/runtime/backend/node/node-backend-adapter-support.js +2 -2
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +2 -2
- package/dist/runtime/backend/node/node-event-loop-poller.d.ts +16 -0
- package/dist/runtime/backend/node/node-event-loop-poller.js +49 -0
- package/dist/runtime/backend/node/node-raw-binding-port.js +24 -41
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +8 -8
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +100 -107
- package/dist/runtime/backend/node/node-socket-backend-adapter.d.ts +3 -2
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +18 -67
- package/dist/runtime/backend/raw-binding-port.d.ts +1 -0
- package/dist/runtime/backend/runtime-values.d.ts +5 -1
- package/dist/runtime/channels/channel-dispatchers.js +2 -2
- package/dist/runtime/channels/channel-envelope.js +324 -121
- package/dist/runtime/channels/channel-multipart.d.ts +8 -1
- package/dist/runtime/channels/channel-outbound-operations.js +0 -2
- package/dist/runtime/channels/channel-receive-loops.d.ts +1 -3
- package/dist/runtime/channels/channel-receive-loops.js +25 -65
- package/dist/runtime/channels/channel-socket-registry.js +3 -1
- package/dist/runtime/channels/channel-transports.d.ts +2 -1
- package/dist/runtime/channels/channel-transports.js +12 -10
- package/dist/runtime/execution/serial-execution-queue.d.ts +5 -18
- package/dist/runtime/execution/serial-execution-queue.js +16 -80
- package/dist/runtime/foundation/event-loop-resources.d.ts +0 -3
- package/dist/runtime/foundation/event-loop-resources.js +2 -11
- package/dist/runtime/foundation/operation-registry.d.ts +2 -8
- package/dist/runtime/foundation/operation-registry.js +3 -21
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +19 -7
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +56 -10
- package/dist/runtime/foundation/service-mailbox.d.ts +1 -7
- package/dist/runtime/foundation/service-mailbox.js +3 -2
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +2 -1
- package/dist/runtime/foundation/service-stateful-registry.d.ts +1 -1
- package/dist/runtime/foundation/service-stateful-registry.js +2 -2
- package/dist/runtime/foundation/service-stateful-runtime.js +14 -14
- package/dist/runtime/framework-errors-internal.d.ts +4 -4
- package/dist/runtime/framework-errors-internal.js +8 -10
- package/dist/runtime/host/actor-placement-coordinator.js +1 -1
- package/dist/runtime/host/index.js +1 -1
- package/dist/runtime/host/service-relocation-host-runtime.js +0 -4
- package/dist/runtime/host/spot-address-transport.js +1 -1
- package/dist/runtime/host/user-spot-creation-coordinator.js +1 -1
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +0 -12
- package/dist/runtime/spots/spot-node-runtime-manager.js +6 -166
- package/dist/runtime/spots/spot-serial-turn-executor.js +1 -4
- package/dist/runtime/spots/spot-timer.d.ts +3 -0
- package/dist/runtime/spots/spot-timer.js +87 -18
- package/dist/runtime/streams/session-serial-executor.js +1 -3
- package/dist/runtime/streams/stream-session-runtime.d.ts +2 -2
- package/dist/runtime/streams/stream-session-runtime.js +13 -19
- package/dist/runtime/workers/index.d.ts +0 -1
- package/dist/runtime/workers/index.js +1 -9
- package/package.json +3 -3
- package/src/contracts/Configuration/EndpointNotation.ts +5 -4
- package/src/contracts/Configuration/InternalDefaults.ts +0 -1
- package/src/contracts/Configuration/RegistrationNormalizers.ts +2 -4
- package/src/contracts/Configuration/RegistrationTypes.ts +2 -5
- package/src/contracts/Configuration/RegistrationValidators.ts +0 -1
- package/src/contracts/Errors/ZLinkFrameworkException.ts +6 -7
- package/src/runtime/actors/actor-client.ts +1 -1
- package/src/runtime/actors/actor-mailbox.ts +1 -12
- package/src/runtime/backend/contracts/index.ts +2 -0
- package/src/runtime/backend/mesh-completion-table.ts +12 -23
- package/src/runtime/backend/mesh-dispatch-pump.ts +206 -138
- package/src/runtime/backend/node/node-backend-adapter-factory.ts +66 -35
- package/src/runtime/backend/node/node-backend-adapter-support.ts +4 -4
- package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +2 -2
- package/src/runtime/backend/node/node-event-loop-poller.ts +55 -0
- package/src/runtime/backend/node/node-raw-binding-port.ts +30 -45
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +97 -110
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +23 -69
- package/src/runtime/backend/raw-binding-port.ts +1 -0
- package/src/runtime/backend/runtime-values.ts +7 -1
- package/src/runtime/channels/channel-dispatchers.ts +2 -2
- package/src/runtime/channels/channel-envelope.ts +379 -109
- package/src/runtime/channels/channel-multipart.ts +9 -1
- package/src/runtime/channels/channel-outbound-operations.ts +0 -1
- package/src/runtime/channels/channel-receive-loops.ts +24 -75
- package/src/runtime/channels/channel-socket-registry.ts +4 -1
- package/src/runtime/channels/channel-transports.ts +20 -10
- package/src/runtime/execution/serial-execution-queue.ts +16 -108
- package/src/runtime/foundation/event-loop-resources.ts +2 -11
- package/src/runtime/foundation/operation-registry.ts +2 -22
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +74 -19
- package/src/runtime/foundation/service-mailbox.ts +3 -13
- package/src/runtime/foundation/service-runtime-contracts.ts +2 -1
- package/src/runtime/foundation/service-stateful-registry.ts +2 -2
- package/src/runtime/foundation/service-stateful-runtime.ts +14 -14
- package/src/runtime/framework-errors-internal.ts +8 -10
- package/src/runtime/host/actor-placement-coordinator.ts +1 -1
- package/src/runtime/host/index.ts +2 -1
- package/src/runtime/host/service-relocation-host-runtime.ts +0 -4
- package/src/runtime/host/spot-address-transport.ts +1 -1
- package/src/runtime/host/user-spot-creation-coordinator.ts +1 -1
- package/src/runtime/spots/spot-node-runtime-manager.ts +13 -233
- package/src/runtime/spots/spot-serial-turn-executor.ts +1 -8
- package/src/runtime/spots/spot-timer.ts +109 -18
- package/src/runtime/streams/session-serial-executor.ts +1 -7
- package/src/runtime/streams/stream-session-runtime.ts +13 -17
- package/src/runtime/workers/index.ts +2 -17
|
@@ -142,22 +142,6 @@ export interface ZLinkSpotNodeRuntimeManagerOptions {
|
|
|
142
142
|
};
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
interface ZLinkPublishSlotWaiter {
|
|
146
|
-
readonly resolve: (acquired: boolean) => void;
|
|
147
|
-
readonly reject: (error: unknown) => void;
|
|
148
|
-
readonly signal?: AbortSignal;
|
|
149
|
-
abortHandler?: () => void;
|
|
150
|
-
timeout?: ReturnType<typeof setTimeout>;
|
|
151
|
-
deadlineMs?: number;
|
|
152
|
-
settled: boolean;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
interface ZLinkPublishSlotQueue {
|
|
156
|
-
readonly waiters: Array<ZLinkPublishSlotWaiter | undefined>;
|
|
157
|
-
head: number;
|
|
158
|
-
count: number;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
145
|
export class ZLinkSpotNodeRuntimeManager {
|
|
162
146
|
private readonly meshNodes = new Map<string, ZLinkBackendMeshNode>();
|
|
163
147
|
private readonly meshPumps = new Map<string, ZLinkMeshDispatchPump>();
|
|
@@ -168,8 +152,6 @@ export class ZLinkSpotNodeRuntimeManager {
|
|
|
168
152
|
string,
|
|
169
153
|
ReturnType<ZLinkBackendMeshNode['createPublisher']>
|
|
170
154
|
>();
|
|
171
|
-
private readonly activePublishes = new Set<string>();
|
|
172
|
-
private readonly publishSlotWaiters = new Map<string, ZLinkPublishSlotQueue>();
|
|
173
155
|
private disposed = false;
|
|
174
156
|
private readonly autoConnectLoops: ZLinkAutoConnectLoop[] = [];
|
|
175
157
|
private readonly publishedMeshNodeDescriptors =
|
|
@@ -829,7 +811,6 @@ export class ZLinkSpotNodeRuntimeManager {
|
|
|
829
811
|
this.meshPumps.clear();
|
|
830
812
|
const publishers = [...this.publishers.values()];
|
|
831
813
|
this.publishers.clear();
|
|
832
|
-
this.rejectPublishSlotWaiters(runtimeShutdownError());
|
|
833
814
|
const errors: unknown[] = [];
|
|
834
815
|
const settle = async (operations: readonly Promise<unknown>[]) => {
|
|
835
816
|
const results = await Promise.allSettled(operations);
|
|
@@ -1120,76 +1101,32 @@ export class ZLinkSpotNodeRuntimeManager {
|
|
|
1120
1101
|
if (publisher === undefined) {
|
|
1121
1102
|
throw new ZLinkConfigurationException(`RouteMesh '${meshName}' publisher is not started.`);
|
|
1122
1103
|
}
|
|
1123
|
-
|
|
1124
|
-
.get(meshName)?.publisherConfig?.sendTimeoutMs ?? 1000;
|
|
1125
|
-
const slot = this.acquirePublishSlot(meshName, sendTimeoutMs, signal);
|
|
1126
|
-
if (slot === false) {
|
|
1127
|
-
// The bounded executor-admission tokens are exhausted. Do not retain the
|
|
1128
|
-
// payload, cancellation state, or a timer for this hard-overload call.
|
|
1129
|
-
return Promise.resolve(this.publishTimedOut());
|
|
1130
|
-
}
|
|
1131
|
-
if (slot === true) {
|
|
1132
|
-
return this.executePublish(
|
|
1133
|
-
publisher,
|
|
1134
|
-
meshName,
|
|
1135
|
-
channelName,
|
|
1136
|
-
topic,
|
|
1137
|
-
packetName,
|
|
1138
|
-
event,
|
|
1139
|
-
metadata
|
|
1140
|
-
);
|
|
1141
|
-
}
|
|
1142
|
-
return slot.then((acquired) => acquired
|
|
1143
|
-
? this.executePublish(
|
|
1144
|
-
publisher,
|
|
1145
|
-
meshName,
|
|
1146
|
-
channelName,
|
|
1147
|
-
topic,
|
|
1148
|
-
packetName,
|
|
1149
|
-
event,
|
|
1150
|
-
metadata
|
|
1151
|
-
)
|
|
1152
|
-
: this.publishTimedOut());
|
|
1104
|
+
return this.executePublish(publisher, channelName, topic, packetName, event, metadata);
|
|
1153
1105
|
}
|
|
1154
1106
|
|
|
1155
1107
|
private async executePublish(
|
|
1156
1108
|
publisher: ReturnType<ZLinkBackendMeshNode['createPublisher']>,
|
|
1157
|
-
meshName: string,
|
|
1158
1109
|
channelName: string,
|
|
1159
1110
|
topic: string,
|
|
1160
1111
|
packetName: string | undefined,
|
|
1161
1112
|
event: Message,
|
|
1162
1113
|
metadata: ReadonlyMap<string, string>
|
|
1163
1114
|
): Promise<ZLinkSubmitResult> {
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
// outlive this publish call.
|
|
1168
|
-
try {
|
|
1169
|
-
const parts = runWithOutboundFlow(
|
|
1170
|
-
this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true,
|
|
1171
|
-
() => encodeChannelPublishEnvelopeParts(
|
|
1172
|
-
channelName,
|
|
1173
|
-
topic,
|
|
1174
|
-
packetName,
|
|
1175
|
-
event,
|
|
1176
|
-
undefined,
|
|
1177
|
-
this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true,
|
|
1178
|
-
metadata
|
|
1179
|
-
)
|
|
1180
|
-
);
|
|
1181
|
-
const processing = publisher.publishAsync(
|
|
1115
|
+
const parts = runWithOutboundFlow(
|
|
1116
|
+
this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true,
|
|
1117
|
+
() => encodeChannelPublishEnvelopeParts(
|
|
1182
1118
|
channelName,
|
|
1183
1119
|
topic,
|
|
1184
|
-
|
|
1120
|
+
packetName,
|
|
1121
|
+
event,
|
|
1185
1122
|
undefined,
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
}
|
|
1123
|
+
this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true,
|
|
1124
|
+
metadata
|
|
1125
|
+
)
|
|
1126
|
+
);
|
|
1127
|
+
const processing = publisher.publishAsync(channelName, topic, parts, undefined, undefined);
|
|
1128
|
+
void Promise.resolve(processing).catch(() => undefined);
|
|
1129
|
+
return Promise.resolve({ status: ZLinkSubmitStatus.Submitted });
|
|
1193
1130
|
}
|
|
1194
1131
|
|
|
1195
1132
|
tryPublish(
|
|
@@ -1242,163 +1179,6 @@ export class ZLinkSpotNodeRuntimeManager {
|
|
|
1242
1179
|
}
|
|
1243
1180
|
}
|
|
1244
1181
|
|
|
1245
|
-
private acquirePublishSlot(
|
|
1246
|
-
meshName: string,
|
|
1247
|
-
timeoutMs: number,
|
|
1248
|
-
signal?: AbortSignal
|
|
1249
|
-
): boolean | Promise<boolean> {
|
|
1250
|
-
if (!this.activePublishes.has(meshName)) {
|
|
1251
|
-
this.activePublishes.add(meshName);
|
|
1252
|
-
return true;
|
|
1253
|
-
}
|
|
1254
|
-
const waiterCapacity = Math.max(
|
|
1255
|
-
1,
|
|
1256
|
-
this.options.registration.spotNodes
|
|
1257
|
-
.get(meshName)?.publisherConfig?.sendHighWaterMark ?? 1
|
|
1258
|
-
);
|
|
1259
|
-
const queue = this.publishSlotWaiters.get(meshName) ?? {
|
|
1260
|
-
waiters: [],
|
|
1261
|
-
head: 0,
|
|
1262
|
-
count: 0
|
|
1263
|
-
};
|
|
1264
|
-
if (queue.count >= waiterCapacity) {
|
|
1265
|
-
// This call has no bounded executor-admission token. Fail without
|
|
1266
|
-
// retaining its payload in a pending work queue.
|
|
1267
|
-
return false;
|
|
1268
|
-
}
|
|
1269
|
-
return new Promise<boolean>((resolve, reject) => {
|
|
1270
|
-
const waiter: ZLinkPublishSlotWaiter = {
|
|
1271
|
-
resolve,
|
|
1272
|
-
reject,
|
|
1273
|
-
signal,
|
|
1274
|
-
settled: false
|
|
1275
|
-
};
|
|
1276
|
-
if (timeoutMs !== -1) {
|
|
1277
|
-
waiter.deadlineMs = performance.now() + Math.max(0, timeoutMs);
|
|
1278
|
-
}
|
|
1279
|
-
queue.waiters.push(waiter);
|
|
1280
|
-
queue.count += 1;
|
|
1281
|
-
this.publishSlotWaiters.set(meshName, queue);
|
|
1282
|
-
if (timeoutMs !== -1) {
|
|
1283
|
-
waiter.timeout = setTimeout(() => this.settlePublishSlotWaiter(
|
|
1284
|
-
meshName,
|
|
1285
|
-
waiter,
|
|
1286
|
-
false
|
|
1287
|
-
), Math.max(0, timeoutMs));
|
|
1288
|
-
}
|
|
1289
|
-
if (signal !== undefined) {
|
|
1290
|
-
waiter.abortHandler = () => this.rejectPublishSlotWaiter(
|
|
1291
|
-
meshName,
|
|
1292
|
-
waiter,
|
|
1293
|
-
signal.reason ?? createAbortError()
|
|
1294
|
-
);
|
|
1295
|
-
signal.addEventListener('abort', waiter.abortHandler, { once: true });
|
|
1296
|
-
}
|
|
1297
|
-
});
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
private publishTimedOut(): ZLinkSubmitResult {
|
|
1301
|
-
return { status: ZLinkSubmitStatus.TimedOut };
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
private releasePublishSlot(meshName: string): void {
|
|
1305
|
-
const queue = this.publishSlotWaiters.get(meshName);
|
|
1306
|
-
while (queue !== undefined && queue.count > 0) {
|
|
1307
|
-
const waiter = this.takePublishSlotWaiter(queue);
|
|
1308
|
-
if (waiter === undefined) continue;
|
|
1309
|
-
if (waiter.settled) continue;
|
|
1310
|
-
if (waiter.deadlineMs !== undefined && performance.now() >= waiter.deadlineMs) {
|
|
1311
|
-
this.cleanupPublishSlotWaiter(waiter);
|
|
1312
|
-
waiter.settled = true;
|
|
1313
|
-
waiter.resolve(false);
|
|
1314
|
-
continue;
|
|
1315
|
-
}
|
|
1316
|
-
this.cleanupPublishSlotWaiter(waiter);
|
|
1317
|
-
waiter.settled = true;
|
|
1318
|
-
waiter.resolve(true);
|
|
1319
|
-
if (queue.count === 0) this.publishSlotWaiters.delete(meshName);
|
|
1320
|
-
return;
|
|
1321
|
-
}
|
|
1322
|
-
this.publishSlotWaiters.delete(meshName);
|
|
1323
|
-
this.activePublishes.delete(meshName);
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
private settlePublishSlotWaiter(
|
|
1327
|
-
meshName: string,
|
|
1328
|
-
waiter: ZLinkPublishSlotWaiter,
|
|
1329
|
-
acquired: boolean
|
|
1330
|
-
): void {
|
|
1331
|
-
if (waiter.settled) return;
|
|
1332
|
-
waiter.settled = true;
|
|
1333
|
-
this.removePublishSlotWaiter(meshName, waiter);
|
|
1334
|
-
this.cleanupPublishSlotWaiter(waiter);
|
|
1335
|
-
waiter.resolve(acquired);
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
private rejectPublishSlotWaiter(
|
|
1339
|
-
meshName: string,
|
|
1340
|
-
waiter: ZLinkPublishSlotWaiter,
|
|
1341
|
-
error: unknown
|
|
1342
|
-
): void {
|
|
1343
|
-
if (waiter.settled) return;
|
|
1344
|
-
waiter.settled = true;
|
|
1345
|
-
this.removePublishSlotWaiter(meshName, waiter);
|
|
1346
|
-
this.cleanupPublishSlotWaiter(waiter);
|
|
1347
|
-
waiter.reject(error);
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
private removePublishSlotWaiter(meshName: string, waiter: ZLinkPublishSlotWaiter): void {
|
|
1351
|
-
const queue = this.publishSlotWaiters.get(meshName);
|
|
1352
|
-
if (queue === undefined) return;
|
|
1353
|
-
const index = queue.waiters.indexOf(waiter, queue.head);
|
|
1354
|
-
if (index >= 0) {
|
|
1355
|
-
queue.waiters[index] = undefined;
|
|
1356
|
-
queue.count -= 1;
|
|
1357
|
-
this.compactPublishSlotWaiters(queue);
|
|
1358
|
-
}
|
|
1359
|
-
if (queue.count === 0) this.publishSlotWaiters.delete(meshName);
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
private cleanupPublishSlotWaiter(waiter: ZLinkPublishSlotWaiter): void {
|
|
1363
|
-
if (waiter.timeout !== undefined) clearTimeout(waiter.timeout);
|
|
1364
|
-
if (waiter.abortHandler !== undefined) {
|
|
1365
|
-
waiter.signal?.removeEventListener('abort', waiter.abortHandler);
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
private rejectPublishSlotWaiters(error: unknown): void {
|
|
1370
|
-
for (const [meshName, queue] of this.publishSlotWaiters) {
|
|
1371
|
-
let waiter: ZLinkPublishSlotWaiter | undefined;
|
|
1372
|
-
while ((waiter = this.takePublishSlotWaiter(queue)) !== undefined) {
|
|
1373
|
-
this.rejectPublishSlotWaiter(meshName, waiter, error);
|
|
1374
|
-
}
|
|
1375
|
-
this.publishSlotWaiters.delete(meshName);
|
|
1376
|
-
}
|
|
1377
|
-
this.activePublishes.clear();
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
private takePublishSlotWaiter(queue: ZLinkPublishSlotQueue): ZLinkPublishSlotWaiter | undefined {
|
|
1381
|
-
while (queue.head < queue.waiters.length && queue.waiters[queue.head] === undefined) {
|
|
1382
|
-
queue.head += 1;
|
|
1383
|
-
}
|
|
1384
|
-
const waiter = queue.waiters[queue.head];
|
|
1385
|
-
if (waiter === undefined) return undefined;
|
|
1386
|
-
queue.waiters[queue.head] = undefined;
|
|
1387
|
-
queue.head += 1;
|
|
1388
|
-
queue.count -= 1;
|
|
1389
|
-
this.compactPublishSlotWaiters(queue);
|
|
1390
|
-
return waiter;
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
private compactPublishSlotWaiters(queue: ZLinkPublishSlotQueue): void {
|
|
1394
|
-
if (queue.count === 0) {
|
|
1395
|
-
queue.waiters.length = 0;
|
|
1396
|
-
queue.head = 0;
|
|
1397
|
-
} else if (queue.head >= 1024 && queue.head * 2 >= queue.waiters.length) {
|
|
1398
|
-
queue.waiters.splice(0, queue.head);
|
|
1399
|
-
queue.head = 0;
|
|
1400
|
-
}
|
|
1401
|
-
}
|
|
1402
1182
|
}
|
|
1403
1183
|
|
|
1404
1184
|
function runtimeShutdownError(): ZLinkFrameworkException {
|
|
@@ -38,14 +38,7 @@ export class ZLinkSpotSerialTurnExecutor {
|
|
|
38
38
|
) {
|
|
39
39
|
this.scheduler = new ZLinkSerialExecutionQueue(
|
|
40
40
|
(record) => this.runQueuedRecord(record),
|
|
41
|
-
|
|
42
|
-
...schedulerOptions,
|
|
43
|
-
capacityError: schedulerOptions?.capacityError ?? (() =>
|
|
44
|
-
createInternalFrameworkException(
|
|
45
|
-
ZLinkFrameworkInternalErrorKind.WorkerQueueFull,
|
|
46
|
-
'Spot execution queue capacity was exceeded.'
|
|
47
|
-
))
|
|
48
|
-
}
|
|
41
|
+
schedulerOptions
|
|
49
42
|
);
|
|
50
43
|
}
|
|
51
44
|
|
|
@@ -41,6 +41,18 @@ const systemTimerClock: ZLinkTimerClock = {
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
type ZLinkTimerOwnerSpot = ZLinkSpot | ZLinkEntrySpot;
|
|
44
|
+
|
|
45
|
+
interface ZLinkTimerFinalization {
|
|
46
|
+
readonly promise: Promise<void>;
|
|
47
|
+
readonly resolve: () => void;
|
|
48
|
+
readonly reject: (cause: unknown) => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface ZLinkTimerFinalizationStart {
|
|
52
|
+
readonly timeout: unknown;
|
|
53
|
+
readonly running: Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
|
|
44
56
|
type ZLinkTimerFailureReporter = (
|
|
45
57
|
tick: ZLinkTimerTick,
|
|
46
58
|
cause: unknown,
|
|
@@ -125,11 +137,13 @@ export class ZLinkSpotTimerRegistry {
|
|
|
125
137
|
? this.executionSerialForTimer?.(name, serial) ?? serial
|
|
126
138
|
: undefined;
|
|
127
139
|
if (this.executionBarrier !== undefined) executionSerial?.setExecutionBarrier(this.executionBarrier);
|
|
140
|
+
let callbackStarted = false;
|
|
128
141
|
const timer = startOutsideStateLane(() => new ZLinkManagedTimer(
|
|
129
142
|
name,
|
|
130
143
|
periodMs,
|
|
131
144
|
normalizeTimerOptions(options),
|
|
132
145
|
async (tick) => {
|
|
146
|
+
callbackStarted = false;
|
|
133
147
|
const timerFlow = createInboundFlow(undefined, 'Timer', this.flowCreationEnabled());
|
|
134
148
|
const operation = () => {
|
|
135
149
|
const current = this.timers.get(name);
|
|
@@ -137,6 +151,7 @@ export class ZLinkSpotTimerRegistry {
|
|
|
137
151
|
return undefined;
|
|
138
152
|
}
|
|
139
153
|
if (!this.executionAllowed()) return undefined;
|
|
154
|
+
callbackStarted = true;
|
|
140
155
|
return runWithFlow(timerFlow, () => handler.handle(spot, tick));
|
|
141
156
|
};
|
|
142
157
|
if (this.executeTimer !== undefined) {
|
|
@@ -146,9 +161,7 @@ export class ZLinkSpotTimerRegistry {
|
|
|
146
161
|
}
|
|
147
162
|
},
|
|
148
163
|
reportFailure,
|
|
149
|
-
() =>
|
|
150
|
-
? !executionSerial!.isExecuting
|
|
151
|
-
: this.isTimerExecuting?.(name) !== true,
|
|
164
|
+
() => callbackStarted,
|
|
152
165
|
this.clock
|
|
153
166
|
));
|
|
154
167
|
const registered = await this.lane.run(() => this.completeAddCore(
|
|
@@ -163,13 +176,18 @@ export class ZLinkSpotTimerRegistry {
|
|
|
163
176
|
|
|
164
177
|
async dispose(): Promise<void> {
|
|
165
178
|
const timers = await this.lane.run(() => {
|
|
166
|
-
const active = [...this.timers.
|
|
179
|
+
const active = [...this.timers.entries()].map(([name, entry]) => ({
|
|
180
|
+
name,
|
|
181
|
+
timer: entry.timer,
|
|
182
|
+
wait: this.executeTimer === undefined
|
|
183
|
+
? true
|
|
184
|
+
: this.isTimerExecuting?.(name) !== true
|
|
185
|
+
}));
|
|
167
186
|
this.timers.clear();
|
|
168
187
|
return active;
|
|
169
188
|
});
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
189
|
+
const finalizations = timers.map(({ timer }) => timer.dispose());
|
|
190
|
+
await Promise.all(finalizations.filter((_, index) => timers[index]!.wait));
|
|
173
191
|
}
|
|
174
192
|
|
|
175
193
|
async captureRelocation(): Promise<readonly ZLinkTimerRelocationState[]> {
|
|
@@ -248,6 +266,8 @@ export class ZLinkSpotTimerRegistry {
|
|
|
248
266
|
}
|
|
249
267
|
|
|
250
268
|
class ZLinkRegisteredTimer implements ZLinkTimer {
|
|
269
|
+
private finalization: Promise<void> | undefined;
|
|
270
|
+
|
|
251
271
|
constructor(
|
|
252
272
|
private readonly registry: ZLinkSpotTimerRegistry,
|
|
253
273
|
private readonly name: string,
|
|
@@ -260,7 +280,18 @@ class ZLinkRegisteredTimer implements ZLinkTimer {
|
|
|
260
280
|
}
|
|
261
281
|
|
|
262
282
|
cancel(signal?: AbortSignal): Promise<void> {
|
|
263
|
-
|
|
283
|
+
if (this.finalization !== undefined) return this.finalization;
|
|
284
|
+
try {
|
|
285
|
+
throwIfAborted(signal);
|
|
286
|
+
} catch (failure) {
|
|
287
|
+
return Promise.reject(failure);
|
|
288
|
+
}
|
|
289
|
+
this.finalization = this.registry.cancel(
|
|
290
|
+
this.name,
|
|
291
|
+
this.generation,
|
|
292
|
+
this.timer
|
|
293
|
+
);
|
|
294
|
+
return this.finalization;
|
|
264
295
|
}
|
|
265
296
|
|
|
266
297
|
dispose(): Promise<void> {
|
|
@@ -278,6 +309,8 @@ export class ZLinkManagedTimer implements ZLinkTimer {
|
|
|
278
309
|
private lastScheduledIndex = 0n;
|
|
279
310
|
private timeout: unknown;
|
|
280
311
|
private running: Promise<void> = Promise.resolve();
|
|
312
|
+
private dispatchFailure: { readonly cause: unknown } | undefined;
|
|
313
|
+
private finalization: ZLinkTimerFinalization | undefined;
|
|
281
314
|
|
|
282
315
|
constructor(
|
|
283
316
|
private readonly name: string,
|
|
@@ -297,9 +330,30 @@ export class ZLinkManagedTimer implements ZLinkTimer {
|
|
|
297
330
|
return this.disposed;
|
|
298
331
|
}
|
|
299
332
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
333
|
+
cancel(_signal?: AbortSignal): Promise<void> {
|
|
334
|
+
if (this.finalization !== undefined) return this.finalization.promise;
|
|
335
|
+
|
|
336
|
+
let resolve!: () => void;
|
|
337
|
+
let reject!: (cause: unknown) => void;
|
|
338
|
+
const promise = new Promise<void>((complete, fail) => {
|
|
339
|
+
resolve = complete;
|
|
340
|
+
reject = fail;
|
|
341
|
+
});
|
|
342
|
+
const finalization = { promise, resolve, reject };
|
|
343
|
+
this.finalization = finalization;
|
|
344
|
+
|
|
345
|
+
try {
|
|
346
|
+
const preparation = this.lane.run(() => this.cancelCore());
|
|
347
|
+
void preparation.then(
|
|
348
|
+
start => {
|
|
349
|
+
void this.completeFinalization(start, finalization).then(undefined, reject);
|
|
350
|
+
},
|
|
351
|
+
reject
|
|
352
|
+
);
|
|
353
|
+
} catch (failure) {
|
|
354
|
+
reject(failure);
|
|
355
|
+
}
|
|
356
|
+
return promise;
|
|
303
357
|
}
|
|
304
358
|
|
|
305
359
|
dispose(): Promise<void> {
|
|
@@ -363,7 +417,13 @@ export class ZLinkManagedTimer implements ZLinkTimer {
|
|
|
363
417
|
: Math.max(0, Number(this.lastScheduledIndex + 1n) * this.periodMs - this.elapsedMs());
|
|
364
418
|
this.timeout = this.clock.setTimeout(() => {
|
|
365
419
|
this.timeout = undefined;
|
|
366
|
-
|
|
420
|
+
const execution = startOutsideStateLane(() => this.fire());
|
|
421
|
+
this.running = execution.then(
|
|
422
|
+
() => undefined,
|
|
423
|
+
cause => this.lane.run(() => {
|
|
424
|
+
this.dispatchFailure ??= { cause };
|
|
425
|
+
})
|
|
426
|
+
);
|
|
367
427
|
}, delayMs);
|
|
368
428
|
}
|
|
369
429
|
|
|
@@ -388,14 +448,45 @@ export class ZLinkManagedTimer implements ZLinkTimer {
|
|
|
388
448
|
await this.lane.run(() => this.completeFireCore(prepared.scheduledIndex, shouldContinue));
|
|
389
449
|
}
|
|
390
450
|
|
|
391
|
-
private cancelCore():
|
|
392
|
-
if (this.disposed) return { running: Promise.resolve() };
|
|
451
|
+
private cancelCore(): ZLinkTimerFinalizationStart {
|
|
393
452
|
this.disposed = true;
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
453
|
+
const timeout = this.timeout;
|
|
454
|
+
this.timeout = undefined;
|
|
455
|
+
return {
|
|
456
|
+
timeout,
|
|
457
|
+
running: this.shouldWaitForRunningOnCancel()
|
|
458
|
+
? this.running
|
|
459
|
+
: Promise.resolve()
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
private async completeFinalization(
|
|
464
|
+
start: ZLinkTimerFinalizationStart,
|
|
465
|
+
finalization: ZLinkTimerFinalization
|
|
466
|
+
): Promise<void> {
|
|
467
|
+
const failures: unknown[] = [];
|
|
468
|
+
if (start.timeout !== undefined) {
|
|
469
|
+
try {
|
|
470
|
+
this.clock.clearTimeout(start.timeout);
|
|
471
|
+
} catch (failure) {
|
|
472
|
+
failures.push(failure);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
try {
|
|
476
|
+
await start.running;
|
|
477
|
+
} catch (failure) {
|
|
478
|
+
failures.push(failure);
|
|
479
|
+
}
|
|
480
|
+
const dispatchFailure = await this.lane.run(() => this.dispatchFailure);
|
|
481
|
+
if (dispatchFailure !== undefined) failures.push(dispatchFailure.cause);
|
|
482
|
+
|
|
483
|
+
if (failures.length === 0) {
|
|
484
|
+
finalization.resolve();
|
|
485
|
+
} else if (failures.length === 1) {
|
|
486
|
+
finalization.reject(failures[0]);
|
|
487
|
+
} else {
|
|
488
|
+
finalization.reject(new AggregateError(failures, `Timer '${this.name}' cleanup failed.`));
|
|
397
489
|
}
|
|
398
|
-
return { running: this.shouldWaitForRunningOnCancel() ? this.running : Promise.resolve() };
|
|
399
490
|
}
|
|
400
491
|
|
|
401
492
|
private prepareCaptureCore(): { readonly running: Promise<void> } {
|
|
@@ -16,13 +16,7 @@ import {
|
|
|
16
16
|
export class ZLinkSessionSerialExecutor {
|
|
17
17
|
private readonly scheduler = new ZLinkSerialExecutionQueue(
|
|
18
18
|
async (record) => this.execute(record),
|
|
19
|
-
{
|
|
20
|
-
capacityError: (lane) => createInternalFrameworkException(
|
|
21
|
-
ZLinkFrameworkInternalErrorKind.WorkerQueueFull,
|
|
22
|
-
`Stream Session ${lane} execution capacity is full.`,
|
|
23
|
-
true
|
|
24
|
-
)
|
|
25
|
-
}
|
|
19
|
+
{}
|
|
26
20
|
);
|
|
27
21
|
private closed = false;
|
|
28
22
|
|
|
@@ -92,29 +92,21 @@ const systemLivenessClock: ZLinkStreamLivenessClock = {
|
|
|
92
92
|
clearTimer: (timer) => clearTimeout(timer as ReturnType<typeof setTimeout>)
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
-
class
|
|
95
|
+
class ZLinkStreamReceiveWake {
|
|
96
96
|
private pending?: Promise<void>;
|
|
97
97
|
private resolvePending?: () => void;
|
|
98
|
-
private timer?: ReturnType<typeof setTimeout>;
|
|
99
98
|
private readonly finish = (): void => {
|
|
100
|
-
if (this.timer !== undefined) {
|
|
101
|
-
clearTimeout(this.timer);
|
|
102
|
-
this.timer = undefined;
|
|
103
|
-
}
|
|
104
99
|
const resolve = this.resolvePending;
|
|
105
100
|
this.pending = undefined;
|
|
106
101
|
this.resolvePending = undefined;
|
|
107
102
|
resolve?.();
|
|
108
103
|
};
|
|
109
104
|
|
|
110
|
-
constructor(private readonly delayMs: number) {}
|
|
111
|
-
|
|
112
105
|
wait(): Promise<void> {
|
|
113
106
|
if (this.pending !== undefined) return this.pending;
|
|
114
107
|
this.pending = new Promise<void>((resolve) => {
|
|
115
108
|
this.resolvePending = resolve;
|
|
116
109
|
});
|
|
117
|
-
this.timer = setTimeout(this.finish, this.delayMs);
|
|
118
110
|
return this.pending;
|
|
119
111
|
}
|
|
120
112
|
|
|
@@ -892,7 +884,7 @@ export class ZLinkStreamSessionNodeRuntime {
|
|
|
892
884
|
private stopped = false;
|
|
893
885
|
private readonly receiveAbortController = new AbortController();
|
|
894
886
|
private receiveLoop: Promise<void> | undefined;
|
|
895
|
-
private readonly
|
|
887
|
+
private readonly receiveWake = new ZLinkStreamReceiveWake();
|
|
896
888
|
private receiveWorkSinceYield = 0;
|
|
897
889
|
constructor(
|
|
898
890
|
private readonly options: ZLinkStreamSessionNodeRuntimeOptions & ZLinkStreamLivenessOptions
|
|
@@ -967,14 +959,15 @@ export class ZLinkStreamSessionNodeRuntime {
|
|
|
967
959
|
}
|
|
968
960
|
if (!this.options.readablePoller.wait(0)) {
|
|
969
961
|
permit.releaseAfterInternalProcessing();
|
|
970
|
-
await this.
|
|
962
|
+
await this.waitReceiveLoopWake(signal);
|
|
971
963
|
continue;
|
|
972
964
|
}
|
|
973
965
|
packet = this.takePacket();
|
|
974
966
|
if (!this.options.socket.recvPacket(packet, ZLINK_RECV_DONT_WAIT)) {
|
|
967
|
+
this.options.readablePoller.markDrained();
|
|
975
968
|
permit.releaseAfterInternalProcessing();
|
|
976
969
|
this.recyclePacket(packet);
|
|
977
|
-
await this.
|
|
970
|
+
await this.waitReceiveLoopWake(signal);
|
|
978
971
|
continue;
|
|
979
972
|
}
|
|
980
973
|
packetTransferred = true;
|
|
@@ -987,10 +980,10 @@ export class ZLinkStreamSessionNodeRuntime {
|
|
|
987
980
|
if (!this.isReceiveStopped(signal) && !packetTransferred) {
|
|
988
981
|
this.options.onError?.(error);
|
|
989
982
|
this.receiveWorkSinceYield = 0;
|
|
990
|
-
await
|
|
983
|
+
await new Promise<void>((resolve) => setImmediate(resolve));
|
|
991
984
|
} else if (!this.isReceiveStopped(signal)) {
|
|
992
985
|
this.receiveWorkSinceYield = 0;
|
|
993
|
-
await
|
|
986
|
+
await new Promise<void>((resolve) => setImmediate(resolve));
|
|
994
987
|
}
|
|
995
988
|
continue;
|
|
996
989
|
}
|
|
@@ -1350,12 +1343,15 @@ export class ZLinkStreamSessionNodeRuntime {
|
|
|
1350
1343
|
});
|
|
1351
1344
|
}
|
|
1352
1345
|
|
|
1353
|
-
private
|
|
1354
|
-
|
|
1346
|
+
private async waitReceiveLoopWake(signal: AbortSignal): Promise<void> {
|
|
1347
|
+
await Promise.race([
|
|
1348
|
+
this.options.readablePoller.waitForReadable(signal),
|
|
1349
|
+
this.receiveWake.wait()
|
|
1350
|
+
]);
|
|
1355
1351
|
}
|
|
1356
1352
|
|
|
1357
1353
|
private wakeReceiveLoop(): void {
|
|
1358
|
-
this.
|
|
1354
|
+
this.receiveWake.wake();
|
|
1359
1355
|
}
|
|
1360
1356
|
|
|
1361
1357
|
private getOrCreateSession(routingId: unknown): ZLinkStreamSessionRuntime | undefined {
|
|
@@ -7,8 +7,7 @@ import { ZLinkConfigurationException } from '../configuration';
|
|
|
7
7
|
import {
|
|
8
8
|
defaultWorkerMaxThreads,
|
|
9
9
|
DEFAULT_WORKER_IDLE_TIMEOUT_MS,
|
|
10
|
-
DEFAULT_WORKER_MIN_THREADS
|
|
11
|
-
DEFAULT_WORKER_QUEUE_LENGTH
|
|
10
|
+
DEFAULT_WORKER_MIN_THREADS
|
|
12
11
|
} from '../../contracts/Configuration/InternalDefaults';
|
|
13
12
|
import { createAbortError } from '../abort';
|
|
14
13
|
import {
|
|
@@ -24,20 +23,17 @@ export interface ZLinkWorkerRuntimeOptions {
|
|
|
24
23
|
readonly minThreads: number;
|
|
25
24
|
readonly maxThreads: number;
|
|
26
25
|
readonly idleTimeoutMs: number;
|
|
27
|
-
readonly maxQueueLength: number;
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
export function resolveWorkerRuntimeOptions(options?: ZLinkWorkerOptions): ZLinkWorkerRuntimeOptions {
|
|
31
29
|
const resolved = {
|
|
32
30
|
minThreads: options?.minThreads ?? DEFAULT_WORKER_MIN_THREADS,
|
|
33
31
|
maxThreads: options?.maxThreads ?? defaultWorkerMaxThreads(),
|
|
34
|
-
idleTimeoutMs: options?.idleTimeoutMs ?? DEFAULT_WORKER_IDLE_TIMEOUT_MS
|
|
35
|
-
maxQueueLength: options?.maxQueueLength ?? DEFAULT_WORKER_QUEUE_LENGTH
|
|
32
|
+
idleTimeoutMs: options?.idleTimeoutMs ?? DEFAULT_WORKER_IDLE_TIMEOUT_MS
|
|
36
33
|
};
|
|
37
34
|
requireNonNegativeInteger('Worker minThreads', resolved.minThreads);
|
|
38
35
|
requirePositiveInteger('Worker maxThreads', resolved.maxThreads);
|
|
39
36
|
requireNonNegativeInteger('Worker idleTimeoutMs', resolved.idleTimeoutMs);
|
|
40
|
-
requirePositiveInteger('Worker maxQueueLength', resolved.maxQueueLength);
|
|
41
37
|
if (resolved.maxThreads < resolved.minThreads) {
|
|
42
38
|
throw new ZLinkConfigurationException('Worker maxThreads must be greater than or equal to minThreads.');
|
|
43
39
|
}
|
|
@@ -98,9 +94,6 @@ class ZLinkCpuWorkerPool {
|
|
|
98
94
|
if (signal?.aborted === true) {
|
|
99
95
|
return Promise.reject(createAbortError());
|
|
100
96
|
}
|
|
101
|
-
if (this.queueCount >= this.options.maxQueueLength) {
|
|
102
|
-
return Promise.reject(workerQueueFull(this.options.maxQueueLength));
|
|
103
|
-
}
|
|
104
97
|
return new Promise<T>((resolve, reject) => {
|
|
105
98
|
const job = {
|
|
106
99
|
source: work.toString(),
|
|
@@ -535,14 +528,6 @@ class ZLinkSerialDeliveredPromise<T> implements Promise<T> {
|
|
|
535
528
|
}
|
|
536
529
|
}
|
|
537
530
|
|
|
538
|
-
function workerQueueFull(maxQueueLength: number): ZLinkFrameworkException {
|
|
539
|
-
return createInternalFrameworkException(
|
|
540
|
-
ZLinkFrameworkInternalErrorKind.WorkerQueueFull,
|
|
541
|
-
`CPU worker queue is full (maxQueueLength=${maxQueueLength}).`,
|
|
542
|
-
true
|
|
543
|
-
);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
531
|
function workerTimedOut(timeoutMs: number): ZLinkFrameworkException {
|
|
547
532
|
return createInternalFrameworkException(
|
|
548
533
|
ZLinkFrameworkInternalErrorKind.WorkerTimedOut,
|