@yuneta/gobj-js 7.9.10 → 7.10.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.
@@ -1415,6 +1415,7 @@ function gobj_destroy(gobj) {
1415
1415
  gobj_stop(gobj);
1416
1416
  }
1417
1417
  gobj.obflag |= obflag_t.obflag_destroying;
1418
+ purge_posted_events(gobj);
1418
1419
  let trace_creation = __trace_gobj_create_delete__(gobj);
1419
1420
  if (trace_creation) trace_machine(sprintf("💔💔⏩ destroying: %s", gobj_full_name(gobj)));
1420
1421
  let parent = gobj_parent(gobj);
@@ -2354,13 +2355,97 @@ function gobj_has_output_event(gobj, event, event_flag) {
2354
2355
  return true;
2355
2356
  }
2356
2357
  /************************************************************
2357
- * post_event, by now only in js
2358
+ * Posted events: gobj_send_event(), but on the next turn
2359
+ *
2360
+ * Same contract as the C side (see gobj.h). It exists to get out of the
2361
+ * stack you are standing on: gobj_publish_event() dispatches synchronously,
2362
+ * so a subscriber action runs inside the publisher's own stack, and
2363
+ * destroying that publisher there dismantles what is still iterating.
2364
+ *
2365
+ * The queue is drained with setTimeout(0) and NOT with queueMicrotask():
2366
+ * a microtask runs before the browser can paint or handle an input, so a
2367
+ * chain of posted events would hold the page exactly the way it used to
2368
+ * hold the event loop in C. A macrotask gives the browser its turn between
2369
+ * one event and the next.
2370
+ *
2371
+ * Delivery is a SNAPSHOT: the events queued when the drain begins are the
2372
+ * ones delivered in it, and whatever an action posts while it runs waits
2373
+ * for the following turn.
2358
2374
  ************************************************************/
2359
- function gobj_post_event(gobj, event, kw, src) {
2360
- setTimeout(() => {
2361
- if (gobj_is_destroying(gobj)) return;
2362
- gobj_send_event(gobj, event, kw, src);
2363
- }, 10);
2375
+ var MAX_POSTED_EVENTS = 1e4;
2376
+ var __dl_posted_events__ = [];
2377
+ var __posted_events_scheduled__ = false;
2378
+ function gobj_post_event(dst, event, kw, src) {
2379
+ if (!dst || !(dst instanceof GObj)) {
2380
+ log_error(`gobj_post_event(): gobj dst NULL, event ${event}`);
2381
+ return -1;
2382
+ }
2383
+ if (dst.obflag & (obflag_t.obflag_destroyed | obflag_t.obflag_destroying)) {
2384
+ log_error(`gobj_post_event(): gobj dst DESTROYING, event ${event}`);
2385
+ return -1;
2386
+ }
2387
+ if (empty_string(event)) {
2388
+ log_error(`gobj_post_event(): event EMPTY`);
2389
+ return -1;
2390
+ }
2391
+ if (!gobj_has_event(dst, event, 0)) {
2392
+ log_error(`gobj_post_event(): event NOT DEFINED in gclass ${gobj_gclass_name(dst)}, event ${event}`);
2393
+ return -1;
2394
+ }
2395
+ if (__dl_posted_events__.length >= MAX_POSTED_EVENTS) {
2396
+ log_error(`gobj_post_event(): too many posted events, this is NOT a work queue, event ${event}, pending ${__dl_posted_events__.length}`);
2397
+ return -1;
2398
+ }
2399
+ __dl_posted_events__.push({
2400
+ dst,
2401
+ event,
2402
+ kw,
2403
+ src
2404
+ });
2405
+ if (is_machine_tracing(dst, event) && !is_machine_not_tracing(src, event)) trace_machine(sprintf("📬 posted(%s%s), ev: %s, pending: %d", !dst.running ? "!!" : "", gobj_short_name(dst), event, __dl_posted_events__.length));
2406
+ if (!__posted_events_scheduled__) {
2407
+ __posted_events_scheduled__ = true;
2408
+ setTimeout(gobj_deliver_posted_events, 0);
2409
+ }
2410
+ return 0;
2411
+ }
2412
+ /************************************************************
2413
+ * How many posted events wait for delivery
2414
+ ************************************************************/
2415
+ function gobj_posted_events_size() {
2416
+ return __dl_posted_events__.length;
2417
+ }
2418
+ /************************************************************
2419
+ * Deliver the posted events. One turn, one snapshot.
2420
+ ************************************************************/
2421
+ function gobj_deliver_posted_events() {
2422
+ __posted_events_scheduled__ = false;
2423
+ let snapshot = __dl_posted_events__;
2424
+ __dl_posted_events__ = [];
2425
+ let delivered = 0;
2426
+ for (let msg of snapshot) {
2427
+ if (msg.dst.obflag & (obflag_t.obflag_destroyed | obflag_t.obflag_destroying)) continue;
2428
+ gobj_send_event(msg.dst, msg.event, msg.kw, msg.src);
2429
+ delivered++;
2430
+ }
2431
+ if (__dl_posted_events__.length > 0 && !__posted_events_scheduled__) {
2432
+ __posted_events_scheduled__ = true;
2433
+ setTimeout(gobj_deliver_posted_events, 0);
2434
+ }
2435
+ return delivered;
2436
+ }
2437
+ /************************************************************
2438
+ * Take a gobj out of the queue of posted events.
2439
+ *
2440
+ * Called from gobj_destroy(), and the two halves are not the same thing:
2441
+ * as DESTINATION the entry is dropped, as SOURCE only the pointer is
2442
+ * cleared -- the destination still wants its event, and it arrives with
2443
+ * src null, which every action already has to accept.
2444
+ ************************************************************/
2445
+ function purge_posted_events(gobj) {
2446
+ if (__dl_posted_events__.length === 0) return;
2447
+ for (let msg of __dl_posted_events__) if (msg.src === gobj) msg.src = null;
2448
+ __dl_posted_events__ = __dl_posted_events__.filter((msg) => msg.dst !== gobj);
2364
2449
  }
2365
2450
  /************************************************************
2366
2451
  * send_event
@@ -5654,7 +5739,6 @@ function mt_writing$1(gobj, path) {}
5654
5739
  * Framework Method: Start
5655
5740
  ***************************************************************/
5656
5741
  function mt_start$1(gobj) {
5657
- gobj_start(gobj.priv.gobj_timer);
5658
5742
  set_timeout_periodic(gobj.priv.gobj_timer, gobj.priv.periodic_timeout);
5659
5743
  return 0;
5660
5744
  }
@@ -5662,7 +5746,7 @@ function mt_start$1(gobj) {
5662
5746
  * Framework Method: Stop
5663
5747
  ***************************************************************/
5664
5748
  function mt_stop$1(gobj) {
5665
- gobj_stop(gobj.priv.gobj_timer);
5749
+ clear_timeout(gobj.priv.gobj_timer);
5666
5750
  gobj_stop_children(gobj);
5667
5751
  return 0;
5668
5752
  }
@@ -5834,7 +5918,6 @@ function mt_writing(gobj, path) {
5834
5918
  ***************************************************************/
5835
5919
  function mt_start(gobj) {
5836
5920
  let priv = gobj.priv;
5837
- gobj_start(priv.gobj_timer);
5838
5921
  priv.retry_ms = 0;
5839
5922
  priv.websocket = setup_websocket(gobj);
5840
5923
  return 0;
@@ -5845,7 +5928,6 @@ function mt_start(gobj) {
5845
5928
  function mt_stop(gobj) {
5846
5929
  let priv = gobj.priv;
5847
5930
  clear_timeout(priv.gobj_timer);
5848
- gobj_stop(priv.gobj_timer);
5849
5931
  if (priv.websocket) close_websocket(gobj);
5850
5932
  return 0;
5851
5933
  }
@@ -6580,6 +6662,7 @@ exports.gobj_create_volatil = gobj_create_volatil;
6580
6662
  exports.gobj_create_yuno = gobj_create_yuno;
6581
6663
  exports.gobj_current_state = gobj_current_state;
6582
6664
  exports.gobj_default_service = gobj_default_service;
6665
+ exports.gobj_deliver_posted_events = gobj_deliver_posted_events;
6583
6666
  exports.gobj_destroy = gobj_destroy;
6584
6667
  exports.gobj_find_child = gobj_find_child;
6585
6668
  exports.gobj_find_gobj = gobj_find_gobj;
@@ -6611,6 +6694,7 @@ exports.gobj_parent = gobj_parent;
6611
6694
  exports.gobj_pause = gobj_pause;
6612
6695
  exports.gobj_play = gobj_play;
6613
6696
  exports.gobj_post_event = gobj_post_event;
6697
+ exports.gobj_posted_events_size = gobj_posted_events_size;
6614
6698
  exports.gobj_publish_event = gobj_publish_event;
6615
6699
  exports.gobj_read_attr = gobj_read_attr;
6616
6700
  exports.gobj_read_attrs = gobj_read_attrs;