@zlink-systems/framework 0.30.1 → 0.30.2
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/Common/ZLinkEncodedPayload.d.ts +6 -0
- package/dist/contracts/Common/ZLinkEncodedPayload.js +18 -3
- package/dist/contracts/Common/encoded-payload-storage.d.ts +1 -1
- package/dist/contracts/Common/encoded-payload-storage.js +5 -2
- package/dist/contracts/Configuration/Builders.d.ts +1 -1
- package/dist/contracts/Configuration/RegistrationBuilderPolicy.d.ts +2 -1
- package/dist/contracts/Configuration/RegistrationBuilderPolicy.js +5 -1
- package/dist/contracts/Configuration/RegistrationBuilders.js +2 -2
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -0
- package/dist/contracts/Dispatch/ZLinkDispatchOptions.d.ts +2 -2
- package/dist/contracts/Locations/Rows.d.ts +2 -0
- package/dist/contracts/Spots/Contracts.d.ts +4 -0
- package/dist/contracts/Spots/ZLinkSpot.d.ts +3 -3
- package/dist/runtime/abort.d.ts +5 -0
- package/dist/runtime/abort.js +10 -0
- package/dist/runtime/actors/actor-authority-publication.js +3 -2
- package/dist/runtime/actors/actor-client.d.ts +0 -1
- package/dist/runtime/actors/actor-client.js +38 -45
- package/dist/runtime/actors/actor-context.d.ts +2 -0
- package/dist/runtime/actors/actor-context.js +46 -9
- package/dist/runtime/actors/actor-handoff.d.ts +7 -3
- package/dist/runtime/actors/actor-handoff.js +212 -88
- package/dist/runtime/actors/actor-join-relocation.d.ts +2 -0
- package/dist/runtime/actors/actor-local-native-join.js +4 -0
- package/dist/runtime/actors/actor-mailbox.js +27 -1
- package/dist/runtime/actors/actor-remote-joiner.js +3 -1
- package/dist/runtime/actors/actor-reply.d.ts +4 -0
- package/dist/runtime/actors/actor-reply.js +29 -0
- package/dist/runtime/actors/actor-runtime-contracts.d.ts +2 -0
- package/dist/runtime/actors/actor-runtime-state.js +2 -20
- package/dist/runtime/actors/actor-source-transfer.d.ts +1 -17
- package/dist/runtime/actors/index.d.ts +6 -2
- package/dist/runtime/actors/index.js +37 -21
- package/dist/runtime/actors/session-disconnected-wire.d.ts +8 -0
- package/dist/runtime/actors/session-disconnected-wire.js +48 -0
- package/dist/runtime/admission.d.ts +9 -1
- package/dist/runtime/admission.js +55 -32
- package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +13 -0
- package/dist/runtime/application-jobs/application-job-queue-scope.js +27 -1
- package/dist/runtime/application-jobs/contracts.d.ts +2 -0
- package/dist/runtime/backend/contracts/index.d.ts +19 -3
- package/dist/runtime/backend/mesh-actor-session-node-adapter.js +2 -2
- package/dist/runtime/backend/mesh-completion-table.js +5 -0
- package/dist/runtime/backend/mesh-dispatch-pump.d.ts +26 -2
- package/dist/runtime/backend/mesh-dispatch-pump.js +193 -107
- package/dist/runtime/backend/node/node-raw-binding-port.js +90 -58
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +33 -8
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +142 -143
- package/dist/runtime/backend/node/node-socket-backend-adapter.d.ts +1 -1
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +5 -5
- package/dist/runtime/channels/channel-clients.d.ts +1 -0
- package/dist/runtime/channels/channel-clients.js +20 -5
- package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +22 -3
- package/dist/runtime/channels/channel-dispatch-pipeline.js +103 -57
- package/dist/runtime/channels/channel-dispatchers.d.ts +3 -2
- package/dist/runtime/channels/channel-dispatchers.js +44 -37
- package/dist/runtime/channels/channel-envelope.d.ts +1 -0
- package/dist/runtime/channels/channel-envelope.js +74 -31
- package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +4 -3
- package/dist/runtime/channels/channel-runtime-lifecycle.js +19 -24
- package/dist/runtime/channels/channel-runtime-manager.d.ts +3 -2
- package/dist/runtime/channels/channel-runtime-manager.js +4 -4
- package/dist/runtime/channels/channel-transports.d.ts +0 -1
- package/dist/runtime/channels/channel-transports.js +35 -33
- package/dist/runtime/channels/client-server-location-runtime.js +5 -4
- package/dist/runtime/channels/dispatch-error-reporter.d.ts +2 -1
- package/dist/runtime/channels/dispatch-error-reporter.js +2 -1
- package/dist/runtime/channels/fanout-location-runtime.d.ts +1 -1
- package/dist/runtime/channels/fanout-location-runtime.js +3 -3
- package/dist/runtime/diagnostics/flow-context.js +19 -7
- package/dist/runtime/diagnostics/index.js +4 -9
- package/dist/runtime/diagnostics/message-flow.d.ts +8 -0
- package/dist/runtime/diagnostics/message-flow.js +35 -5
- package/dist/runtime/diagnostics/runtime-metrics.d.ts +2 -2
- package/dist/runtime/diagnostics/runtime-metrics.js +10 -8
- package/dist/runtime/execution/index.d.ts +4 -2
- package/dist/runtime/execution/index.js +33 -10
- package/dist/runtime/execution/serial-execution-queue.d.ts +4 -0
- package/dist/runtime/execution/serial-execution-queue.js +21 -2
- package/dist/runtime/execution/state-lane.d.ts +2 -0
- package/dist/runtime/execution/state-lane.js +5 -0
- package/dist/runtime/foundation/operation-registry.d.ts +25 -1
- package/dist/runtime/foundation/operation-registry.js +86 -18
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +39 -3
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +151 -55
- package/dist/runtime/foundation/service-liveness-registry.d.ts +12 -1
- package/dist/runtime/foundation/service-liveness-registry.js +38 -0
- package/dist/runtime/foundation/service-mailbox.d.ts +7 -0
- package/dist/runtime/foundation/service-maintenance-runtime.js +1 -6
- package/dist/runtime/foundation/service-relocation-object-owner.d.ts +2 -3
- package/dist/runtime/foundation/service-relocation-object-owner.js +48 -35
- package/dist/runtime/foundation/service-relocation-runtime.js +7 -3
- package/dist/runtime/foundation/service-runtime-contracts.d.ts +8 -1
- package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +2 -1
- package/dist/runtime/foundation/service-stateful-registry.d.ts +5 -4
- package/dist/runtime/foundation/service-stateful-registry.js +24 -26
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +72 -6
- package/dist/runtime/foundation/service-stateful-runtime.js +444 -117
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +14 -1
- package/dist/runtime/foundation/service-stateful-wire-codec.js +48 -27
- package/dist/runtime/foundation/service-wire-binary-primitives.d.ts +6 -1
- package/dist/runtime/foundation/service-wire-binary-primitives.js +27 -11
- package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
- package/dist/runtime/foundation/service-wire-constants.generated.js +1 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +10 -0
- package/dist/runtime/foundation/service-wire-m6a-codec.js +166 -53
- package/dist/runtime/host/actor-packet-relay.d.ts +6 -4
- package/dist/runtime/host/actor-packet-relay.js +59 -72
- package/dist/runtime/host/actor-placement-coordinator.js +25 -8
- package/dist/runtime/host/actor-transfer-runtime.d.ts +7 -31
- package/dist/runtime/host/actor-transfer-runtime.js +85 -331
- package/dist/runtime/host/application-job-queue.d.ts +24 -3
- package/dist/runtime/host/application-job-queue.js +28 -41
- package/dist/runtime/host/index.d.ts +5 -1
- package/dist/runtime/host/index.js +171 -97
- package/dist/runtime/host/location-runtime-owner.js +2 -6
- package/dist/runtime/host/mesh-router-resolver.d.ts +4 -0
- package/dist/runtime/host/mesh-router-resolver.js +14 -6
- package/dist/runtime/host/remote-bound-session-relay.d.ts +2 -4
- package/dist/runtime/host/remote-bound-session-relay.js +71 -190
- package/dist/runtime/host/route-mesh-runtime.d.ts +2 -2
- package/dist/runtime/host/route-mesh-runtime.js +5 -3
- package/dist/runtime/host/runtime-shutdown.d.ts +1 -0
- package/dist/runtime/host/runtime-shutdown.js +3 -2
- package/dist/runtime/host/service-relocation-host-runtime.d.ts +17 -7
- package/dist/runtime/host/service-relocation-host-runtime.js +550 -367
- package/dist/runtime/host/spot-runtime-options-factory.js +15 -7
- package/dist/runtime/host/user-spot-creation-coordinator.js +33 -84
- package/dist/runtime/locations/auto-connect-loop.d.ts +2 -0
- package/dist/runtime/locations/auto-connect-loop.js +22 -14
- package/dist/runtime/locations/auto-connect-reconciler.js +0 -6
- package/dist/runtime/locations/auto-connect-types.d.ts +0 -1
- package/dist/runtime/locations/descriptor-publication.d.ts +4 -0
- package/dist/runtime/locations/descriptor-publication.js +11 -0
- package/dist/runtime/locations/domain-store-contract.d.ts +1 -0
- package/dist/runtime/locations/in-memory-authority-store.d.ts +7 -1
- package/dist/runtime/locations/in-memory-authority-store.js +56 -19
- package/dist/runtime/locations/in-memory-location-store.js +1 -10
- package/dist/runtime/locations/index.d.ts +1 -0
- package/dist/runtime/locations/index.js +1 -0
- package/dist/runtime/locations/lease-tracker.js +1 -6
- package/dist/runtime/locations/location-store-repository.d.ts +51 -1
- package/dist/runtime/locations/location-store-repository.js +363 -139
- package/dist/runtime/locations/relocation-blob.d.ts +18 -1
- package/dist/runtime/locations/relocation-blob.js +52 -1
- package/dist/runtime/locations/request-source-resolver.d.ts +37 -0
- package/dist/runtime/locations/request-source-resolver.js +80 -0
- package/dist/runtime/locations/resolvers.js +1 -1
- package/dist/runtime/locations/runtime.d.ts +6 -2
- package/dist/runtime/locations/runtime.js +37 -30
- package/dist/runtime/messaging/payload-codec.d.ts +7 -1
- package/dist/runtime/messaging/payload-codec.js +7 -6
- package/dist/runtime/messaging/request-result.js +29 -26
- package/dist/runtime/protocol/service_wire_codec.generated.d.ts +19 -1
- package/dist/runtime/protocol/service_wire_codec.generated.js +85 -0
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.d.ts +7 -1
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +23 -7
- package/dist/runtime/spots/formal-remote-actor-admission-registry.js +3 -2
- package/dist/runtime/spots/index.d.ts +18 -11
- package/dist/runtime/spots/index.js +234 -176
- package/dist/runtime/spots/spot-activation-registry.d.ts +4 -2
- package/dist/runtime/spots/spot-activation-registry.js +21 -11
- package/dist/runtime/spots/spot-activation-state.d.ts +1 -1
- package/dist/runtime/spots/spot-activation-state.js +7 -7
- package/dist/runtime/spots/spot-activation.d.ts +6 -3
- package/dist/runtime/spots/spot-activation.js +123 -44
- package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +1 -0
- package/dist/runtime/spots/spot-actor-admission-coordinator.js +13 -22
- package/dist/runtime/spots/spot-actor-membership.d.ts +0 -1
- package/dist/runtime/spots/spot-actor-membership.js +0 -8
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +33 -8
- package/dist/runtime/spots/spot-actor-packet-drain.js +5 -33
- package/dist/runtime/spots/spot-entry-activation.d.ts +2 -0
- package/dist/runtime/spots/spot-entry-activation.js +14 -9
- package/dist/runtime/spots/spot-handler-registry.d.ts +4 -3
- package/dist/runtime/spots/spot-handler-registry.js +6 -2
- package/dist/runtime/spots/spot-manager-internal-contracts.d.ts +2 -2
- package/dist/runtime/spots/spot-manager-public.js +2 -2
- package/dist/runtime/spots/spot-node-autoconnect.d.ts +2 -1
- package/dist/runtime/spots/spot-node-autoconnect.js +18 -39
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +19 -2
- package/dist/runtime/spots/spot-node-runtime-manager.js +115 -50
- package/dist/runtime/spots/spot-outbound.js +3 -3
- package/dist/runtime/spots/spot-remote-codec.js +5 -1
- package/dist/runtime/spots/spot-runtime-ports.d.ts +0 -1
- package/dist/runtime/spots/spot-serial-executor.d.ts +10 -0
- package/dist/runtime/spots/spot-serial-executor.js +61 -8
- package/dist/runtime/spots/spot-serial-turn-executor.js +19 -2
- package/dist/runtime/spots/spot-subscription-dispatch.js +1 -1
- package/dist/runtime/spots/spot-timer.d.ts +57 -5
- package/dist/runtime/spots/spot-timer.js +287 -103
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +42 -11
- package/dist/runtime/streams/actor-session-binding-registry.js +165 -48
- package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +8 -4
- package/dist/runtime/streams/bound-actor-relay-sender.d.ts +2 -1
- package/dist/runtime/streams/bound-actor-relay-sender.js +15 -8
- package/dist/runtime/streams/bound-session-service.d.ts +1 -1
- package/dist/runtime/streams/bound-session-service.js +6 -11
- package/dist/runtime/streams/index.d.ts +3 -2
- package/dist/runtime/streams/index.js +6 -4
- package/dist/runtime/streams/managed-stream.js +3 -2
- package/dist/runtime/streams/native-fallback-bound-session.js +8 -73
- package/dist/runtime/streams/session-actor-coordinator.d.ts +2 -8
- package/dist/runtime/streams/session-actor-coordinator.js +15 -32
- package/dist/runtime/streams/stream-binding-runtime-ports.d.ts +3 -10
- package/dist/runtime/streams/stream-session-runtime.js +1 -4
- package/dist/runtime/workers/index.js +1 -1
- package/package.json +3 -3
- package/src/contracts/Common/ZLinkEncodedPayload.ts +24 -4
- package/src/contracts/Common/encoded-payload-storage.ts +6 -3
- package/src/contracts/Configuration/Builders.ts +4 -1
- package/src/contracts/Configuration/RegistrationBuilderPolicy.ts +7 -2
- package/src/contracts/Configuration/RegistrationBuilders.ts +6 -2
- package/src/contracts/Configuration/RegistrationTypes.ts +1 -0
- package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +2 -1
- package/src/contracts/Locations/Rows.ts +2 -0
- package/src/contracts/Spots/Contracts.ts +8 -0
- package/src/contracts/Spots/ZLinkSpot.ts +3 -3
- package/src/runtime/abort.ts +12 -0
- package/src/runtime/actors/actor-authority-publication.ts +3 -2
- package/src/runtime/actors/actor-client.ts +61 -70
- package/src/runtime/actors/actor-context.ts +49 -9
- package/src/runtime/actors/actor-handoff.ts +282 -106
- package/src/runtime/actors/actor-join-relocation.ts +2 -0
- package/src/runtime/actors/actor-local-native-join.ts +4 -0
- package/src/runtime/actors/actor-mailbox.ts +40 -2
- package/src/runtime/actors/actor-remote-joiner.ts +6 -1
- package/src/runtime/actors/actor-reply.ts +42 -0
- package/src/runtime/actors/actor-runtime-contracts.ts +2 -0
- package/src/runtime/actors/actor-runtime-state.ts +2 -20
- package/src/runtime/actors/actor-source-transfer.ts +1 -34
- package/src/runtime/actors/index.ts +41 -27
- package/src/runtime/actors/session-disconnected-wire.ts +66 -0
- package/src/runtime/admission.ts +69 -34
- package/src/runtime/application-jobs/application-job-queue-scope.ts +33 -1
- package/src/runtime/application-jobs/contracts.ts +2 -0
- package/src/runtime/backend/contracts/index.ts +39 -4
- package/src/runtime/backend/mesh-actor-session-node-adapter.ts +5 -3
- package/src/runtime/backend/mesh-completion-table.ts +4 -0
- package/src/runtime/backend/mesh-dispatch-pump.ts +223 -124
- package/src/runtime/backend/node/node-raw-binding-port.ts +106 -70
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +221 -170
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +5 -5
- package/src/runtime/channels/channel-clients.ts +49 -24
- package/src/runtime/channels/channel-dispatch-pipeline.ts +173 -124
- package/src/runtime/channels/channel-dispatchers.ts +116 -72
- package/src/runtime/channels/channel-envelope.ts +105 -36
- package/src/runtime/channels/channel-runtime-lifecycle.ts +13 -13
- package/src/runtime/channels/channel-runtime-manager.ts +12 -5
- package/src/runtime/channels/channel-transports.ts +49 -58
- package/src/runtime/channels/client-server-location-runtime.ts +11 -10
- package/src/runtime/channels/dispatch-error-reporter.ts +4 -2
- package/src/runtime/channels/fanout-location-runtime.ts +17 -9
- package/src/runtime/diagnostics/flow-context.ts +20 -7
- package/src/runtime/diagnostics/index.ts +1 -7
- package/src/runtime/diagnostics/message-flow.ts +47 -9
- package/src/runtime/diagnostics/runtime-metrics.ts +12 -10
- package/src/runtime/execution/index.ts +40 -14
- package/src/runtime/execution/serial-execution-queue.ts +31 -4
- package/src/runtime/execution/state-lane.ts +7 -0
- package/src/runtime/foundation/operation-registry.ts +98 -20
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +244 -67
- package/src/runtime/foundation/service-liveness-registry.ts +39 -0
- package/src/runtime/foundation/service-mailbox.ts +8 -0
- package/src/runtime/foundation/service-maintenance-runtime.ts +1 -8
- package/src/runtime/foundation/service-relocation-object-owner.ts +54 -37
- package/src/runtime/foundation/service-relocation-runtime.ts +7 -3
- package/src/runtime/foundation/service-runtime-contracts.ts +12 -1
- package/src/runtime/foundation/service-session-binding-ingress-port.ts +2 -1
- package/src/runtime/foundation/service-stateful-registry.ts +38 -35
- package/src/runtime/foundation/service-stateful-runtime.ts +667 -152
- package/src/runtime/foundation/service-stateful-wire-codec.ts +49 -11
- package/src/runtime/foundation/service-wire-binary-primitives.ts +35 -12
- package/src/runtime/foundation/service-wire-constants.generated.ts +1 -0
- package/src/runtime/foundation/service-wire-m6a-codec.ts +187 -51
- package/src/runtime/host/actor-packet-relay.ts +113 -94
- package/src/runtime/host/actor-placement-coordinator.ts +31 -8
- package/src/runtime/host/actor-transfer-runtime.ts +104 -430
- package/src/runtime/host/application-job-queue.ts +29 -44
- package/src/runtime/host/index.ts +286 -129
- package/src/runtime/host/location-runtime-owner.ts +2 -6
- package/src/runtime/host/mesh-router-resolver.ts +13 -5
- package/src/runtime/host/remote-bound-session-relay.ts +86 -248
- package/src/runtime/host/route-mesh-runtime.ts +11 -5
- package/src/runtime/host/runtime-shutdown.ts +15 -4
- package/src/runtime/host/service-relocation-host-runtime.ts +784 -475
- package/src/runtime/host/spot-runtime-options-factory.ts +18 -7
- package/src/runtime/host/user-spot-creation-coordinator.ts +41 -100
- package/src/runtime/locations/auto-connect-loop.ts +22 -14
- package/src/runtime/locations/auto-connect-reconciler.ts +0 -6
- package/src/runtime/locations/auto-connect-types.ts +0 -1
- package/src/runtime/locations/descriptor-publication.ts +7 -0
- package/src/runtime/locations/domain-store-contract.ts +5 -0
- package/src/runtime/locations/in-memory-authority-store.ts +85 -23
- package/src/runtime/locations/in-memory-location-store.ts +1 -12
- package/src/runtime/locations/index.ts +1 -0
- package/src/runtime/locations/lease-tracker.ts +2 -9
- package/src/runtime/locations/location-store-repository.ts +628 -152
- package/src/runtime/locations/relocation-blob.ts +75 -1
- package/src/runtime/locations/request-source-resolver.ts +138 -0
- package/src/runtime/locations/resolvers.ts +2 -2
- package/src/runtime/locations/runtime.ts +70 -40
- package/src/runtime/messaging/payload-codec.ts +8 -11
- package/src/runtime/messaging/request-result.ts +33 -29
- package/src/runtime/protocol/service_wire_codec.generated.ts +87 -4
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +13 -10
- package/src/runtime/spots/formal-remote-actor-admission-registry.ts +3 -2
- package/src/runtime/spots/index.ts +375 -281
- package/src/runtime/spots/spot-activation-registry.ts +25 -13
- package/src/runtime/spots/spot-activation-state.ts +12 -19
- package/src/runtime/spots/spot-activation.ts +170 -86
- package/src/runtime/spots/spot-actor-admission-coordinator.ts +25 -55
- package/src/runtime/spots/spot-actor-membership.ts +0 -15
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +56 -11
- package/src/runtime/spots/spot-actor-packet-drain.ts +9 -52
- package/src/runtime/spots/spot-entry-activation.ts +24 -19
- package/src/runtime/spots/spot-handler-registry.ts +15 -4
- package/src/runtime/spots/spot-manager-internal-contracts.ts +2 -2
- package/src/runtime/spots/spot-manager-public.ts +2 -2
- package/src/runtime/spots/spot-node-autoconnect.ts +22 -45
- package/src/runtime/spots/spot-node-runtime-manager.ts +197 -67
- package/src/runtime/spots/spot-outbound.ts +3 -3
- package/src/runtime/spots/spot-remote-codec.ts +9 -2
- package/src/runtime/spots/spot-runtime-ports.ts +0 -1
- package/src/runtime/spots/spot-serial-executor.ts +97 -25
- package/src/runtime/spots/spot-serial-turn-executor.ts +22 -3
- package/src/runtime/spots/spot-subscription-dispatch.ts +1 -1
- package/src/runtime/spots/spot-timer.ts +413 -157
- package/src/runtime/streams/actor-session-binding-registry.ts +236 -64
- package/src/runtime/streams/actor-session-binding-runtime-owner.ts +16 -8
- package/src/runtime/streams/bound-actor-relay-sender.ts +23 -9
- package/src/runtime/streams/bound-session-service.ts +7 -10
- package/src/runtime/streams/index.ts +12 -6
- package/src/runtime/streams/managed-stream.ts +4 -2
- package/src/runtime/streams/native-fallback-bound-session.ts +14 -103
- package/src/runtime/streams/session-actor-coordinator.ts +39 -88
- package/src/runtime/streams/stream-binding-runtime-ports.ts +3 -9
- package/src/runtime/streams/stream-session-runtime.ts +1 -5
- package/src/runtime/workers/index.ts +1 -1
|
@@ -18,6 +18,10 @@ import type {
|
|
|
18
18
|
import { ZLinkTimerOverrunPolicy } from '../../contracts';
|
|
19
19
|
import { validateTimerRegistration } from '../../contracts/Configuration/TimerRegistrationValidator';
|
|
20
20
|
import { throwIfAborted } from '../abort';
|
|
21
|
+
import {
|
|
22
|
+
ZLinkFrameworkInternalErrorKind,
|
|
23
|
+
createInternalFrameworkException
|
|
24
|
+
} from '../framework-errors-internal';
|
|
21
25
|
import { ZLinkSpotSerialTurnExecutor } from './spot-serial-turn-executor';
|
|
22
26
|
import {
|
|
23
27
|
prepareHandlerInstance,
|
|
@@ -25,10 +29,7 @@ import {
|
|
|
25
29
|
} from '../handlers/handler-instance-scope';
|
|
26
30
|
import { createInboundFlow, runWithFlow } from '../diagnostics/flow-context';
|
|
27
31
|
import type { ZLinkExecutionBarrier } from '../execution';
|
|
28
|
-
import { ZLinkStateLane } from '../execution/state-lane';
|
|
29
|
-
import { AsyncResource } from 'node:async_hooks';
|
|
30
|
-
|
|
31
|
-
const detachedTimerStateLaneResource = new AsyncResource('zlink:spot-timer');
|
|
32
|
+
import { startOutsideStateLane, ZLinkStateLane } from '../execution/state-lane';
|
|
32
33
|
|
|
33
34
|
export interface ZLinkTimerClock {
|
|
34
35
|
now(): number;
|
|
@@ -54,7 +55,7 @@ interface ZLinkTimerFinalization {
|
|
|
54
55
|
|
|
55
56
|
interface ZLinkTimerFinalizationStart {
|
|
56
57
|
readonly timeout: unknown;
|
|
57
|
-
readonly running: Promise<void
|
|
58
|
+
readonly running: Promise<void> | undefined;
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
type ZLinkTimerFailureReporter = (
|
|
@@ -78,6 +79,13 @@ export interface ZLinkTimerRelocationState {
|
|
|
78
79
|
readonly pendingTicks: readonly ZLinkTimerRelocationPendingTick[];
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
export interface ZLinkManagedTimerStart {
|
|
83
|
+
/** Created paused; the owner starts it with `resume()`. */
|
|
84
|
+
readonly paused?: boolean;
|
|
85
|
+
/** Relocated logical schedule the timer continues from. */
|
|
86
|
+
readonly cursor?: ZLinkTimerRelocationState;
|
|
87
|
+
}
|
|
88
|
+
|
|
81
89
|
export interface ZLinkTimerRelocationPendingTick {
|
|
82
90
|
readonly deliveryIndex: bigint;
|
|
83
91
|
readonly scheduledIndex: bigint;
|
|
@@ -85,18 +93,56 @@ export interface ZLinkTimerRelocationPendingTick {
|
|
|
85
93
|
readonly skippedTicks: bigint;
|
|
86
94
|
}
|
|
87
95
|
|
|
96
|
+
interface ZLinkTimerBinding {
|
|
97
|
+
readonly handler: ZLinkSpotTimerHandler<ZLinkTimerOwnerSpot>;
|
|
98
|
+
readonly spot: ZLinkTimerOwnerSpot;
|
|
99
|
+
readonly executionSerial?: ZLinkSpotSerialTurnExecutor;
|
|
100
|
+
readonly reportFailure?: ZLinkTimerFailureReporter;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface ZLinkTimerBindingSlot {
|
|
104
|
+
binding: ZLinkTimerBinding | undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface ZLinkTimerEntry {
|
|
108
|
+
readonly generation: bigint;
|
|
109
|
+
readonly handlerType: string;
|
|
110
|
+
readonly timer: ZLinkManagedTimer;
|
|
111
|
+
readonly handle: ZLinkRegisteredTimer;
|
|
112
|
+
/** Restored from a relocation payload; Configure and OnInitialize reattach it. */
|
|
113
|
+
readonly restored: boolean;
|
|
114
|
+
readonly slot: ZLinkTimerBindingSlot;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
type ZLinkTimerAddPreparation =
|
|
118
|
+
| { readonly kind: 'reattached'; readonly handle: ZLinkTimer }
|
|
119
|
+
| {
|
|
120
|
+
readonly kind: 'registered';
|
|
121
|
+
readonly previous: ZLinkTimerEntry | undefined;
|
|
122
|
+
readonly generation: bigint;
|
|
123
|
+
};
|
|
124
|
+
|
|
88
125
|
export class ZLinkSpotTimerRegistry {
|
|
89
126
|
private readonly lane = new ZLinkStateLane();
|
|
90
|
-
private readonly timers = new Map<
|
|
91
|
-
string,
|
|
92
|
-
{
|
|
93
|
-
readonly generation: bigint;
|
|
94
|
-
readonly handlerType: string;
|
|
95
|
-
readonly timer: ZLinkManagedTimer;
|
|
96
|
-
}
|
|
97
|
-
>();
|
|
127
|
+
private readonly timers = new Map<string, ZLinkTimerEntry>();
|
|
98
128
|
private readonly generations = new Map<string, bigint>();
|
|
99
129
|
private executionBarrier: ZLinkExecutionBarrier | undefined;
|
|
130
|
+
/** Frozen from the source capture or the target restore until abort or Ready. */
|
|
131
|
+
private frozen = false;
|
|
132
|
+
/**
|
|
133
|
+
* The lifecycle owner opens this window around the target's Configure and
|
|
134
|
+
* pre-Ready OnInitialize only (Spot timer §1). A registration captures it at
|
|
135
|
+
* the call, so a registration started inside the window reattaches a
|
|
136
|
+
* restored key even when its asynchronous part finishes after the window.
|
|
137
|
+
*/
|
|
138
|
+
private initializing = false;
|
|
139
|
+
/**
|
|
140
|
+
* Handler constructors the target Spot announced with `addTimer(...)` during
|
|
141
|
+
* Configure. Together with `handlers.addTimerHandler(...)` they form the
|
|
142
|
+
* table that resolves a restored timer's handler by name.
|
|
143
|
+
*/
|
|
144
|
+
private readonly configureTimerHandlers = new Set<Type>();
|
|
145
|
+
private relocationHandlersResolved = false;
|
|
100
146
|
|
|
101
147
|
constructor(
|
|
102
148
|
_metrics?: import('../diagnostics').ZLinkRuntimeMetrics,
|
|
@@ -137,61 +183,148 @@ export class ZLinkSpotTimerRegistry {
|
|
|
137
183
|
reportFailure?: ZLinkTimerFailureReporter
|
|
138
184
|
): Promise<ZLinkTimer> {
|
|
139
185
|
validateTimerRegistration(name, periodMs, options);
|
|
186
|
+
const initializing = this.initializing;
|
|
187
|
+
if (initializing && !this.relocationHandlersResolved) {
|
|
188
|
+
this.configureTimerHandlers.add(handlerType);
|
|
189
|
+
}
|
|
140
190
|
throwIfAborted(signal);
|
|
141
191
|
prepareHandlerInstance(providerResolver, handlerType);
|
|
142
192
|
const handler = await resolveLifecycleHandler(spot, handlerType, providerResolver);
|
|
143
|
-
const prepared = await this.lane.run(() => this.prepareAddCore(name));
|
|
144
|
-
if (prepared.previous !== undefined) await prepared.previous.timer.cancel(signal);
|
|
145
193
|
const executionSerial =
|
|
146
194
|
this.executeTimer === undefined
|
|
147
195
|
? (this.executionSerialForTimer?.(name, serial) ?? serial)
|
|
148
196
|
: undefined;
|
|
149
197
|
if (this.executionBarrier !== undefined)
|
|
150
198
|
executionSerial?.setExecutionBarrier(this.executionBarrier);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
callbackStarted = false;
|
|
160
|
-
const timerFlow = createInboundFlow(
|
|
161
|
-
undefined,
|
|
162
|
-
'Timer',
|
|
163
|
-
this.flowCreationEnabled(),
|
|
164
|
-
undefined
|
|
165
|
-
);
|
|
166
|
-
const operation = () => {
|
|
167
|
-
const current = this.timers.get(name);
|
|
168
|
-
if (
|
|
169
|
-
current === undefined ||
|
|
170
|
-
current.generation !== prepared.generation ||
|
|
171
|
-
current.timer !== timer
|
|
172
|
-
) {
|
|
173
|
-
return undefined;
|
|
174
|
-
}
|
|
175
|
-
if (!this.executionAllowed()) return undefined;
|
|
176
|
-
callbackStarted = true;
|
|
177
|
-
return runWithFlow(timerFlow, () => handler.handle(spot, tick));
|
|
178
|
-
};
|
|
179
|
-
if (this.executeTimer !== undefined) {
|
|
180
|
-
await this.executeTimer(name, operation);
|
|
181
|
-
} else {
|
|
182
|
-
await executionSerial!.execute(operation);
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
reportFailure,
|
|
186
|
-
() => callbackStarted,
|
|
187
|
-
this.clock
|
|
188
|
-
)
|
|
199
|
+
const binding: ZLinkTimerBinding = {
|
|
200
|
+
handler: handler as ZLinkSpotTimerHandler<ZLinkTimerOwnerSpot>,
|
|
201
|
+
spot,
|
|
202
|
+
executionSerial,
|
|
203
|
+
reportFailure
|
|
204
|
+
};
|
|
205
|
+
const prepared = await this.lane.run(() =>
|
|
206
|
+
this.prepareAddCore(name, handlerType.name, initializing, binding)
|
|
189
207
|
);
|
|
208
|
+
if (prepared.kind === 'reattached') return prepared.handle;
|
|
209
|
+
if (prepared.previous !== undefined) await prepared.previous.timer.cancel(signal);
|
|
210
|
+
const slot: ZLinkTimerBindingSlot = { binding };
|
|
211
|
+
const timer = this.createTimer(name, prepared.generation, periodMs, options, slot);
|
|
212
|
+
const handle = new ZLinkRegisteredTimer(this, name, prepared.generation, timer);
|
|
190
213
|
const registered = await this.lane.run(() =>
|
|
191
|
-
this.completeAddCore(name,
|
|
214
|
+
this.completeAddCore(name, {
|
|
215
|
+
generation: prepared.generation,
|
|
216
|
+
handlerType: handlerType.name,
|
|
217
|
+
timer,
|
|
218
|
+
handle,
|
|
219
|
+
restored: false,
|
|
220
|
+
slot
|
|
221
|
+
})
|
|
192
222
|
);
|
|
193
223
|
if (!registered) await timer.cancel(signal);
|
|
194
|
-
return
|
|
224
|
+
return handle;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Stages the source payload on a fresh relocation target before Configure.
|
|
229
|
+
* The restored registrations keep their schedule and stay frozen until Ready.
|
|
230
|
+
*/
|
|
231
|
+
stageRelocation(states: readonly ZLinkTimerRelocationState[]): void {
|
|
232
|
+
if (this.frozen || this.timers.size !== 0) {
|
|
233
|
+
throw new Error('Timer relocation can only be restored into a fresh registry.');
|
|
234
|
+
}
|
|
235
|
+
const restored = states.map((state): [string, ZLinkTimerEntry] => {
|
|
236
|
+
if (states.some((other) => other !== state && other.name === state.name)) {
|
|
237
|
+
throw new Error(`Timer '${state.name}' appears twice in relocation state.`);
|
|
238
|
+
}
|
|
239
|
+
const options = {
|
|
240
|
+
overrunPolicy: state.overrunPolicy,
|
|
241
|
+
maxCatchUpTicks: state.maxCatchUpTicks,
|
|
242
|
+
stopOnUnhandledException: state.stopOnUnhandledException
|
|
243
|
+
};
|
|
244
|
+
validateTimerRegistration(state.name, state.periodMs, options);
|
|
245
|
+
const slot: ZLinkTimerBindingSlot = { binding: undefined };
|
|
246
|
+
const timer = this.createTimer(state.name, 1n, state.periodMs, options, slot, state);
|
|
247
|
+
return [
|
|
248
|
+
state.name,
|
|
249
|
+
{
|
|
250
|
+
generation: 1n,
|
|
251
|
+
handlerType: state.handlerType,
|
|
252
|
+
timer,
|
|
253
|
+
handle: new ZLinkRegisteredTimer(this, state.name, 1n, timer),
|
|
254
|
+
restored: true,
|
|
255
|
+
slot
|
|
256
|
+
}
|
|
257
|
+
];
|
|
258
|
+
});
|
|
259
|
+
this.frozen = true;
|
|
260
|
+
for (const [name, entry] of restored) {
|
|
261
|
+
this.generations.set(name, entry.generation);
|
|
262
|
+
this.timers.set(name, entry);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** Runs one relocation-target initialization callback inside the reattach window. */
|
|
267
|
+
async runRelocationInitialization<T>(operation: () => Promise<T> | T): Promise<T> {
|
|
268
|
+
if (!this.frozen || this.initializing) {
|
|
269
|
+
throw new Error('Timer relocation initialization is not active.');
|
|
270
|
+
}
|
|
271
|
+
this.initializing = true;
|
|
272
|
+
try {
|
|
273
|
+
return await operation();
|
|
274
|
+
} finally {
|
|
275
|
+
this.initializing = false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* After Configure, resolves every restored timer's handler constructor by
|
|
281
|
+
* name among the constructors the Spot announced in Configure: `addTimer(...)`
|
|
282
|
+
* there or `handlers.addTimerHandler(...)`. A restored key that Configure did
|
|
283
|
+
* not reattach is registered with the resolved constructor so it survives
|
|
284
|
+
* Ready without re-registration. No match or more than one match fails the
|
|
285
|
+
* target restore.
|
|
286
|
+
*/
|
|
287
|
+
async resolveRelocationHandlers(
|
|
288
|
+
declared: readonly Type[],
|
|
289
|
+
register: (name: string, periodMs: number, handlerType: Type) => Promise<unknown>
|
|
290
|
+
): Promise<void> {
|
|
291
|
+
if (!this.frozen || !this.initializing || this.relocationHandlersResolved) {
|
|
292
|
+
throw new Error('Timer relocation handler resolution is not active.');
|
|
293
|
+
}
|
|
294
|
+
this.relocationHandlersResolved = true;
|
|
295
|
+
const constructors = [...new Set([...declared, ...this.configureTimerHandlers])];
|
|
296
|
+
const restored = await this.lane.run(() =>
|
|
297
|
+
[...this.timers.entries()].filter(([, entry]) => entry.restored)
|
|
298
|
+
);
|
|
299
|
+
for (const [name, entry] of restored) {
|
|
300
|
+
const matches = constructors.filter((type) => type.name === entry.handlerType);
|
|
301
|
+
if (matches.length !== 1) {
|
|
302
|
+
throw createInternalFrameworkException(
|
|
303
|
+
ZLinkFrameworkInternalErrorKind.RelocationFailed,
|
|
304
|
+
matches.length === 0
|
|
305
|
+
? `Timer '${name}' handler '${entry.handlerType}' is not declared. Register its timer with context.addTimer(...) in configure(), or declare it with context.handlers.addTimerHandler(Handler) in configure().`
|
|
306
|
+
: `Timer '${name}' handler '${entry.handlerType}' is ambiguous: ${matches.length} declared constructors share the name.`
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
if (entry.slot.binding === undefined) {
|
|
310
|
+
await register(name, entry.timer.periodMs, matches[0]!);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** Ready: starts the restored and newly registered timers. */
|
|
316
|
+
async publishRelocation(): Promise<void> {
|
|
317
|
+
await this.lane.run(() => {
|
|
318
|
+
for (const [name, entry] of this.timers) {
|
|
319
|
+
if (entry.slot.binding === undefined) {
|
|
320
|
+
throw new Error(`Restored timer '${name}' has no resolved handler at Ready.`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
this.frozen = false;
|
|
324
|
+
for (const entry of this.timers.values()) {
|
|
325
|
+
startOutsideStateLane(() => entry.timer.resume());
|
|
326
|
+
}
|
|
327
|
+
});
|
|
195
328
|
}
|
|
196
329
|
|
|
197
330
|
async dispose(): Promise<void> {
|
|
@@ -209,9 +342,10 @@ export class ZLinkSpotTimerRegistry {
|
|
|
209
342
|
}
|
|
210
343
|
|
|
211
344
|
async captureRelocation(): Promise<readonly ZLinkTimerRelocationState[]> {
|
|
212
|
-
const timers = await this.lane.run(() =>
|
|
213
|
-
|
|
214
|
-
|
|
345
|
+
const timers = await this.lane.run(() => {
|
|
346
|
+
this.frozen = true;
|
|
347
|
+
return [...this.timers.entries()].sort(([left], [right]) => left.localeCompare(right));
|
|
348
|
+
});
|
|
215
349
|
const states: ZLinkTimerRelocationState[] = [];
|
|
216
350
|
for (const [name, entry] of timers) {
|
|
217
351
|
const state = await entry.timer.captureRelocation(entry.handlerType);
|
|
@@ -221,24 +355,14 @@ export class ZLinkSpotTimerRegistry {
|
|
|
221
355
|
return states;
|
|
222
356
|
}
|
|
223
357
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
for (const [name, entry] of this.timers) {
|
|
230
|
-
const state = byName.get(name);
|
|
231
|
-
if (state === undefined) {
|
|
232
|
-
throw new Error(`Timer '${name}' is missing from relocation state.`);
|
|
233
|
-
}
|
|
234
|
-
entry.timer.restoreRelocation(state, entry.handlerType);
|
|
358
|
+
/** Source abort: the captured timers continue from their own unchanged cursor. */
|
|
359
|
+
abortRelocation(): void {
|
|
360
|
+
this.frozen = false;
|
|
361
|
+
for (const entry of this.timers.values()) {
|
|
362
|
+
startOutsideStateLane(() => entry.timer.resume());
|
|
235
363
|
}
|
|
236
364
|
}
|
|
237
365
|
|
|
238
|
-
abortRelocation(states: readonly ZLinkTimerRelocationState[]): void {
|
|
239
|
-
this.restoreRelocation(states);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
366
|
async commitRelocation(): Promise<void> {
|
|
243
367
|
await this.dispose();
|
|
244
368
|
}
|
|
@@ -254,31 +378,92 @@ export class ZLinkSpotTimerRegistry {
|
|
|
254
378
|
await timer.cancel(signal);
|
|
255
379
|
}
|
|
256
380
|
|
|
257
|
-
private
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
381
|
+
private createTimer(
|
|
382
|
+
name: string,
|
|
383
|
+
generation: bigint,
|
|
384
|
+
periodMs: number,
|
|
385
|
+
options: ZLinkTimerOptions | undefined,
|
|
386
|
+
slot: ZLinkTimerBindingSlot,
|
|
387
|
+
cursor?: ZLinkTimerRelocationState
|
|
388
|
+
): ZLinkManagedTimer {
|
|
389
|
+
let callbackStarted = false;
|
|
390
|
+
const timer: ZLinkManagedTimer = startOutsideStateLane(
|
|
391
|
+
() =>
|
|
392
|
+
new ZLinkManagedTimer(
|
|
393
|
+
name,
|
|
394
|
+
periodMs,
|
|
395
|
+
normalizeTimerOptions(options),
|
|
396
|
+
async (tick) => {
|
|
397
|
+
callbackStarted = false;
|
|
398
|
+
const binding = slot.binding;
|
|
399
|
+
if (binding === undefined) return;
|
|
400
|
+
const timerFlow = createInboundFlow(
|
|
401
|
+
undefined,
|
|
402
|
+
'Timer',
|
|
403
|
+
this.flowCreationEnabled(),
|
|
404
|
+
undefined
|
|
405
|
+
);
|
|
406
|
+
const operation = () => {
|
|
407
|
+
const current = this.timers.get(name);
|
|
408
|
+
if (
|
|
409
|
+
current === undefined ||
|
|
410
|
+
current.generation !== generation ||
|
|
411
|
+
current.timer !== timer
|
|
412
|
+
) {
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
if (!this.executionAllowed()) return undefined;
|
|
416
|
+
callbackStarted = true;
|
|
417
|
+
return runWithFlow(timerFlow, () => binding.handler.handle(binding.spot, tick));
|
|
418
|
+
};
|
|
419
|
+
if (this.executeTimer !== undefined) {
|
|
420
|
+
await this.executeTimer(name, operation);
|
|
421
|
+
} else {
|
|
422
|
+
await binding.executionSerial!.execute(operation);
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
(tick, cause, event) => slot.binding?.reportFailure?.(tick, cause, event),
|
|
426
|
+
() => callbackStarted,
|
|
427
|
+
this.clock,
|
|
428
|
+
{ paused: true, cursor }
|
|
429
|
+
)
|
|
430
|
+
);
|
|
431
|
+
return timer;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
private prepareAddCore(
|
|
435
|
+
name: string,
|
|
436
|
+
handlerType: string,
|
|
437
|
+
initializing: boolean,
|
|
438
|
+
binding: ZLinkTimerBinding
|
|
439
|
+
): ZLinkTimerAddPreparation {
|
|
440
|
+
if (this.frozen && !initializing) {
|
|
441
|
+
throw createInternalFrameworkException(
|
|
442
|
+
ZLinkFrameworkInternalErrorKind.InvalidOperation,
|
|
443
|
+
`Timer '${name}' registration is sealed for relocation.`
|
|
444
|
+
);
|
|
445
|
+
}
|
|
267
446
|
const previous = this.timers.get(name);
|
|
447
|
+
if (initializing && previous?.restored === true) {
|
|
448
|
+
if (previous.handlerType !== handlerType) {
|
|
449
|
+
throw createInternalFrameworkException(
|
|
450
|
+
ZLinkFrameworkInternalErrorKind.InvalidOperation,
|
|
451
|
+
`Timer '${name}' was restored with handler '${previous.handlerType}', not '${handlerType}'.`
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
previous.slot.binding = binding;
|
|
455
|
+
return { kind: 'reattached', handle: previous.handle };
|
|
456
|
+
}
|
|
268
457
|
this.timers.delete(name);
|
|
269
458
|
const generation = (this.generations.get(name) ?? 0n) + 1n;
|
|
270
459
|
this.generations.set(name, generation);
|
|
271
|
-
return { previous, generation };
|
|
460
|
+
return { kind: 'registered', previous, generation };
|
|
272
461
|
}
|
|
273
462
|
|
|
274
|
-
private completeAddCore(
|
|
275
|
-
name
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
timer: ZLinkManagedTimer
|
|
279
|
-
): boolean {
|
|
280
|
-
if (this.generations.get(name) !== generation) return false;
|
|
281
|
-
this.timers.set(name, { generation, handlerType, timer });
|
|
463
|
+
private completeAddCore(name: string, entry: ZLinkTimerEntry): boolean {
|
|
464
|
+
if (this.generations.get(name) !== entry.generation) return false;
|
|
465
|
+
this.timers.set(name, entry);
|
|
466
|
+
if (!this.frozen) startOutsideStateLane(() => entry.timer.resume());
|
|
282
467
|
return true;
|
|
283
468
|
}
|
|
284
469
|
|
|
@@ -326,24 +511,44 @@ export class ZLinkManagedTimer implements ZLinkTimer {
|
|
|
326
511
|
private pausedForRelocation = false;
|
|
327
512
|
private startedAtMs: number;
|
|
328
513
|
private startedAtUnixMs: number;
|
|
514
|
+
/** DeliveryIndex and ScheduledIndex of the last completed tick. */
|
|
329
515
|
private deliveryIndex = 0n;
|
|
330
516
|
private lastScheduledIndex = 0n;
|
|
517
|
+
/**
|
|
518
|
+
* The selected tick whose callback has not completed (Spot timer §1). It is
|
|
519
|
+
* the only pending tick a relocation captures, and a restored timer delivers
|
|
520
|
+
* it before it selects another tick.
|
|
521
|
+
*/
|
|
522
|
+
private pendingScheduledIndex: bigint | undefined;
|
|
331
523
|
private timeout: unknown;
|
|
332
|
-
|
|
524
|
+
/** The dispatch in flight, from its timer callback until `completeFireCore`. */
|
|
525
|
+
private running: Promise<void> | undefined;
|
|
333
526
|
private dispatchFailure: { readonly cause: unknown } | undefined;
|
|
334
527
|
private finalization: ZLinkTimerFinalization | undefined;
|
|
335
528
|
|
|
336
529
|
constructor(
|
|
337
530
|
private readonly name: string,
|
|
338
|
-
|
|
531
|
+
readonly periodMs: number,
|
|
339
532
|
private readonly options: Required<ZLinkTimerOptions>,
|
|
340
533
|
private readonly onTick: (tick: ZLinkTimerTick) => Promise<void>,
|
|
341
534
|
private readonly onFailure?: ZLinkTimerFailureReporter,
|
|
342
535
|
private readonly shouldWaitForRunningOnCancel: () => boolean = () => true,
|
|
343
|
-
private readonly clock: ZLinkTimerClock = systemTimerClock
|
|
536
|
+
private readonly clock: ZLinkTimerClock = systemTimerClock,
|
|
537
|
+
start: ZLinkManagedTimerStart = {}
|
|
344
538
|
) {
|
|
345
|
-
|
|
346
|
-
|
|
539
|
+
const cursor = start.cursor;
|
|
540
|
+
if (cursor === undefined) {
|
|
541
|
+
this.startedAtMs = clock.now();
|
|
542
|
+
this.startedAtUnixMs = clock.utcNow();
|
|
543
|
+
} else {
|
|
544
|
+
validateRelocationCursor(name, periodMs, cursor);
|
|
545
|
+
this.startedAtUnixMs = cursor.startedAtUnixMs;
|
|
546
|
+
this.startedAtMs = clock.now() - Math.max(0, clock.utcNow() - cursor.startedAtUnixMs);
|
|
547
|
+
this.deliveryIndex = cursor.deliveryIndex;
|
|
548
|
+
this.lastScheduledIndex = cursor.lastScheduledIndex;
|
|
549
|
+
this.pendingScheduledIndex = cursor.pendingTicks[0]?.scheduledIndex;
|
|
550
|
+
}
|
|
551
|
+
this.pausedForRelocation = start.paused === true;
|
|
347
552
|
this.scheduleNext();
|
|
348
553
|
}
|
|
349
554
|
|
|
@@ -392,59 +597,44 @@ export class ZLinkManagedTimer implements ZLinkTimer {
|
|
|
392
597
|
deliveryIndex: this.deliveryIndex,
|
|
393
598
|
lastScheduledIndex: this.lastScheduledIndex,
|
|
394
599
|
nextDueAtUnixMs: this.startedAtUnixMs + Number(this.lastScheduledIndex + 1n) * this.periodMs,
|
|
395
|
-
pendingTicks:
|
|
600
|
+
pendingTicks:
|
|
601
|
+
this.pendingScheduledIndex === undefined
|
|
602
|
+
? []
|
|
603
|
+
: [this.pendingTick(this.pendingScheduledIndex)]
|
|
396
604
|
}));
|
|
397
605
|
}
|
|
398
606
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if (
|
|
402
|
-
state.name !== this.name ||
|
|
403
|
-
state.handlerType !== handlerType ||
|
|
404
|
-
state.periodMs !== this.periodMs ||
|
|
405
|
-
state.overrunPolicy !== this.options.overrunPolicy ||
|
|
406
|
-
state.maxCatchUpTicks !== this.options.maxCatchUpTicks ||
|
|
407
|
-
state.stopOnUnhandledException !== this.options.stopOnUnhandledException ||
|
|
408
|
-
!Number.isSafeInteger(state.startedAtUnixMs) ||
|
|
409
|
-
state.deliveryIndex < 0n ||
|
|
410
|
-
state.lastScheduledIndex < 0n ||
|
|
411
|
-
!Number.isSafeInteger(state.nextDueAtUnixMs) ||
|
|
412
|
-
state.nextDueAtUnixMs !==
|
|
413
|
-
state.startedAtUnixMs + Number(state.lastScheduledIndex + 1n) * state.periodMs ||
|
|
414
|
-
state.pendingTicks.length !== 0
|
|
415
|
-
) {
|
|
416
|
-
throw new Error(`Timer '${this.name}' relocation contract does not match its registration.`);
|
|
417
|
-
}
|
|
418
|
-
if (this.timeout !== undefined) this.clock.clearTimeout(this.timeout);
|
|
419
|
-
this.timeout = undefined;
|
|
420
|
-
this.startedAtUnixMs = state.startedAtUnixMs;
|
|
421
|
-
this.startedAtMs = this.clock.now() - Math.max(0, this.clock.utcNow() - state.startedAtUnixMs);
|
|
422
|
-
this.deliveryIndex = state.deliveryIndex;
|
|
423
|
-
this.lastScheduledIndex = state.lastScheduledIndex;
|
|
607
|
+
/** Starts a timer created paused, or continues one paused by a relocation capture. */
|
|
608
|
+
resume(): void {
|
|
609
|
+
if (this.disposed || !this.pausedForRelocation) return;
|
|
424
610
|
this.pausedForRelocation = false;
|
|
425
611
|
this.scheduleNext();
|
|
426
612
|
}
|
|
427
613
|
|
|
428
614
|
private scheduleNext(): void {
|
|
429
|
-
|
|
615
|
+
// The dispatch in flight schedules the next tick when it completes.
|
|
616
|
+
if (this.disposed || this.pausedForRelocation || this.running !== undefined) {
|
|
430
617
|
return;
|
|
431
618
|
}
|
|
432
619
|
|
|
433
620
|
const delayMs =
|
|
434
|
-
this.
|
|
435
|
-
?
|
|
436
|
-
:
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
this.
|
|
441
|
-
|
|
442
|
-
(
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
621
|
+
this.pendingScheduledIndex !== undefined
|
|
622
|
+
? 0
|
|
623
|
+
: this.options.overrunPolicy === ZLinkTimerOverrunPolicy.DelayNextTick
|
|
624
|
+
? this.periodMs
|
|
625
|
+
: Math.max(0, Number(this.lastScheduledIndex + 1n) * this.periodMs - this.elapsedMs());
|
|
626
|
+
this.timeout = startOutsideStateLane(() =>
|
|
627
|
+
this.clock.setTimeout(() => {
|
|
628
|
+
this.timeout = undefined;
|
|
629
|
+
this.running = this.fire().then(
|
|
630
|
+
() => undefined,
|
|
631
|
+
(cause) =>
|
|
632
|
+
this.lane.run(() => {
|
|
633
|
+
this.dispatchFailure ??= { cause };
|
|
634
|
+
})
|
|
635
|
+
);
|
|
636
|
+
}, delayMs)
|
|
637
|
+
);
|
|
448
638
|
}
|
|
449
639
|
|
|
450
640
|
private async fire(): Promise<void> {
|
|
@@ -455,6 +645,11 @@ export class ZLinkManagedTimer implements ZLinkTimer {
|
|
|
455
645
|
try {
|
|
456
646
|
await this.onTick(prepared.tick);
|
|
457
647
|
} catch (cause) {
|
|
648
|
+
// A tick whose callback had not started when a relocation captured it
|
|
649
|
+
// belongs to the capture: the target delivers it, so the source neither
|
|
650
|
+
// reports nor completes it.
|
|
651
|
+
const started = this.shouldWaitForRunningOnCancel();
|
|
652
|
+
if (!started && (await this.lane.run(() => this.pausedForRelocation))) return;
|
|
458
653
|
shouldContinue = !this.options.stopOnUnhandledException;
|
|
459
654
|
await this.onFailure?.(
|
|
460
655
|
prepared.tick,
|
|
@@ -465,7 +660,7 @@ export class ZLinkManagedTimer implements ZLinkTimer {
|
|
|
465
660
|
);
|
|
466
661
|
}
|
|
467
662
|
|
|
468
|
-
await this.lane.run(() => this.completeFireCore(
|
|
663
|
+
await this.lane.run(() => this.completeFireCore(shouldContinue));
|
|
469
664
|
}
|
|
470
665
|
|
|
471
666
|
private cancelCore(): ZLinkTimerFinalizationStart {
|
|
@@ -474,7 +669,7 @@ export class ZLinkManagedTimer implements ZLinkTimer {
|
|
|
474
669
|
this.timeout = undefined;
|
|
475
670
|
return {
|
|
476
671
|
timeout,
|
|
477
|
-
running: this.shouldWaitForRunningOnCancel() ? this.running :
|
|
672
|
+
running: this.shouldWaitForRunningOnCancel() ? this.running : undefined
|
|
478
673
|
};
|
|
479
674
|
}
|
|
480
675
|
|
|
@@ -507,43 +702,54 @@ export class ZLinkManagedTimer implements ZLinkTimer {
|
|
|
507
702
|
}
|
|
508
703
|
}
|
|
509
704
|
|
|
510
|
-
private prepareCaptureCore(): { readonly running: Promise<void> } {
|
|
705
|
+
private prepareCaptureCore(): { readonly running: Promise<void> | undefined } {
|
|
511
706
|
if (this.disposed) throw new Error(`Timer '${this.name}' is disposed.`);
|
|
512
707
|
this.pausedForRelocation = true;
|
|
513
708
|
if (this.timeout !== undefined) {
|
|
514
709
|
this.clock.clearTimeout(this.timeout);
|
|
515
710
|
this.timeout = undefined;
|
|
516
711
|
}
|
|
517
|
-
return { running: this.shouldWaitForRunningOnCancel() ? this.running :
|
|
712
|
+
return { running: this.shouldWaitForRunningOnCancel() ? this.running : undefined };
|
|
518
713
|
}
|
|
519
714
|
|
|
520
|
-
private prepareFireCore():
|
|
521
|
-
{ readonly tick: ZLinkTimerTick; readonly scheduledIndex: bigint } | undefined {
|
|
715
|
+
private prepareFireCore(): { readonly tick: ZLinkTimerTick } | undefined {
|
|
522
716
|
if (this.disposed) return undefined;
|
|
523
|
-
|
|
524
|
-
const
|
|
717
|
+
this.pendingScheduledIndex ??= this.selectScheduledIndex();
|
|
718
|
+
const pending = this.pendingTick(this.pendingScheduledIndex);
|
|
525
719
|
const startedElapsedMs = this.elapsedMs();
|
|
526
|
-
const scheduledElapsedMs = Number(scheduledIndex) * this.periodMs;
|
|
527
|
-
this.deliveryIndex += 1n;
|
|
720
|
+
const scheduledElapsedMs = Number(pending.scheduledIndex) * this.periodMs;
|
|
528
721
|
return {
|
|
529
|
-
scheduledIndex,
|
|
530
722
|
tick: {
|
|
531
723
|
name: this.name,
|
|
532
|
-
deliveryIndex:
|
|
533
|
-
scheduledIndex,
|
|
724
|
+
deliveryIndex: pending.deliveryIndex,
|
|
725
|
+
scheduledIndex: pending.scheduledIndex,
|
|
534
726
|
periodMs: this.periodMs,
|
|
535
|
-
scheduledAt: new Date(
|
|
727
|
+
scheduledAt: new Date(pending.scheduledAtUnixMs),
|
|
536
728
|
startedAt: new Date(this.clock.utcNow()),
|
|
537
729
|
scheduledElapsedMs,
|
|
538
730
|
startedElapsedMs,
|
|
539
731
|
delayMs: startedElapsedMs - scheduledElapsedMs,
|
|
540
|
-
skippedTicks
|
|
732
|
+
skippedTicks: pending.skippedTicks
|
|
541
733
|
}
|
|
542
734
|
};
|
|
543
735
|
}
|
|
544
736
|
|
|
545
|
-
|
|
546
|
-
|
|
737
|
+
/** The tick info of the pending tick at `scheduledIndex` after the completed cursor. */
|
|
738
|
+
private pendingTick(scheduledIndex: bigint): ZLinkTimerRelocationPendingTick {
|
|
739
|
+
return relocationPendingTick(
|
|
740
|
+
this.startedAtUnixMs,
|
|
741
|
+
this.periodMs,
|
|
742
|
+
this.deliveryIndex,
|
|
743
|
+
this.lastScheduledIndex,
|
|
744
|
+
scheduledIndex
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
private completeFireCore(shouldContinue: boolean): void {
|
|
749
|
+
this.deliveryIndex += 1n;
|
|
750
|
+
this.lastScheduledIndex = this.pendingScheduledIndex!;
|
|
751
|
+
this.pendingScheduledIndex = undefined;
|
|
752
|
+
this.running = undefined;
|
|
547
753
|
if (!shouldContinue) {
|
|
548
754
|
this.disposed = true;
|
|
549
755
|
return;
|
|
@@ -580,8 +786,58 @@ export class ZLinkManagedTimer implements ZLinkTimer {
|
|
|
580
786
|
}
|
|
581
787
|
}
|
|
582
788
|
|
|
583
|
-
function
|
|
584
|
-
|
|
789
|
+
function validateRelocationCursor(
|
|
790
|
+
name: string,
|
|
791
|
+
periodMs: number,
|
|
792
|
+
state: ZLinkTimerRelocationState
|
|
793
|
+
): void {
|
|
794
|
+
if (
|
|
795
|
+
state.name !== name ||
|
|
796
|
+
state.periodMs !== periodMs ||
|
|
797
|
+
!Number.isSafeInteger(state.startedAtUnixMs) ||
|
|
798
|
+
state.deliveryIndex < 0n ||
|
|
799
|
+
state.lastScheduledIndex < 0n ||
|
|
800
|
+
!Number.isSafeInteger(state.nextDueAtUnixMs) ||
|
|
801
|
+
state.nextDueAtUnixMs !==
|
|
802
|
+
state.startedAtUnixMs + Number(state.lastScheduledIndex + 1n) * state.periodMs ||
|
|
803
|
+
state.pendingTicks.length > 1 ||
|
|
804
|
+
state.pendingTicks.some((pending) => {
|
|
805
|
+
const expected = relocationPendingTick(
|
|
806
|
+
state.startedAtUnixMs,
|
|
807
|
+
state.periodMs,
|
|
808
|
+
state.deliveryIndex,
|
|
809
|
+
state.lastScheduledIndex,
|
|
810
|
+
pending.scheduledIndex
|
|
811
|
+
);
|
|
812
|
+
return (
|
|
813
|
+
pending.scheduledIndex <= state.lastScheduledIndex ||
|
|
814
|
+
pending.deliveryIndex !== expected.deliveryIndex ||
|
|
815
|
+
pending.scheduledAtUnixMs !== expected.scheduledAtUnixMs ||
|
|
816
|
+
pending.skippedTicks !== expected.skippedTicks
|
|
817
|
+
);
|
|
818
|
+
})
|
|
819
|
+
) {
|
|
820
|
+
throw new Error(`Timer '${name}' relocation state is invalid.`);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Spot timer §1: the tick info that follows the completed cursor. A timer holds
|
|
826
|
+
* at most one pending tick, so its DeliveryIndex is the next one.
|
|
827
|
+
*/
|
|
828
|
+
function relocationPendingTick(
|
|
829
|
+
startedAtUnixMs: number,
|
|
830
|
+
periodMs: number,
|
|
831
|
+
deliveryIndex: bigint,
|
|
832
|
+
lastScheduledIndex: bigint,
|
|
833
|
+
scheduledIndex: bigint
|
|
834
|
+
): ZLinkTimerRelocationPendingTick {
|
|
835
|
+
return {
|
|
836
|
+
deliveryIndex: deliveryIndex + 1n,
|
|
837
|
+
scheduledIndex,
|
|
838
|
+
scheduledAtUnixMs: startedAtUnixMs + Number(scheduledIndex) * periodMs,
|
|
839
|
+
skippedTicks: scheduledIndex - lastScheduledIndex - 1n
|
|
840
|
+
};
|
|
585
841
|
}
|
|
586
842
|
|
|
587
843
|
export function createTimerDiagnostics(
|