supersonic-scsynth 0.4.0 → 0.6.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/supersonic.js +903 -483
- package/dist/wasm/manifest.json +3 -3
- package/dist/wasm/scsynth-nrt.wasm +0 -0
- package/dist/workers/debug_worker.js +20 -26
- package/dist/workers/osc_in_worker.js +27 -31
- package/dist/workers/osc_out_prescheduler_worker.js +86 -91
- package/dist/workers/scsynth_audio_worklet.js +46 -55
- package/package.json +1 -1
package/dist/wasm/manifest.json
CHANGED
|
Binary file
|
|
@@ -26,6 +26,10 @@ var bufferConstants = null;
|
|
|
26
26
|
// Control indices (calculated after init)
|
|
27
27
|
var CONTROL_INDICES = {};
|
|
28
28
|
|
|
29
|
+
// Metrics view (for writing stats to SAB)
|
|
30
|
+
var metricsView = null;
|
|
31
|
+
var METRICS_INDICES = {};
|
|
32
|
+
|
|
29
33
|
// Worker state
|
|
30
34
|
var running = false;
|
|
31
35
|
|
|
@@ -36,14 +40,6 @@ function debugWorkerLog() {
|
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
42
|
|
|
39
|
-
// Statistics
|
|
40
|
-
var stats = {
|
|
41
|
-
messagesReceived: 0,
|
|
42
|
-
wakeups: 0,
|
|
43
|
-
timeouts: 0,
|
|
44
|
-
bytesRead: 0
|
|
45
|
-
};
|
|
46
|
-
|
|
47
43
|
/**
|
|
48
44
|
* Initialize ring buffer access
|
|
49
45
|
*/
|
|
@@ -60,6 +56,17 @@ function initRingBuffer(buffer, base, constants) {
|
|
|
60
56
|
DEBUG_HEAD: (ringBufferBase + bufferConstants.CONTROL_START + 16) / 4,
|
|
61
57
|
DEBUG_TAIL: (ringBufferBase + bufferConstants.CONTROL_START + 20) / 4
|
|
62
58
|
};
|
|
59
|
+
|
|
60
|
+
// Initialize metrics view (Debug metrics are at offsets 23-26 in the metrics array)
|
|
61
|
+
var metricsBase = ringBufferBase + bufferConstants.METRICS_START;
|
|
62
|
+
metricsView = new Uint32Array(sharedBuffer, metricsBase, bufferConstants.METRICS_SIZE / 4);
|
|
63
|
+
|
|
64
|
+
METRICS_INDICES = {
|
|
65
|
+
MESSAGES_RECEIVED: 23,
|
|
66
|
+
WAKEUPS: 24,
|
|
67
|
+
TIMEOUTS: 25,
|
|
68
|
+
BYTES_READ: 26
|
|
69
|
+
};
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
/**
|
|
@@ -137,13 +144,13 @@ function readDebugMessages() {
|
|
|
137
144
|
// Move to next message
|
|
138
145
|
currentTail = (currentTail + length) % bufferConstants.DEBUG_BUFFER_SIZE;
|
|
139
146
|
messagesRead++;
|
|
140
|
-
|
|
147
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.MESSAGES_RECEIVED, 1);
|
|
141
148
|
}
|
|
142
149
|
|
|
143
150
|
// Update tail pointer (consume messages)
|
|
144
151
|
if (messagesRead > 0) {
|
|
145
152
|
Atomics.store(atomicView, CONTROL_INDICES.DEBUG_TAIL, currentTail);
|
|
146
|
-
|
|
153
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.BYTES_READ, messagesRead);
|
|
147
154
|
}
|
|
148
155
|
|
|
149
156
|
return messages.length > 0 ? messages : null;
|
|
@@ -166,9 +173,9 @@ function waitLoop() {
|
|
|
166
173
|
|
|
167
174
|
if (result === 'ok' || result === 'not-equal') {
|
|
168
175
|
// We were notified or value changed!
|
|
169
|
-
|
|
176
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.WAKEUPS, 1);
|
|
170
177
|
} else if (result === 'timed-out') {
|
|
171
|
-
|
|
178
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.TIMEOUTS, 1);
|
|
172
179
|
continue; // Check running flag
|
|
173
180
|
}
|
|
174
181
|
}
|
|
@@ -180,13 +187,7 @@ function waitLoop() {
|
|
|
180
187
|
// Send to main thread
|
|
181
188
|
self.postMessage({
|
|
182
189
|
type: 'debug',
|
|
183
|
-
messages: messages
|
|
184
|
-
stats: {
|
|
185
|
-
wakeups: stats.wakeups,
|
|
186
|
-
timeouts: stats.timeouts,
|
|
187
|
-
messagesReceived: stats.messagesReceived,
|
|
188
|
-
bytesRead: stats.bytesRead
|
|
189
|
-
}
|
|
190
|
+
messages: messages
|
|
190
191
|
});
|
|
191
192
|
}
|
|
192
193
|
|
|
@@ -265,13 +266,6 @@ self.addEventListener('message', function(event) {
|
|
|
265
266
|
clear();
|
|
266
267
|
break;
|
|
267
268
|
|
|
268
|
-
case 'getStats':
|
|
269
|
-
self.postMessage({
|
|
270
|
-
type: 'stats',
|
|
271
|
-
stats: stats
|
|
272
|
-
});
|
|
273
|
-
break;
|
|
274
|
-
|
|
275
269
|
default:
|
|
276
270
|
console.warn('[DebugWorker] Unknown message type:', data.type);
|
|
277
271
|
}
|
|
@@ -26,6 +26,10 @@ var bufferConstants = null;
|
|
|
26
26
|
// Control indices (calculated after init)
|
|
27
27
|
var CONTROL_INDICES = {};
|
|
28
28
|
|
|
29
|
+
// Metrics view (for writing stats to SAB)
|
|
30
|
+
var metricsView = null;
|
|
31
|
+
var METRICS_INDICES = {};
|
|
32
|
+
|
|
29
33
|
// Worker state
|
|
30
34
|
var running = false;
|
|
31
35
|
|
|
@@ -36,14 +40,8 @@ function oscInLog() {
|
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
42
|
|
|
39
|
-
//
|
|
40
|
-
var
|
|
41
|
-
messagesReceived: 0,
|
|
42
|
-
lastSequenceReceived: -1,
|
|
43
|
-
droppedMessages: 0,
|
|
44
|
-
wakeups: 0,
|
|
45
|
-
timeouts: 0
|
|
46
|
-
};
|
|
43
|
+
// Sequence tracking for dropped message detection
|
|
44
|
+
var lastSequenceReceived = -1;
|
|
47
45
|
|
|
48
46
|
/**
|
|
49
47
|
* Initialize ring buffer access
|
|
@@ -61,6 +59,17 @@ function initRingBuffer(buffer, base, constants) {
|
|
|
61
59
|
OUT_HEAD: (ringBufferBase + bufferConstants.CONTROL_START + 8) / 4,
|
|
62
60
|
OUT_TAIL: (ringBufferBase + bufferConstants.CONTROL_START + 12) / 4
|
|
63
61
|
};
|
|
62
|
+
|
|
63
|
+
// Initialize metrics view (OSC In metrics are at offsets 19-22 in the metrics array)
|
|
64
|
+
var metricsBase = ringBufferBase + bufferConstants.METRICS_START;
|
|
65
|
+
metricsView = new Uint32Array(sharedBuffer, metricsBase, bufferConstants.METRICS_SIZE / 4);
|
|
66
|
+
|
|
67
|
+
METRICS_INDICES = {
|
|
68
|
+
MESSAGES_RECEIVED: 19,
|
|
69
|
+
DROPPED_MESSAGES: 20,
|
|
70
|
+
WAKEUPS: 21,
|
|
71
|
+
TIMEOUTS: 22
|
|
72
|
+
};
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
/**
|
|
@@ -100,7 +109,7 @@ function readMessages() {
|
|
|
100
109
|
|
|
101
110
|
if (magic !== bufferConstants.MESSAGE_MAGIC) {
|
|
102
111
|
console.error('[OSCInWorker] Corrupted message at position', currentTail);
|
|
103
|
-
|
|
112
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.DROPPED_MESSAGES, 1);
|
|
104
113
|
// Skip this byte and continue
|
|
105
114
|
currentTail = (currentTail + 1) % bufferConstants.OUT_BUFFER_SIZE;
|
|
106
115
|
continue;
|
|
@@ -113,23 +122,23 @@ function readMessages() {
|
|
|
113
122
|
// Validate message length
|
|
114
123
|
if (length < bufferConstants.MESSAGE_HEADER_SIZE || length > bufferConstants.OUT_BUFFER_SIZE) {
|
|
115
124
|
console.error('[OSCInWorker] Invalid message length:', length);
|
|
116
|
-
|
|
125
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.DROPPED_MESSAGES, 1);
|
|
117
126
|
currentTail = (currentTail + 1) % bufferConstants.OUT_BUFFER_SIZE;
|
|
118
127
|
continue;
|
|
119
128
|
}
|
|
120
129
|
|
|
121
130
|
// Check for dropped messages via sequence
|
|
122
|
-
if (
|
|
123
|
-
var expectedSeq = (
|
|
131
|
+
if (lastSequenceReceived >= 0) {
|
|
132
|
+
var expectedSeq = (lastSequenceReceived + 1) & 0xFFFFFFFF;
|
|
124
133
|
if (sequence !== expectedSeq) {
|
|
125
134
|
var dropped = (sequence - expectedSeq + 0x100000000) & 0xFFFFFFFF;
|
|
126
135
|
if (dropped < 1000) { // Sanity check
|
|
127
136
|
console.warn('[OSCInWorker] Detected', dropped, 'dropped messages (expected seq', expectedSeq, 'got', sequence, ')');
|
|
128
|
-
|
|
137
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.DROPPED_MESSAGES, dropped);
|
|
129
138
|
}
|
|
130
139
|
}
|
|
131
140
|
}
|
|
132
|
-
|
|
141
|
+
lastSequenceReceived = sequence;
|
|
133
142
|
|
|
134
143
|
// Read payload (OSC binary data) - now contiguous due to padding
|
|
135
144
|
var payloadLength = length - bufferConstants.MESSAGE_HEADER_SIZE;
|
|
@@ -149,7 +158,7 @@ function readMessages() {
|
|
|
149
158
|
// Move to next message
|
|
150
159
|
currentTail = (currentTail + length) % bufferConstants.OUT_BUFFER_SIZE;
|
|
151
160
|
messagesRead++;
|
|
152
|
-
|
|
161
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.MESSAGES_RECEIVED, 1);
|
|
153
162
|
}
|
|
154
163
|
|
|
155
164
|
// Update tail pointer (consume messages)
|
|
@@ -177,9 +186,9 @@ function waitLoop() {
|
|
|
177
186
|
|
|
178
187
|
if (result === 'ok' || result === 'not-equal') {
|
|
179
188
|
// We were notified or value changed!
|
|
180
|
-
|
|
189
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.WAKEUPS, 1);
|
|
181
190
|
} else if (result === 'timed-out') {
|
|
182
|
-
|
|
191
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.TIMEOUTS, 1);
|
|
183
192
|
continue; // Check running flag
|
|
184
193
|
}
|
|
185
194
|
}
|
|
@@ -191,13 +200,7 @@ function waitLoop() {
|
|
|
191
200
|
// Send to main thread
|
|
192
201
|
self.postMessage({
|
|
193
202
|
type: 'messages',
|
|
194
|
-
messages: messages
|
|
195
|
-
stats: {
|
|
196
|
-
wakeups: stats.wakeups,
|
|
197
|
-
timeouts: stats.timeouts,
|
|
198
|
-
messagesReceived: stats.messagesReceived,
|
|
199
|
-
droppedMessages: stats.droppedMessages
|
|
200
|
-
}
|
|
203
|
+
messages: messages
|
|
201
204
|
});
|
|
202
205
|
}
|
|
203
206
|
|
|
@@ -261,13 +264,6 @@ self.addEventListener('message', function(event) {
|
|
|
261
264
|
stop();
|
|
262
265
|
break;
|
|
263
266
|
|
|
264
|
-
case 'getStats':
|
|
265
|
-
self.postMessage({
|
|
266
|
-
type: 'stats',
|
|
267
|
-
stats: stats
|
|
268
|
-
});
|
|
269
|
-
break;
|
|
270
|
-
|
|
271
267
|
default:
|
|
272
268
|
console.warn('[OSCInWorker] Unknown message type:', data.type);
|
|
273
269
|
}
|
|
@@ -17,6 +17,10 @@ var uint8View = null;
|
|
|
17
17
|
// Ring buffer control indices
|
|
18
18
|
var CONTROL_INDICES = {};
|
|
19
19
|
|
|
20
|
+
// Metrics view (for writing stats to SAB)
|
|
21
|
+
var metricsView = null;
|
|
22
|
+
var METRICS_INDICES = {};
|
|
23
|
+
|
|
20
24
|
// Priority queue implemented as binary min-heap
|
|
21
25
|
// Entries: { ntpTime, seq, editorId, runTag, oscData }
|
|
22
26
|
var eventHeap = [];
|
|
@@ -24,33 +28,14 @@ var periodicTimer = null; // Single periodic timer (25ms interval)
|
|
|
24
28
|
var sequenceCounter = 0;
|
|
25
29
|
var isDispatching = false; // Prevent reentrancy into dispatch loop
|
|
26
30
|
|
|
31
|
+
// Message sequence counter for ring buffer writes (NOT a metric - used for message headers)
|
|
32
|
+
var outgoingMessageSeq = 0;
|
|
33
|
+
|
|
27
34
|
// Retry queue for failed writes
|
|
28
35
|
var retryQueue = [];
|
|
29
36
|
var MAX_RETRY_QUEUE_SIZE = 100;
|
|
30
37
|
var MAX_RETRIES_PER_MESSAGE = 5;
|
|
31
38
|
|
|
32
|
-
// Statistics
|
|
33
|
-
var stats = {
|
|
34
|
-
bundlesScheduled: 0,
|
|
35
|
-
bundlesWritten: 0,
|
|
36
|
-
bundlesDropped: 0,
|
|
37
|
-
bufferOverruns: 0,
|
|
38
|
-
eventsPending: 0,
|
|
39
|
-
maxEventsPending: 0,
|
|
40
|
-
eventsCancelled: 0,
|
|
41
|
-
totalDispatches: 0,
|
|
42
|
-
totalLateDispatchMs: 0,
|
|
43
|
-
maxLateDispatchMs: 0,
|
|
44
|
-
totalSendTasks: 0,
|
|
45
|
-
totalSendProcessMs: 0,
|
|
46
|
-
maxSendProcessMs: 0,
|
|
47
|
-
messagesRetried: 0,
|
|
48
|
-
retriesSucceeded: 0,
|
|
49
|
-
retriesFailed: 0,
|
|
50
|
-
retryQueueSize: 0,
|
|
51
|
-
maxRetryQueueSize: 0
|
|
52
|
-
};
|
|
53
|
-
|
|
54
39
|
// Timing constants
|
|
55
40
|
var NTP_EPOCH_OFFSET = 2208988800; // Seconds from 1900-01-01 to 1970-01-01
|
|
56
41
|
var POLL_INTERVAL_MS = 25; // Check every 25ms
|
|
@@ -127,7 +112,44 @@ function initSharedBuffer() {
|
|
|
127
112
|
IN_TAIL: (ringBufferBase + bufferConstants.CONTROL_START + 4) / 4
|
|
128
113
|
};
|
|
129
114
|
|
|
130
|
-
|
|
115
|
+
// Initialize metrics view (OSC Out metrics are at offsets 7-12 in the metrics array)
|
|
116
|
+
var metricsBase = ringBufferBase + bufferConstants.METRICS_START;
|
|
117
|
+
metricsView = new Uint32Array(sharedBuffer, metricsBase, bufferConstants.METRICS_SIZE / 4);
|
|
118
|
+
|
|
119
|
+
METRICS_INDICES = {
|
|
120
|
+
EVENTS_PENDING: 7,
|
|
121
|
+
MAX_EVENTS_PENDING: 8,
|
|
122
|
+
BUNDLES_WRITTEN: 9,
|
|
123
|
+
BUNDLES_DROPPED: 10,
|
|
124
|
+
RETRIES_SUCCEEDED: 11,
|
|
125
|
+
RETRIES_FAILED: 12,
|
|
126
|
+
BUNDLES_SCHEDULED: 13,
|
|
127
|
+
EVENTS_CANCELLED: 14,
|
|
128
|
+
TOTAL_DISPATCHES: 15,
|
|
129
|
+
MESSAGES_RETRIED: 16,
|
|
130
|
+
RETRY_QUEUE_SIZE: 17,
|
|
131
|
+
RETRY_QUEUE_MAX: 18
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
console.log('[PreScheduler] SharedArrayBuffer initialized with direct ring buffer writing and metrics');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Write metrics to SharedArrayBuffer
|
|
139
|
+
* Increments use Atomics.add() for thread safety, stores use Atomics.store()
|
|
140
|
+
*/
|
|
141
|
+
function updateMetrics() {
|
|
142
|
+
if (!metricsView) return;
|
|
143
|
+
|
|
144
|
+
// Update current values (use Atomics.store for absolute values)
|
|
145
|
+
Atomics.store(metricsView, METRICS_INDICES.EVENTS_PENDING, eventHeap.length);
|
|
146
|
+
|
|
147
|
+
// Update max if current exceeds it
|
|
148
|
+
var currentPending = eventHeap.length;
|
|
149
|
+
var currentMax = Atomics.load(metricsView, METRICS_INDICES.MAX_EVENTS_PENDING);
|
|
150
|
+
if (currentPending > currentMax) {
|
|
151
|
+
Atomics.store(metricsView, METRICS_INDICES.MAX_EVENTS_PENDING, currentPending);
|
|
152
|
+
}
|
|
131
153
|
}
|
|
132
154
|
|
|
133
155
|
/**
|
|
@@ -138,7 +160,7 @@ function initSharedBuffer() {
|
|
|
138
160
|
function writeToRingBuffer(oscMessage, isRetry) {
|
|
139
161
|
if (!sharedBuffer || !atomicView) {
|
|
140
162
|
console.error('[PreScheduler] Not initialized for ring buffer writing');
|
|
141
|
-
|
|
163
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.BUNDLES_DROPPED, 1);
|
|
142
164
|
return false;
|
|
143
165
|
}
|
|
144
166
|
|
|
@@ -148,7 +170,7 @@ function writeToRingBuffer(oscMessage, isRetry) {
|
|
|
148
170
|
// Check if message fits in buffer at all
|
|
149
171
|
if (totalSize > bufferConstants.IN_BUFFER_SIZE - bufferConstants.MESSAGE_HEADER_SIZE) {
|
|
150
172
|
console.error('[PreScheduler] Message too large:', totalSize);
|
|
151
|
-
|
|
173
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.BUNDLES_DROPPED, 1);
|
|
152
174
|
return false;
|
|
153
175
|
}
|
|
154
176
|
|
|
@@ -161,11 +183,10 @@ function writeToRingBuffer(oscMessage, isRetry) {
|
|
|
161
183
|
|
|
162
184
|
if (available < totalSize) {
|
|
163
185
|
// Buffer full - return false so caller can queue for retry
|
|
164
|
-
stats.bufferOverruns++;
|
|
165
186
|
if (!isRetry) {
|
|
166
187
|
// Only increment bundlesDropped on initial attempt
|
|
167
188
|
// Retries increment different counters
|
|
168
|
-
|
|
189
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.BUNDLES_DROPPED, 1);
|
|
169
190
|
console.warn('[PreScheduler] Ring buffer full, message will be queued for retry');
|
|
170
191
|
}
|
|
171
192
|
return false;
|
|
@@ -183,7 +204,7 @@ function writeToRingBuffer(oscMessage, isRetry) {
|
|
|
183
204
|
var headerView = new DataView(headerBytes.buffer);
|
|
184
205
|
headerView.setUint32(0, bufferConstants.MESSAGE_MAGIC, true);
|
|
185
206
|
headerView.setUint32(4, totalSize, true);
|
|
186
|
-
headerView.setUint32(8,
|
|
207
|
+
headerView.setUint32(8, outgoingMessageSeq, true);
|
|
187
208
|
headerView.setUint32(12, 0, true);
|
|
188
209
|
|
|
189
210
|
var writePos1 = ringBufferBase + bufferConstants.IN_BUFFER_START + head;
|
|
@@ -214,7 +235,7 @@ function writeToRingBuffer(oscMessage, isRetry) {
|
|
|
214
235
|
// Write header
|
|
215
236
|
dataView.setUint32(writePos, bufferConstants.MESSAGE_MAGIC, true);
|
|
216
237
|
dataView.setUint32(writePos + 4, totalSize, true);
|
|
217
|
-
dataView.setUint32(writePos + 8,
|
|
238
|
+
dataView.setUint32(writePos + 8, outgoingMessageSeq, true);
|
|
218
239
|
dataView.setUint32(writePos + 12, 0, true);
|
|
219
240
|
|
|
220
241
|
// Write payload
|
|
@@ -222,8 +243,8 @@ function writeToRingBuffer(oscMessage, isRetry) {
|
|
|
222
243
|
}
|
|
223
244
|
|
|
224
245
|
// Diagnostic: Log first few writes
|
|
225
|
-
if (
|
|
226
|
-
schedulerLog('[PreScheduler] Write:', 'seq=' +
|
|
246
|
+
if (outgoingMessageSeq < 5) {
|
|
247
|
+
schedulerLog('[PreScheduler] Write:', 'seq=' + outgoingMessageSeq,
|
|
227
248
|
'pos=' + head, 'size=' + totalSize, 'newHead=' + ((head + totalSize) % bufferConstants.IN_BUFFER_SIZE));
|
|
228
249
|
}
|
|
229
250
|
|
|
@@ -236,7 +257,11 @@ function writeToRingBuffer(oscMessage, isRetry) {
|
|
|
236
257
|
var newHead = (head + totalSize) % bufferConstants.IN_BUFFER_SIZE;
|
|
237
258
|
Atomics.store(atomicView, CONTROL_INDICES.IN_HEAD, newHead);
|
|
238
259
|
|
|
239
|
-
|
|
260
|
+
// Increment sequence counter for next message
|
|
261
|
+
outgoingMessageSeq = (outgoingMessageSeq + 1) & 0xFFFFFFFF;
|
|
262
|
+
|
|
263
|
+
// Update SAB metrics
|
|
264
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.BUNDLES_WRITTEN, 1);
|
|
240
265
|
return true;
|
|
241
266
|
}
|
|
242
267
|
|
|
@@ -246,7 +271,7 @@ function writeToRingBuffer(oscMessage, isRetry) {
|
|
|
246
271
|
function queueForRetry(oscData, context) {
|
|
247
272
|
if (retryQueue.length >= MAX_RETRY_QUEUE_SIZE) {
|
|
248
273
|
console.error('[PreScheduler] Retry queue full, dropping message permanently');
|
|
249
|
-
|
|
274
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.RETRIES_FAILED, 1);
|
|
250
275
|
return;
|
|
251
276
|
}
|
|
252
277
|
|
|
@@ -257,9 +282,13 @@ function queueForRetry(oscData, context) {
|
|
|
257
282
|
queuedAt: performance.now()
|
|
258
283
|
});
|
|
259
284
|
|
|
260
|
-
|
|
261
|
-
if (
|
|
262
|
-
|
|
285
|
+
// Update SAB metrics
|
|
286
|
+
if (metricsView) {
|
|
287
|
+
Atomics.store(metricsView, METRICS_INDICES.RETRY_QUEUE_SIZE, retryQueue.length);
|
|
288
|
+
var currentMax = Atomics.load(metricsView, METRICS_INDICES.RETRY_QUEUE_MAX);
|
|
289
|
+
if (retryQueue.length > currentMax) {
|
|
290
|
+
Atomics.store(metricsView, METRICS_INDICES.RETRY_QUEUE_MAX, retryQueue.length);
|
|
291
|
+
}
|
|
263
292
|
}
|
|
264
293
|
|
|
265
294
|
schedulerLog('[PreScheduler] Queued message for retry:', context, 'queue size:', retryQueue.length);
|
|
@@ -284,24 +313,28 @@ function processRetryQueue() {
|
|
|
284
313
|
if (success) {
|
|
285
314
|
// Success - remove from queue
|
|
286
315
|
retryQueue.splice(i, 1);
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
316
|
+
if (metricsView) {
|
|
317
|
+
Atomics.add(metricsView, METRICS_INDICES.RETRIES_SUCCEEDED, 1);
|
|
318
|
+
Atomics.add(metricsView, METRICS_INDICES.MESSAGES_RETRIED, 1);
|
|
319
|
+
Atomics.store(metricsView, METRICS_INDICES.RETRY_QUEUE_SIZE, retryQueue.length);
|
|
320
|
+
}
|
|
290
321
|
schedulerLog('[PreScheduler] Retry succeeded for:', item.context,
|
|
291
322
|
'after', item.retryCount + 1, 'attempts');
|
|
292
323
|
// Don't increment i - we removed an item
|
|
293
324
|
} else {
|
|
294
325
|
// Failed - increment retry count
|
|
295
326
|
item.retryCount++;
|
|
296
|
-
|
|
327
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.MESSAGES_RETRIED, 1);
|
|
297
328
|
|
|
298
329
|
if (item.retryCount >= MAX_RETRIES_PER_MESSAGE) {
|
|
299
330
|
// Give up on this message
|
|
300
331
|
console.error('[PreScheduler] Giving up on message after',
|
|
301
332
|
MAX_RETRIES_PER_MESSAGE, 'retries:', item.context);
|
|
302
333
|
retryQueue.splice(i, 1);
|
|
303
|
-
|
|
304
|
-
|
|
334
|
+
if (metricsView) {
|
|
335
|
+
Atomics.add(metricsView, METRICS_INDICES.RETRIES_FAILED, 1);
|
|
336
|
+
Atomics.store(metricsView, METRICS_INDICES.RETRY_QUEUE_SIZE, retryQueue.length);
|
|
337
|
+
}
|
|
305
338
|
// Don't increment i - we removed an item
|
|
306
339
|
} else {
|
|
307
340
|
// Keep in queue, try again next cycle
|
|
@@ -343,17 +376,14 @@ function scheduleEvent(oscData, editorId, runTag) {
|
|
|
343
376
|
|
|
344
377
|
heapPush(event);
|
|
345
378
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
if (stats.eventsPending > stats.maxEventsPending) {
|
|
349
|
-
stats.maxEventsPending = stats.eventsPending;
|
|
350
|
-
}
|
|
379
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.BUNDLES_SCHEDULED, 1);
|
|
380
|
+
updateMetrics(); // Update SAB with current queue depth and peak
|
|
351
381
|
|
|
352
382
|
schedulerLog('[PreScheduler] Scheduled bundle:',
|
|
353
383
|
'NTP=' + ntpTime.toFixed(3),
|
|
354
384
|
'current=' + currentNTP.toFixed(3),
|
|
355
385
|
'wait=' + (timeUntilExec * 1000).toFixed(1) + 'ms',
|
|
356
|
-
'pending=' +
|
|
386
|
+
'pending=' + eventHeap.length);
|
|
357
387
|
}
|
|
358
388
|
|
|
359
389
|
function heapPush(event) {
|
|
@@ -469,16 +499,16 @@ function checkAndDispatch() {
|
|
|
469
499
|
if (nextEvent.ntpTime <= lookaheadTime) {
|
|
470
500
|
// Ready to dispatch
|
|
471
501
|
heapPop();
|
|
472
|
-
|
|
502
|
+
updateMetrics(); // Update SAB with current queue depth
|
|
473
503
|
|
|
474
504
|
var timeUntilExec = nextEvent.ntpTime - currentNTP;
|
|
475
|
-
|
|
505
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.TOTAL_DISPATCHES, 1);
|
|
476
506
|
|
|
477
507
|
schedulerLog('[PreScheduler] Dispatching bundle:',
|
|
478
508
|
'NTP=' + nextEvent.ntpTime.toFixed(3),
|
|
479
509
|
'current=' + currentNTP.toFixed(3),
|
|
480
510
|
'early=' + (timeUntilExec * 1000).toFixed(1) + 'ms',
|
|
481
|
-
'remaining=' +
|
|
511
|
+
'remaining=' + eventHeap.length);
|
|
482
512
|
|
|
483
513
|
var success = writeToRingBuffer(nextEvent.oscData, false);
|
|
484
514
|
if (!success) {
|
|
@@ -524,8 +554,8 @@ function cancelBy(predicate) {
|
|
|
524
554
|
if (removed > 0) {
|
|
525
555
|
eventHeap = remaining;
|
|
526
556
|
heapify();
|
|
527
|
-
|
|
528
|
-
|
|
557
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.EVENTS_CANCELLED, removed);
|
|
558
|
+
updateMetrics(); // Update SAB with current queue depth
|
|
529
559
|
console.log('[PreScheduler] Cancelled ' + removed + ' events, ' + eventHeap.length + ' remaining');
|
|
530
560
|
}
|
|
531
561
|
}
|
|
@@ -553,9 +583,9 @@ function cancelAllTags() {
|
|
|
553
583
|
return;
|
|
554
584
|
}
|
|
555
585
|
var cancelled = eventHeap.length;
|
|
556
|
-
|
|
586
|
+
if (metricsView) Atomics.add(metricsView, METRICS_INDICES.EVENTS_CANCELLED, cancelled);
|
|
557
587
|
eventHeap = [];
|
|
558
|
-
|
|
588
|
+
updateMetrics(); // Update SAB (sets eventsPending to 0)
|
|
559
589
|
console.log('[PreScheduler] Cancelled all ' + cancelled + ' events');
|
|
560
590
|
// Note: Periodic timer continues running (it will just find empty queue)
|
|
561
591
|
}
|
|
@@ -633,22 +663,13 @@ self.addEventListener('message', function(event) {
|
|
|
633
663
|
break;
|
|
634
664
|
|
|
635
665
|
case 'send':
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
// New NTP-based scheduling: extract NTP from bundle
|
|
666
|
+
// NTP-based scheduling: extract NTP from bundle
|
|
639
667
|
// scheduleEvent() will dispatch immediately if not a bundle
|
|
640
668
|
scheduleEvent(
|
|
641
669
|
data.oscData,
|
|
642
670
|
data.editorId || 0,
|
|
643
671
|
data.runTag || ''
|
|
644
672
|
);
|
|
645
|
-
|
|
646
|
-
var sendDuration = performance.now() - sendStart;
|
|
647
|
-
stats.totalSendTasks++;
|
|
648
|
-
stats.totalSendProcessMs += sendDuration;
|
|
649
|
-
if (sendDuration > stats.maxSendProcessMs) {
|
|
650
|
-
stats.maxSendProcessMs = sendDuration;
|
|
651
|
-
}
|
|
652
673
|
break;
|
|
653
674
|
|
|
654
675
|
case 'sendImmediate':
|
|
@@ -669,32 +690,6 @@ self.addEventListener('message', function(event) {
|
|
|
669
690
|
cancelAllTags();
|
|
670
691
|
break;
|
|
671
692
|
|
|
672
|
-
case 'getStats':
|
|
673
|
-
self.postMessage({
|
|
674
|
-
type: 'stats',
|
|
675
|
-
stats: {
|
|
676
|
-
bundlesScheduled: stats.bundlesScheduled,
|
|
677
|
-
bundlesWritten: stats.bundlesWritten,
|
|
678
|
-
bundlesDropped: stats.bundlesDropped,
|
|
679
|
-
bufferOverruns: stats.bufferOverruns,
|
|
680
|
-
eventsPending: stats.eventsPending,
|
|
681
|
-
maxEventsPending: stats.maxEventsPending,
|
|
682
|
-
eventsCancelled: stats.eventsCancelled,
|
|
683
|
-
totalDispatches: stats.totalDispatches,
|
|
684
|
-
totalLateDispatchMs: stats.totalLateDispatchMs,
|
|
685
|
-
maxLateDispatchMs: stats.maxLateDispatchMs,
|
|
686
|
-
totalSendTasks: stats.totalSendTasks,
|
|
687
|
-
totalSendProcessMs: stats.totalSendProcessMs,
|
|
688
|
-
maxSendProcessMs: stats.maxSendProcessMs,
|
|
689
|
-
messagesRetried: stats.messagesRetried,
|
|
690
|
-
retriesSucceeded: stats.retriesSucceeded,
|
|
691
|
-
retriesFailed: stats.retriesFailed,
|
|
692
|
-
retryQueueSize: stats.retryQueueSize,
|
|
693
|
-
maxRetryQueueSize: stats.maxRetryQueueSize
|
|
694
|
-
}
|
|
695
|
-
});
|
|
696
|
-
break;
|
|
697
|
-
|
|
698
693
|
default:
|
|
699
694
|
console.warn('[OSCPreSchedulerWorker] Unknown message type:', data.type);
|
|
700
695
|
}
|