@andersbakken/fisk 4.0.55 → 4.0.57

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.
@@ -42,6 +42,12 @@ const Constants = {
42
42
  get JSON() {
43
43
  return 5;
44
44
  },
45
+ get AcquireSlot() {
46
+ return 6;
47
+ },
48
+ get ReleaseLocalSlot() {
49
+ return 7;
50
+ },
45
51
  // daemon codes
46
52
  get CppSlotAcquired() {
47
53
  return 10;
@@ -51,6 +57,9 @@ const Constants = {
51
57
  },
52
58
  get JSONResponse() {
53
59
  return 12;
60
+ },
61
+ get LocalSlotAcquired() {
62
+ return 13;
54
63
  }
55
64
  };
56
65
 
@@ -208,6 +217,12 @@ class Compile extends EventEmitter__default["default"] {
208
217
  case Constants.ReleaseCompileSlot:
209
218
  emit("releaseCompileSlot");
210
219
  continue;
220
+ case Constants.AcquireSlot:
221
+ emit("acquireSlot");
222
+ continue;
223
+ case Constants.ReleaseLocalSlot:
224
+ emit("releaseLocalSlot");
225
+ continue;
211
226
  case Constants.JSON:
212
227
  if (available < 5) {
213
228
  break;
@@ -3330,16 +3345,40 @@ class Slots extends EventEmitter__default["default"] {
3330
3345
  this.debug = debug;
3331
3346
  this.used = new Map();
3332
3347
  this.pending = new Map();
3348
+ this._totalAcquired = 0;
3333
3349
  if (this.debug) {
3334
3350
  console.log("Slots created", this.toString());
3335
3351
  }
3336
3352
  }
3353
+ get capacity() {
3354
+ return this.count;
3355
+ }
3356
+ get active() {
3357
+ return this.used.size;
3358
+ }
3359
+ get totalAcquired() {
3360
+ return this._totalAcquired;
3361
+ }
3362
+ tryAcquire(id, data) {
3363
+ if (this.used.size < this.count) {
3364
+ this.used.set(id, data);
3365
+ ++this._totalAcquired;
3366
+ if (this.debug) {
3367
+ console.log("tryAcquire succeeded", id, data, this.toString());
3368
+ }
3369
+ this.emit("changed");
3370
+ return true;
3371
+ }
3372
+ return false;
3373
+ }
3337
3374
  acquire(id, data, cb) {
3338
3375
  if (this.used.size < this.count) {
3339
3376
  this.used.set(id, data);
3377
+ ++this._totalAcquired;
3340
3378
  if (this.debug) {
3341
3379
  console.log("acquired slot", id, data, this.toString());
3342
3380
  }
3381
+ this.emit("changed");
3343
3382
  cb();
3344
3383
  }
3345
3384
  else {
@@ -3363,9 +3402,11 @@ class Slots extends EventEmitter__default["default"] {
3363
3402
  for (const p of this.pending) {
3364
3403
  this.used.set(p[0], p[1].data);
3365
3404
  this.pending.delete(p[0]);
3405
+ ++this._totalAcquired;
3366
3406
  p[1].cb();
3367
3407
  break;
3368
3408
  }
3409
+ this.emit("changed");
3369
3410
  }
3370
3411
  }
3371
3412
  toString() {
@@ -4118,6 +4159,8 @@ Options:
4118
4159
  --socket=PATH Unix socket path (default: ~/.cache/fisk/daemon/socket)
4119
4160
  --cpp-slots=N Preprocess slot count (default: cpus * 2)
4120
4161
  --slots=N Compile slot count (default: cpus)
4162
+ --local-slots=N Local compile slot count (default: 0, disabled)
4163
+ --local-slots-max-load=N Max system load average (1-min) to allow local compiles (default: 0, no limit)
4121
4164
  --cache-dir=PATH Cache directory (default: ~/.cache/fisk/daemon)
4122
4165
 
4123
4166
  Config files: ~/.config/fisk/daemon.conf, /etc/xdg/fisk/daemon.conf
@@ -4153,15 +4196,87 @@ server.on("error", (err) => {
4153
4196
  });
4154
4197
  const cppSlots = new Slots(option.int("cpp-slots", Math.max(os__default["default"].cpus().length * 2, 1)), "cpp", debug);
4155
4198
  const compileSlots = new Slots(option.int("slots", Math.max(os__default["default"].cpus().length, 1)), "compile", debug);
4199
+ const localSlotCount = option.int("local-slots", 0);
4200
+ const localSlots = new Slots(localSlotCount, "local", debug);
4201
+ const localSlotsMaxLoad = option("local-slots-max-load") || 0;
4202
+ const slotSubscribers = [];
4203
+ function slotsInfo() {
4204
+ return {
4205
+ type: "slotsInfo",
4206
+ local: {
4207
+ active: localSlots.active,
4208
+ capacity: localSlots.capacity,
4209
+ total: localSlots.totalAcquired
4210
+ },
4211
+ cpp: {
4212
+ active: cppSlots.active,
4213
+ capacity: cppSlots.capacity,
4214
+ total: cppSlots.totalAcquired
4215
+ },
4216
+ compile: {
4217
+ active: compileSlots.active,
4218
+ capacity: compileSlots.capacity,
4219
+ total: compileSlots.totalAcquired
4220
+ }
4221
+ };
4222
+ }
4223
+ function broadcastSlotsInfo() {
4224
+ if (slotSubscribers.length === 0) {
4225
+ return;
4226
+ }
4227
+ const info = slotsInfo();
4228
+ for (const sub of slotSubscribers) {
4229
+ sub.compile.send(info);
4230
+ }
4231
+ }
4232
+ for (const slots of [localSlots, cppSlots, compileSlots]) {
4233
+ slots.on("changed", broadcastSlotsInfo);
4234
+ }
4235
+ function canAcquireLocalSlot() {
4236
+ if (localSlotCount <= 0) {
4237
+ return false;
4238
+ }
4239
+ if (localSlotsMaxLoad > 0) {
4240
+ const loadAvg = os__default["default"].loadavg()[0];
4241
+ if (loadAvg > localSlotsMaxLoad) {
4242
+ if (debug) {
4243
+ console.log(`Local slot denied: load ${loadAvg.toFixed(2)} > max ${localSlotsMaxLoad}`);
4244
+ }
4245
+ return false;
4246
+ }
4247
+ }
4248
+ return true;
4249
+ }
4156
4250
  server.on("compile", (compile) => {
4157
4251
  compile.on("dumpSlots", () => {
4158
- const ret = { cpp: cppSlots.dump(), compile: compileSlots.dump() };
4252
+ const ret = { cpp: cppSlots.dump(), compile: compileSlots.dump(), local: localSlots.dump() };
4159
4253
  if (debug) {
4160
4254
  console.log("sending dump", ret);
4161
4255
  }
4162
4256
  compile.send(ret);
4163
4257
  });
4258
+ compile.on("subscribeSlots", () => {
4259
+ if (debug) {
4260
+ console.log("subscribeSlots from", compile.id);
4261
+ }
4262
+ const subscriber = {
4263
+ compile,
4264
+ handler: () => {
4265
+ // Remove subscriber on disconnect
4266
+ const idx = slotSubscribers.indexOf(subscriber);
4267
+ if (idx !== -1) {
4268
+ slotSubscribers.splice(idx, 1);
4269
+ }
4270
+ }
4271
+ };
4272
+ slotSubscribers.push(subscriber);
4273
+ compile.on("end", subscriber.handler);
4274
+ compile.on("error", subscriber.handler);
4275
+ // Send current state immediately
4276
+ compile.send(slotsInfo());
4277
+ });
4164
4278
  let requestedCppSlot = false;
4279
+ let requestedLocalSlot = false;
4165
4280
  compile.on("acquireCppSlot", () => {
4166
4281
  if (debug) {
4167
4282
  console.log("acquireCppSlot");
@@ -4205,6 +4320,38 @@ server.on("compile", (compile) => {
4205
4320
  compileSlots.release(compile.id);
4206
4321
  }
4207
4322
  });
4323
+ compile.on("acquireSlot", () => {
4324
+ if (debug) {
4325
+ console.log("acquireSlot");
4326
+ }
4327
+ if (canAcquireLocalSlot() && localSlots.tryAcquire(compile.id, { pid: compile.pid })) {
4328
+ if (debug) {
4329
+ console.log("acquireSlot -> local slot granted");
4330
+ }
4331
+ requestedLocalSlot = true;
4332
+ compile.send(Constants.LocalSlotAcquired);
4333
+ }
4334
+ else {
4335
+ if (debug) {
4336
+ console.log("acquireSlot -> falling back to cpp slot");
4337
+ }
4338
+ assert__default["default"](!requestedCppSlot);
4339
+ requestedCppSlot = true;
4340
+ cppSlots.acquire(compile.id, { pid: compile.pid }, () => {
4341
+ compile.send(Constants.CppSlotAcquired);
4342
+ });
4343
+ }
4344
+ });
4345
+ compile.on("releaseLocalSlot", () => {
4346
+ if (debug) {
4347
+ console.log("releaseLocalSlot");
4348
+ }
4349
+ assert__default["default"](requestedLocalSlot);
4350
+ if (requestedLocalSlot) {
4351
+ requestedLocalSlot = false;
4352
+ localSlots.release(compile.id);
4353
+ }
4354
+ });
4208
4355
  compile.on("error", (err) => {
4209
4356
  if (debug) {
4210
4357
  console.error("Got error from fiskc", compile.id, compile.pid, err);
@@ -4217,6 +4364,10 @@ server.on("compile", (compile) => {
4217
4364
  requestedCompileSlot = false;
4218
4365
  compileSlots.release(compile.id);
4219
4366
  }
4367
+ if (requestedLocalSlot) {
4368
+ requestedLocalSlot = false;
4369
+ localSlots.release(compile.id);
4370
+ }
4220
4371
  });
4221
4372
  compile.on("end", () => {
4222
4373
  if (debug) {
@@ -4230,6 +4381,10 @@ server.on("compile", (compile) => {
4230
4381
  requestedCompileSlot = false;
4231
4382
  compileSlots.release(compile.id);
4232
4383
  }
4384
+ if (requestedLocalSlot) {
4385
+ requestedLocalSlot = false;
4386
+ localSlots.release(compile.id);
4387
+ }
4233
4388
  });
4234
4389
  });
4235
4390
  process.on("exit", () => {
@@ -5895,6 +5895,7 @@ if (process.argv.includes("--help") || process.argv.includes("-h")) {
5895
5895
 
5896
5896
  Options:
5897
5897
  --scheduler=URL Scheduler URL (default: ws://localhost:8097)
5898
+ --daemon-socket=PATH Daemon Unix socket path (default: ~/.cache/fisk/daemon/socket)
5898
5899
 
5899
5900
  Config files: ~/.config/fisk/monitor.conf, /etc/xdg/fisk/monitor.conf
5900
5901
  Environment variables: FISK_MONITOR_SCHEDULER, etc.`);
@@ -6298,7 +6299,13 @@ function notify(msg) {
6298
6299
  return;
6299
6300
  }
6300
6301
  const notifyNow = (message) => {
6301
- notificationBox.setContent(message || "");
6302
+ if (message) {
6303
+ notificationBox.setContent(message);
6304
+ }
6305
+ else {
6306
+ updateNotificationBar();
6307
+ return;
6308
+ }
6302
6309
  screen.render();
6303
6310
  };
6304
6311
  notificationInterval = setInterval(() => {
@@ -6624,5 +6631,91 @@ function connect() {
6624
6631
  setTimeout(connect, 1000);
6625
6632
  });
6626
6633
  }
6634
+ // --- Daemon connection for local slot info ---
6635
+ const DaemonJSON = 5;
6636
+ const DaemonJSONResponse = 12;
6637
+ const daemonSocketPath = String(option("daemon-socket", path__default["default"].join(require$$2__default["default"].homedir(), ".cache", "fisk", "daemon", "socket")));
6638
+ let daemonSlotsInfo;
6639
+ let daemonSocket;
6640
+ let daemonRecvBuffer = Buffer.alloc(0);
6641
+ function updateNotificationBar() {
6642
+ if (daemonSlotsInfo && daemonSlotsInfo.local.capacity > 0) {
6643
+ const local = daemonSlotsInfo.local;
6644
+ notificationBox.setContent(`{bold}Local:{/bold} ${local.active}/${local.capacity} active, ${local.total} total`);
6645
+ }
6646
+ else {
6647
+ notificationBox.setContent("");
6648
+ }
6649
+ screen.render();
6650
+ }
6651
+ function processDaemonMessage(json) {
6652
+ try {
6653
+ const msg = JSON.parse(json);
6654
+ if (msg.type === "slotsInfo") {
6655
+ daemonSlotsInfo = msg;
6656
+ updateNotificationBar();
6657
+ }
6658
+ }
6659
+ catch (e) {
6660
+ // ignore parse errors
6661
+ }
6662
+ }
6663
+ function processDaemonData() {
6664
+ while (daemonRecvBuffer.length > 0) {
6665
+ const type = daemonRecvBuffer[0];
6666
+ if (type === DaemonJSONResponse) {
6667
+ if (daemonRecvBuffer.length < 5) {
6668
+ break; // need more data for length header
6669
+ }
6670
+ const msgLen = daemonRecvBuffer.readUInt32BE(1);
6671
+ if (daemonRecvBuffer.length < 5 + msgLen) {
6672
+ break; // need more data for body
6673
+ }
6674
+ const json = daemonRecvBuffer.subarray(5, 5 + msgLen).toString("utf8");
6675
+ daemonRecvBuffer = daemonRecvBuffer.subarray(5 + msgLen);
6676
+ processDaemonMessage(json);
6677
+ }
6678
+ else {
6679
+ // Unknown single-byte message, skip it
6680
+ daemonRecvBuffer = daemonRecvBuffer.subarray(1);
6681
+ }
6682
+ }
6683
+ }
6684
+ function sendDaemonJSON(sock, obj) {
6685
+ const jsonBuf = Buffer.from(JSON.stringify(obj), "utf8");
6686
+ const header = Buffer.allocUnsafe(5);
6687
+ header.writeUInt8(DaemonJSON, 0);
6688
+ header.writeUInt32BE(jsonBuf.length, 1);
6689
+ sock.write(header);
6690
+ sock.write(jsonBuf);
6691
+ }
6692
+ function connectDaemon() {
6693
+ daemonSlotsInfo = undefined;
6694
+ daemonRecvBuffer = Buffer.alloc(0);
6695
+ daemonSocket = require$$3__default["default"].createConnection(daemonSocketPath);
6696
+ daemonSocket.on("connect", () => {
6697
+ assert__default["default"](daemonSocket);
6698
+ // Send PID (4 bytes, big-endian) as the daemon protocol requires
6699
+ const pidBuf = Buffer.allocUnsafe(4);
6700
+ pidBuf.writeUInt32BE(process.pid, 0);
6701
+ daemonSocket.write(pidBuf);
6702
+ // Subscribe to slot updates
6703
+ sendDaemonJSON(daemonSocket, { type: "subscribeSlots" });
6704
+ });
6705
+ daemonSocket.on("data", (data) => {
6706
+ daemonRecvBuffer = Buffer.concat([daemonRecvBuffer, data]);
6707
+ processDaemonData();
6708
+ });
6709
+ daemonSocket.on("error", () => {
6710
+ // Daemon may not be running, silently retry
6711
+ });
6712
+ daemonSocket.on("close", () => {
6713
+ daemonSocket = undefined;
6714
+ daemonSlotsInfo = undefined;
6715
+ updateNotificationBar();
6716
+ setTimeout(connectDaemon, 5000);
6717
+ });
6718
+ }
6627
6719
  connect();
6720
+ connectDaemon();
6628
6721
  //# sourceMappingURL=fisk-monitor.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andersbakken/fisk",
3
- "version": "4.0.55",
3
+ "version": "4.0.57",
4
4
  "description": "Fisk, a distributed compile system",
5
5
  "scripts": {
6
6
  "lint": "eslint . --ext .ts",