asterism-zenoh 0.3.0 → 0.4.1

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.
@@ -30,11 +30,16 @@
30
30
  * it, the ones without it by the session's op_lock, and closing marks the
31
31
  * session closed for Ruby (with the GVL held) before zenoh-c drops it.
32
32
  *
33
- * Losing the connection (same rules as the mruby gem): a client session is
34
- * closed when it has no router left, a peer session that only connects when
35
- * it has no peer left; a listening peer session stays open. The check runs
36
- * in poll, closed?, put and get. From then on poll returns false, closed?
37
- * is true and put raises Asterism::Zenoh::Error. There is no reconnection.
33
+ * Losing the connection (same rules as the mruby gem):
34
+ * - A client session is not closed when its router goes away: zenoh-c opens
35
+ * the connection again by itself and declares again what the session had
36
+ * declared (subscribers, queryables, liveliness tokens and watches), so
37
+ * the Ruby objects stay valid. Meanwhile reconnecting? is true (no
38
+ * router), put reaches nobody and a get ends at its time limit.
39
+ * - A peer session that only connects is closed when it has no peer left; a
40
+ * listening peer session stays open.
41
+ * The check runs in poll, closed?, put and get. Once closed, poll returns
42
+ * false, closed? is true and put raises Asterism::Zenoh::ClosedError.
38
43
  *
39
44
  * Lifetime: closing is optional. A session keeps lists of its live
40
45
  * subscribers, queryables and tokens and undeclares them before it closes,
@@ -43,11 +48,13 @@
43
48
  * undeclared or the get is finished); whichever lets go last frees it.
44
49
  */
45
50
  #include <pthread.h>
51
+ #include <stdarg.h>
46
52
  #include <stdatomic.h>
47
53
  #include <stdbool.h>
48
54
  #include <stdint.h>
49
55
  #include <stdlib.h>
50
56
  #include <string.h>
57
+ #include <unistd.h>
51
58
 
52
59
  #include <ruby.h>
53
60
  #include <ruby/encoding.h>
@@ -56,7 +63,13 @@
56
63
  #include <zenoh.h>
57
64
 
58
65
  #define ZRB_DEFAULT_DEPTH 16
59
- #define ZRB_MAX_DEPTH 1024
66
+ /* A get, a liveliness get and a liveliness watch hold every reply or token
67
+ * a router sends in one burst (a wildcard over a busy network). On a PC the
68
+ * queue costs nothing until it fills: zenoh-c's FIFO and the gem's lists
69
+ * grow as entries come. */
70
+ #define ZRB_DEFAULT_GET_DEPTH 1024
71
+ #define ZRB_DEFAULT_WATCH_DEPTH 1024
72
+ #define ZRB_MAX_DEPTH 65536
60
73
  #define ZRB_DEFAULT_POLL_STEPS 8
61
74
  #define ZRB_DEFAULT_GET_TIMEOUT_MS 2000
62
75
  #define ZRB_MAX_GET_TIMEOUT_MS 600000
@@ -64,13 +77,13 @@
64
77
  #define ZRB_CONNECT_TIMEOUT_MS 3000
65
78
  #define ZRB_SEND_TIMEOUT_MS 3000
66
79
 
67
- static VALUE mAsterism, mZenoh, eZenohError;
80
+ static VALUE mAsterism, mZenoh, eAsterismError, eZenohError, eClosedError;
68
81
  static VALUE cSession, cSubscriber, cWatch, cQueryable, cQuery, cGet, cToken;
69
82
  static VALUE cAdvSubscriber, cPublisher, cAdvPublisher, cQuerier, cKeyExpr, cTimestamp;
70
83
  static VALUE cMatchingListener, cEventListener;
71
84
  static ID id_attachment, id_target, id_consolidation, id_complete, id_mode, id_listen;
72
85
  static ID id_config, id_config_file, id_scouting, id_history, id_new;
73
- static ID id_iv_session, id_iv_key, id_iv_parent;
86
+ static ID id_iv_session, id_iv_key, id_iv_parent, id_iv_code, id_iv_qkey;
74
87
  static ID id_client, id_peer, id_router, id_all, id_all_complete, id_best_matching;
75
88
  static ID id_none, id_latest, id_monotonic, id_auto;
76
89
 
@@ -347,14 +360,16 @@ static void zrb_owned_key(z_owned_keyexpr_t *ke, const char *key) {
347
360
  }
348
361
  }
349
362
 
350
- static long zrb_check_depth(VALUE v) {
351
- long depth = NIL_P(v) ? ZRB_DEFAULT_DEPTH : NUM2LONG(v);
363
+ static long zrb_check_depth_or(VALUE v, long def) {
364
+ long depth = NIL_P(v) ? def : NUM2LONG(v);
352
365
  if (depth < 1 || depth > ZRB_MAX_DEPTH) {
353
366
  rb_raise(rb_eArgError, "depth must be 1..%d", ZRB_MAX_DEPTH);
354
367
  }
355
368
  return depth;
356
369
  }
357
370
 
371
+ static long zrb_check_depth(VALUE v) { return zrb_check_depth_or(v, ZRB_DEFAULT_DEPTH); }
372
+
358
373
  static long zrb_check_timeout(VALUE v) {
359
374
  long t = NIL_P(v) ? ZRB_DEFAULT_GET_TIMEOUT_MS : NUM2LONG(v);
360
375
  if (t < 1 || t > ZRB_MAX_GET_TIMEOUT_MS) {
@@ -407,6 +422,22 @@ static void zrb_hold_session(VALUE obj, VALUE session, VALUE key) {
407
422
  }
408
423
  }
409
424
 
425
+ /* Raises klass with the zenoh-c result code as the exception's code
426
+ * (Asterism::Zenoh::Error#code) as well as in the message. */
427
+ #ifdef RBIMPL_ATTR_FORMAT
428
+ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 4)
429
+ #endif
430
+ NORETURN(static void zrb_raise_code(VALUE klass, int code, const char *fmt, ...));
431
+ static void zrb_raise_code(VALUE klass, int code, const char *fmt, ...) {
432
+ va_list ap;
433
+ va_start(ap, fmt);
434
+ VALUE msg = rb_vsprintf(fmt, ap);
435
+ va_end(ap);
436
+ VALUE exc = rb_exc_new_str(klass, msg);
437
+ rb_ivar_set(exc, id_iv_code, INT2NUM(code));
438
+ rb_exc_raise(exc);
439
+ }
440
+
410
441
  /* ------------------------------------------------------- object structs */
411
442
 
412
443
  typedef struct zrb_session zrb_session;
@@ -441,6 +472,7 @@ struct zrb_session {
441
472
  zrb_qable *qables;
442
473
  zrb_token *tokens;
443
474
  zrb_ent *ents; /* the rest (0.3.0) */
475
+ pid_t pid; /* the process that opened it (zenoh-c's threads do not survive fork) */
444
476
  };
445
477
 
446
478
  typedef enum { ZSUB_PLAIN, ZSUB_LIVELINESS, ZSUB_ADVANCED } zsub_type;
@@ -913,6 +945,7 @@ typedef struct {
913
945
  int accept_replies;
914
946
  VALUE target, consolidation, timeout;
915
947
  bool has_target, has_consolidation, has_timeout;
948
+ VALUE depth; /* gets: nil (the default) or the queue depth */
916
949
  } zrb_opts;
917
950
 
918
951
  static int zrb_priority_arg(VALUE v) {
@@ -1018,6 +1051,7 @@ static void zrb_opts_parse(VALUE hash, const char *const *names, int n, zrb_opts
1018
1051
  o->priority = o->congestion_control = o->express = o->reliability = o->destination = o->accept_replies = -1;
1019
1052
  o->target = o->consolidation = o->timeout = Qnil;
1020
1053
  o->has_target = o->has_consolidation = o->has_timeout = false;
1054
+ o->depth = Qnil;
1021
1055
  if (NIL_P(hash)) {
1022
1056
  return;
1023
1057
  }
@@ -1079,6 +1113,8 @@ static void zrb_opts_parse(VALUE hash, const char *const *names, int n, zrb_opts
1079
1113
  } else if (strcmp(k, "timeout_ms") == 0) {
1080
1114
  o->timeout = v;
1081
1115
  o->has_timeout = true;
1116
+ } else if (strcmp(k, "depth") == 0) {
1117
+ o->depth = v;
1082
1118
  }
1083
1119
  }
1084
1120
  }
@@ -1670,6 +1706,9 @@ static VALUE zrb_query_yield_ensure(VALUE obj) {
1670
1706
  static VALUE zrb_qable_each_pending(VALUE self) {
1671
1707
  zrb_qable *q = zrb_qable_get(self);
1672
1708
  bool collect = !rb_block_given_p();
1709
+ /* The queryable's own key goes with each query (Query#reply with one
1710
+ * argument looks at it). */
1711
+ VALUE qkey = rb_attr_get(self, id_iv_key);
1673
1712
  VALUE out = collect ? rb_ary_new() : Qnil;
1674
1713
  long taken = 0;
1675
1714
  unsigned todo = atomic_load(&q->ch->queued);
@@ -1680,6 +1719,9 @@ static VALUE zrb_qable_each_pending(VALUE self) {
1680
1719
  break;
1681
1720
  }
1682
1721
  VALUE obj = zrb_query_wrap(&oq);
1722
+ if (!NIL_P(qkey)) {
1723
+ rb_ivar_set(obj, id_iv_qkey, qkey);
1724
+ }
1683
1725
  taken++;
1684
1726
  if (collect) {
1685
1727
  rb_ary_push(out, obj);
@@ -1806,7 +1848,7 @@ static VALUE zrb_query_reply(int argc, VALUE *argv, VALUE self) {
1806
1848
  zrb_bytes_from_str(&bytes, payload, "payload");
1807
1849
  z_result_t ret = z_query_reply(z_loan(zq->query), kp, z_move(bytes), &ro);
1808
1850
  if (ret != Z_OK) {
1809
- rb_raise(eZenohError, "reply failed (%d)%" PRIsVALUE, (int)ret, zrb_last_error());
1851
+ zrb_raise_code(eZenohError, (int)ret, "reply failed (%d)%" PRIsVALUE, (int)ret, zrb_last_error());
1810
1852
  }
1811
1853
  return Qnil;
1812
1854
  }
@@ -1833,7 +1875,7 @@ static VALUE zrb_query_reply_err(int argc, VALUE *argv, VALUE self) {
1833
1875
  zrb_bytes_from_str(&bytes, payload, "payload");
1834
1876
  z_result_t ret = z_query_reply_err(z_loan(zq->query), z_move(bytes), &eo);
1835
1877
  if (ret != Z_OK) {
1836
- rb_raise(eZenohError, "reply_err failed (%d)%" PRIsVALUE, (int)ret, zrb_last_error());
1878
+ zrb_raise_code(eZenohError, (int)ret, "reply_err failed (%d)%" PRIsVALUE, (int)ret, zrb_last_error());
1837
1879
  }
1838
1880
  return Qnil;
1839
1881
  }
@@ -1876,7 +1918,7 @@ static VALUE zrb_query_reply_del(int argc, VALUE *argv, VALUE self) {
1876
1918
  }
1877
1919
  z_result_t ret = z_query_reply_del(z_loan(zq->query), kp, &d);
1878
1920
  if (ret != Z_OK) {
1879
- rb_raise(eZenohError, "reply_del failed (%d)%" PRIsVALUE, (int)ret, zrb_last_error());
1921
+ zrb_raise_code(eZenohError, (int)ret, "reply_del failed (%d)%" PRIsVALUE, (int)ret, zrb_last_error());
1880
1922
  }
1881
1923
  return Qnil;
1882
1924
  }
@@ -1915,10 +1957,12 @@ static zrb_get *zrb_get_get(VALUE self) {
1915
1957
  return g;
1916
1958
  }
1917
1959
 
1918
- static VALUE zrb_get_new(zrb_get **out) {
1960
+ /* depth: how many replies wait to be taken; past it the oldest go (and
1961
+ * are counted in dropped). */
1962
+ static VALUE zrb_get_new(long depth, zrb_get **out) {
1919
1963
  zrb_get *g;
1920
1964
  VALUE obj = TypedData_Make_Struct(cGet, zrb_get, &zrb_get_type, g);
1921
- g->ch = zch_new(ZCH_REPLY, ZRB_DEFAULT_DEPTH);
1965
+ g->ch = zch_new(ZCH_REPLY, (uint32_t)depth);
1922
1966
  *out = g;
1923
1967
  return obj;
1924
1968
  }
@@ -2084,6 +2128,12 @@ static void *zrb_session_close_nogvl(void *p) {
2084
2128
  }
2085
2129
 
2086
2130
  static void zrb_session_shutdown(zrb_session *z, bool with_gvl_release) {
2131
+ if (z->open && z->pid != getpid()) {
2132
+ /* Opened before a fork: zenoh-c's threads are in the parent. Leave
2133
+ * the session to it; only this process's view is closed. */
2134
+ z->open = false;
2135
+ return;
2136
+ }
2087
2137
  zrb_session_detach_all(z);
2088
2138
  if (!z->open) {
2089
2139
  return;
@@ -2142,6 +2192,9 @@ static bool zrb_session_check_link(zrb_session *z) {
2142
2192
  if (!z->open) {
2143
2193
  return true;
2144
2194
  }
2195
+ if (z->pid != getpid()) {
2196
+ return true;
2197
+ }
2145
2198
  if (z_session_is_closed(z_loan(z->session))) {
2146
2199
  zrb_session_shutdown(z, true);
2147
2200
  return true;
@@ -2149,6 +2202,12 @@ static bool zrb_session_check_link(zrb_session *z) {
2149
2202
  if (z->peer && z->listening) {
2150
2203
  return false;
2151
2204
  }
2205
+ if (!z->peer) {
2206
+ /* A client: zenoh-c opens a lost router connection again by itself
2207
+ * (and declares again what the session had declared), so no router
2208
+ * now means "reconnecting", not closed. */
2209
+ return false;
2210
+ }
2152
2211
  if (zrb_session_links(z) == 0) {
2153
2212
  zrb_session_shutdown(z, true);
2154
2213
  return true;
@@ -2156,10 +2215,23 @@ static bool zrb_session_check_link(zrb_session *z) {
2156
2215
  return false;
2157
2216
  }
2158
2217
 
2218
+ /* True while a client session has no router (zenoh-c is opening the
2219
+ * connection again). */
2220
+ static bool zrb_session_reconnecting(zrb_session *z) {
2221
+ if (zrb_session_check_link(z) || z->peer) {
2222
+ return false;
2223
+ }
2224
+ return zrb_session_links(z) == 0;
2225
+ }
2226
+
2159
2227
  static zrb_session *zrb_session_get_open(VALUE self) {
2160
2228
  zrb_session *z = zrb_session_get(self);
2229
+ if (z->open && z->pid != getpid()) {
2230
+ rb_raise(eClosedError, "the session was opened in process %ld; open a new session after fork",
2231
+ (long)z->pid);
2232
+ }
2161
2233
  if (zrb_session_check_link(z)) {
2162
- rb_raise(eZenohError, "session is closed");
2234
+ rb_raise(eClosedError, "session is closed");
2163
2235
  }
2164
2236
  return z;
2165
2237
  }
@@ -2441,13 +2513,14 @@ static VALUE zrb_session_s_open(int argc, VALUE *argv, VALUE klass) {
2441
2513
 
2442
2514
  rb_thread_call_without_gvl(zrb_open_nogvl, &a, RUBY_UBF_IO, NULL);
2443
2515
  if (a.ret != Z_OK) {
2444
- rb_raise(eZenohError, "cannot open a session to %s (%d)%" PRIsVALUE,
2516
+ zrb_raise_code(eZenohError, (int)a.ret, "cannot open a session to %s (%d)%" PRIsVALUE,
2445
2517
  locator != NULL ? locator : (listen != NULL ? listen : "the configured endpoints"), (int)a.ret,
2446
2518
  zrb_last_error());
2447
2519
  }
2448
2520
  z->open = true;
2449
2521
  z->live = true;
2450
2522
  z->peer = peer;
2523
+ z->pid = getpid();
2451
2524
  /* A peer that listens (or finds the others by scouting) stays open
2452
2525
  * without peers; one that only connects closes when it has none. */
2453
2526
  z->listening = peer && (is_router || listens || scouts);
@@ -2483,7 +2556,7 @@ static VALUE zrb_sub_new(VALUE self, zrb_session *z, VALUE key_v, long depth, bo
2483
2556
  ret = z_declare_subscriber(z_loan(z->session), &s->u.sub, z_loan(ke), z_move(cb), &so);
2484
2557
  }
2485
2558
  if (ret != Z_OK) {
2486
- rb_raise(eZenohError, "cannot subscribe to %s (%d)%" PRIsVALUE, key, (int)ret, zrb_last_error());
2559
+ zrb_raise_code(eZenohError, (int)ret, "cannot subscribe to %s (%d)%" PRIsVALUE, key, (int)ret, zrb_last_error());
2487
2560
  }
2488
2561
  s->declared = true;
2489
2562
  s->owner = z;
@@ -2503,11 +2576,12 @@ static VALUE zrb_session_subscribe(int argc, VALUE *argv, VALUE self) {
2503
2576
  return zrb_sub_new(self, z, key_v, depth, false);
2504
2577
  }
2505
2578
 
2506
- /* session.liveliness_watch(key, depth = 16) -> LivelinessWatch */
2579
+ /* session.liveliness_watch(key, depth = 1024) -> LivelinessWatch. The
2580
+ * tokens alive when it is declared come in one burst. */
2507
2581
  static VALUE zrb_session_liveliness_watch(int argc, VALUE *argv, VALUE self) {
2508
2582
  VALUE key_v, depth_v = Qnil;
2509
2583
  rb_scan_args(argc, argv, "11", &key_v, &depth_v);
2510
- long depth = zrb_check_depth(depth_v);
2584
+ long depth = zrb_check_depth_or(depth_v, ZRB_DEFAULT_WATCH_DEPTH);
2511
2585
  StringValueCStr(key_v);
2512
2586
  zrb_session *z = zrb_session_get_open(self);
2513
2587
  return zrb_sub_new(self, z, key_v, depth, true);
@@ -2541,7 +2615,7 @@ static VALUE zrb_session_queryable(int argc, VALUE *argv, VALUE self) {
2541
2615
  qo.allowed_origin = Z_LOCALITY_REMOTE;
2542
2616
  z_result_t ret = z_declare_queryable(z_loan(z->session), &q->qable, z_loan(ke), z_move(cb), &qo);
2543
2617
  if (ret != Z_OK) {
2544
- rb_raise(eZenohError, "cannot declare a queryable on %s (%d)%" PRIsVALUE, key, (int)ret, zrb_last_error());
2618
+ zrb_raise_code(eZenohError, (int)ret, "cannot declare a queryable on %s (%d)%" PRIsVALUE, key, (int)ret, zrb_last_error());
2545
2619
  }
2546
2620
  q->declared = true;
2547
2621
  q->owner = z;
@@ -2626,29 +2700,31 @@ static VALUE zrb_run_get(VALUE self, zrb_session *z, zrb_get_args *a, VALUE obj,
2626
2700
  z_drop(z_move(a->key));
2627
2701
  free(a->params);
2628
2702
  if (zrb_session_check_link(z)) {
2629
- rb_raise(eZenohError, "%s failed: the connection is lost (%d)", what, (int)a->ret);
2703
+ zrb_raise_code(eClosedError, (int)a->ret, "%s failed: the connection is lost (%d)", what, (int)a->ret);
2630
2704
  }
2631
2705
  if (a->ret != Z_OK) {
2632
- rb_raise(eZenohError, "%s failed (%d)", what, (int)a->ret);
2706
+ zrb_raise_code(eZenohError, (int)a->ret, "%s failed (%d)", what, (int)a->ret);
2633
2707
  }
2634
2708
  zrb_hold_session(obj, self, key_v);
2635
2709
  return obj;
2636
2710
  }
2637
2711
 
2638
- static const char *const zrb_get_kw[] = {"attachment", "target", "consolidation", "encoding",
2639
- "priority", "congestion_control", "express", "accept_replies"};
2712
+ static const char *const zrb_get_kw[] = {"attachment", "target", "consolidation", "encoding", "priority",
2713
+ "congestion_control", "express", "accept_replies", "depth"};
2640
2714
 
2641
2715
  /* session.get(key, timeout_ms = 2000, params = nil, payload = nil,
2642
2716
  * attachment: nil, target: :all, consolidation: :none,
2643
2717
  * encoding: nil, priority: nil, congestion_control: nil,
2644
- * express: nil, accept_replies: nil) -> Get.
2718
+ * express: nil, accept_replies: nil, depth: 1024) -> Get.
2645
2719
  * Returns at once; the replies come in on zenoh's threads. key: a String
2646
- * or a KeyExpr. accept_replies: :matching_query (zenoh's default) or :any. */
2720
+ * or a KeyExpr. accept_replies: :matching_query (zenoh's default) or :any.
2721
+ * depth: the replies kept until taken (the oldest go past it; dropped). */
2647
2722
  static VALUE zrb_session_get_m(int argc, VALUE *argv, VALUE self) {
2648
2723
  VALUE key_v, timeout_v = Qnil, params_v = Qnil, payload = Qnil, opts = Qnil;
2649
2724
  rb_scan_args(argc, argv, "13:", &key_v, &timeout_v, &params_v, &payload, &opts);
2650
2725
  zrb_opts o;
2651
- zrb_opts_parse(opts, zrb_get_kw, 8, &o);
2726
+ zrb_opts_parse(opts, zrb_get_kw, 9, &o);
2727
+ long depth = zrb_check_depth_or(o.depth, ZRB_DEFAULT_GET_DEPTH);
2652
2728
  VALUE key_str = zrb_key_string(key_v);
2653
2729
  long timeout_ms = zrb_check_timeout(timeout_v);
2654
2730
  const char *params = NIL_P(params_v) ? NULL : StringValueCStr(params_v);
@@ -2664,7 +2740,7 @@ static VALUE zrb_session_get_m(int argc, VALUE *argv, VALUE self) {
2664
2740
  a.z = z;
2665
2741
  zrb_key_owned(key_v, &a.key);
2666
2742
  zrb_get *g;
2667
- VALUE obj = zrb_get_new(&g);
2743
+ VALUE obj = zrb_get_new(depth, &g);
2668
2744
  z_get_options_default(&a.opts);
2669
2745
  a.opts.timeout_ms = (uint64_t)timeout_ms;
2670
2746
  a.opts.target = target;
@@ -2714,7 +2790,7 @@ static VALUE zrb_session_liveliness(VALUE self, VALUE key_v) {
2714
2790
  VALUE obj = TypedData_Make_Struct(cToken, zrb_token, &zrb_token_type, t);
2715
2791
  z_result_t ret = z_liveliness_declare_token(z_loan(z->session), &t->token, z_loan(ke), NULL);
2716
2792
  if (ret != Z_OK) {
2717
- rb_raise(eZenohError, "cannot declare a liveliness token on %s (%d)%" PRIsVALUE, key, (int)ret, zrb_last_error());
2793
+ zrb_raise_code(eZenohError, (int)ret, "cannot declare a liveliness token on %s (%d)%" PRIsVALUE, key, (int)ret, zrb_last_error());
2718
2794
  }
2719
2795
  t->declared = true;
2720
2796
  t->owner = z;
@@ -2724,10 +2800,15 @@ static VALUE zrb_session_liveliness(VALUE self, VALUE key_v) {
2724
2800
  return obj;
2725
2801
  }
2726
2802
 
2727
- /* session.liveliness_get(key, timeout_ms = 2000) -> Get */
2803
+ static const char *const zrb_lget_kw[] = {"depth"};
2804
+
2805
+ /* session.liveliness_get(key, timeout_ms = 2000, depth: 1024) -> Get */
2728
2806
  static VALUE zrb_session_liveliness_get(int argc, VALUE *argv, VALUE self) {
2729
- VALUE key_v, timeout_v = Qnil;
2730
- rb_scan_args(argc, argv, "11", &key_v, &timeout_v);
2807
+ VALUE key_v, timeout_v = Qnil, opts = Qnil;
2808
+ rb_scan_args(argc, argv, "11:", &key_v, &timeout_v, &opts);
2809
+ zrb_opts o;
2810
+ zrb_opts_parse(opts, zrb_lget_kw, 1, &o);
2811
+ long depth = zrb_check_depth_or(o.depth, ZRB_DEFAULT_GET_DEPTH);
2731
2812
  const char *key = StringValueCStr(key_v);
2732
2813
  long timeout_ms = zrb_check_timeout(timeout_v);
2733
2814
  zrb_session *z = zrb_session_get_open(self);
@@ -2738,7 +2819,7 @@ static VALUE zrb_session_liveliness_get(int argc, VALUE *argv, VALUE self) {
2738
2819
  a.liveliness = true;
2739
2820
  zrb_owned_key(&a.key, key);
2740
2821
  zrb_get *g;
2741
- VALUE obj = zrb_get_new(&g);
2822
+ VALUE obj = zrb_get_new(depth, &g);
2742
2823
  z_liveliness_get_options_default(&a.lopts);
2743
2824
  a.lopts.timeout_ms = (uint64_t)timeout_ms;
2744
2825
  zch_closure_reply(g->ch, &a.cb);
@@ -2762,6 +2843,12 @@ static VALUE zrb_session_closed_p(VALUE self) {
2762
2843
  return zrb_session_check_link(zrb_session_get(self)) ? Qtrue : Qfalse;
2763
2844
  }
2764
2845
 
2846
+ /* session.reconnecting? -> true while a client session has no router and
2847
+ * zenoh-c opens the connection again (see the top of this file). */
2848
+ static VALUE zrb_session_reconnecting_p(VALUE self) {
2849
+ return zrb_session_reconnecting(zrb_session_get(self)) ? Qtrue : Qfalse;
2850
+ }
2851
+
2765
2852
  static VALUE zrb_session_close(VALUE self) {
2766
2853
  zrb_session_shutdown(zrb_session_get(self), true);
2767
2854
  return Qnil;
@@ -2778,8 +2865,9 @@ static VALUE zrb_session_zid(VALUE self) {
2778
2865
  return out;
2779
2866
  }
2780
2867
 
2781
- /* session.peers -> Integer: connected peers (peer mode), or routers (1
2782
- * normally) for a client session; 0 once closed. */
2868
+ /* session.connection_count -> Integer: connected peers (peer mode), or
2869
+ * routers (1 normally) for a client session; 0 once closed. (peers, its
2870
+ * old name, is defined in Ruby with a deprecation warning.) */
2783
2871
  static VALUE zrb_session_peers(VALUE self) {
2784
2872
  zrb_session *z = zrb_session_get(self);
2785
2873
  if (zrb_session_check_link(z)) {
@@ -2901,13 +2989,13 @@ static void zrb_send_run(zrb_send *s, const char *what) {
2901
2989
  z_drop(z_move(s->att));
2902
2990
  z_drop(z_move(s->enc));
2903
2991
  if (zrb_session_check_link(s->z)) {
2904
- rb_raise(eZenohError, "%s failed: the connection is lost (%d)", what, (int)s->ret);
2992
+ zrb_raise_code(eClosedError, (int)s->ret, "%s failed: the connection is lost (%d)", what, (int)s->ret);
2905
2993
  }
2906
2994
  if (s->undeclared) {
2907
2995
  rb_raise(eZenohError, "%s failed: the publisher is closed", what);
2908
2996
  }
2909
2997
  if (s->ret != Z_OK) {
2910
- rb_raise(eZenohError, "%s failed (%d)", what, (int)s->ret);
2998
+ zrb_raise_code(eZenohError, (int)s->ret, "%s failed (%d)", what, (int)s->ret);
2911
2999
  }
2912
3000
  }
2913
3001
 
@@ -3059,7 +3147,7 @@ static zrb_pub *zrb_pub_get_open(VALUE self) {
3059
3147
  rb_raise(eZenohError, "the publisher is closed");
3060
3148
  }
3061
3149
  if (zrb_session_check_link(p->ent.owner)) {
3062
- rb_raise(eZenohError, "session is closed");
3150
+ rb_raise(eClosedError, "session is closed");
3063
3151
  }
3064
3152
  return p;
3065
3153
  }
@@ -3153,7 +3241,7 @@ static VALUE zrb_session_publisher(int argc, VALUE *argv, VALUE self) {
3153
3241
  }
3154
3242
  z_result_t ret = z_declare_publisher(z_loan(z->session), &p->u.p, ke, &po);
3155
3243
  if (ret != Z_OK) {
3156
- rb_raise(eZenohError, "cannot declare a publisher on %" PRIsVALUE " (%d)%" PRIsVALUE, key_str, (int)ret, zrb_last_error());
3244
+ zrb_raise_code(eZenohError, (int)ret, "cannot declare a publisher on %" PRIsVALUE " (%d)%" PRIsVALUE, key_str, (int)ret, zrb_last_error());
3157
3245
  }
3158
3246
  return zrb_pub_wrap(self, z, p, obj, key_str);
3159
3247
  }
@@ -3259,7 +3347,7 @@ static VALUE zrb_session_advanced_publisher(int argc, VALUE *argv, VALUE self) {
3259
3347
  }
3260
3348
  z_result_t ret = ze_declare_advanced_publisher(z_loan(z->session), &p->u.a, ke, &ao);
3261
3349
  if (ret != Z_OK) {
3262
- rb_raise(eZenohError, "cannot declare an advanced publisher on %" PRIsVALUE " (%d)%" PRIsVALUE, key_str, (int)ret, zrb_last_error());
3350
+ zrb_raise_code(eZenohError, (int)ret, "cannot declare an advanced publisher on %" PRIsVALUE " (%d)%" PRIsVALUE, key_str, (int)ret, zrb_last_error());
3263
3351
  }
3264
3352
  return zrb_pub_wrap(self, z, p, obj, key_str);
3265
3353
  }
@@ -3326,7 +3414,7 @@ static VALUE zrb_pub_matching_p(VALUE self) {
3326
3414
  z_result_t r = p->advanced ? ze_advanced_publisher_get_matching_status(z_loan(p->u.a), &st)
3327
3415
  : z_publisher_get_matching_status(z_loan(p->u.p), &st);
3328
3416
  if (r != Z_OK) {
3329
- rb_raise(eZenohError, "cannot get the matching status (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3417
+ zrb_raise_code(eZenohError, (int)r, "cannot get the matching status (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3330
3418
  }
3331
3419
  return st.matching ? Qtrue : Qfalse;
3332
3420
  }
@@ -3370,7 +3458,7 @@ static zrb_querier *zrb_querier_get_open(VALUE self) {
3370
3458
  rb_raise(eZenohError, "the querier is closed");
3371
3459
  }
3372
3460
  if (zrb_session_check_link(q->ent.owner)) {
3373
- rb_raise(eZenohError, "session is closed");
3461
+ rb_raise(eClosedError, "session is closed");
3374
3462
  }
3375
3463
  return q;
3376
3464
  }
@@ -3415,7 +3503,7 @@ static VALUE zrb_session_querier(int argc, VALUE *argv, VALUE self) {
3415
3503
  VALUE obj = TypedData_Make_Struct(cQuerier, zrb_querier, &zrb_querier_type, q);
3416
3504
  z_result_t ret = z_declare_querier(z_loan(z->session), &q->q, ke, &qo);
3417
3505
  if (ret != Z_OK) {
3418
- rb_raise(eZenohError, "cannot declare a querier on %" PRIsVALUE " (%d)%" PRIsVALUE, key_str, (int)ret, zrb_last_error());
3506
+ zrb_raise_code(eZenohError, (int)ret, "cannot declare a querier on %" PRIsVALUE " (%d)%" PRIsVALUE, key_str, (int)ret, zrb_last_error());
3419
3507
  }
3420
3508
  zrb_ent_link(&q->ent, z, ZENT_ENTITY, true, zrb_querier_undeclare);
3421
3509
  zrb_hold_session(obj, self, key_str);
@@ -3449,14 +3537,16 @@ static void *zrb_qget_nogvl(void *p) {
3449
3537
  return NULL;
3450
3538
  }
3451
3539
 
3452
- static const char *const zrb_qget_kw[] = {"attachment", "encoding"};
3540
+ static const char *const zrb_qget_kw[] = {"attachment", "encoding", "depth"};
3453
3541
 
3454
- /* querier.get(params = nil, payload = nil, attachment: nil, encoding: nil) -> Get */
3542
+ /* querier.get(params = nil, payload = nil, attachment: nil, encoding: nil,
3543
+ * depth: 1024) -> Get */
3455
3544
  static VALUE zrb_querier_get_m(int argc, VALUE *argv, VALUE self) {
3456
3545
  VALUE params_v = Qnil, payload = Qnil, opts = Qnil;
3457
3546
  rb_scan_args(argc, argv, "02:", &params_v, &payload, &opts);
3458
3547
  zrb_opts o;
3459
- zrb_opts_parse(opts, zrb_qget_kw, 2, &o);
3548
+ zrb_opts_parse(opts, zrb_qget_kw, 3, &o);
3549
+ long depth = zrb_check_depth_or(o.depth, ZRB_DEFAULT_GET_DEPTH);
3460
3550
  const char *params = NIL_P(params_v) ? NULL : StringValueCStr(params_v);
3461
3551
  if (!NIL_P(payload) && !RB_TYPE_P(payload, T_STRING)) {
3462
3552
  rb_raise(rb_eTypeError, "payload must be a String");
@@ -3470,7 +3560,7 @@ static VALUE zrb_querier_get_m(int argc, VALUE *argv, VALUE self) {
3470
3560
  z_internal_bytes_null(&a.att);
3471
3561
  z_internal_encoding_null(&a.enc);
3472
3562
  zrb_get *g;
3473
- VALUE obj = zrb_get_new(&g);
3563
+ VALUE obj = zrb_get_new(depth, &g);
3474
3564
  z_querier_get_options_default(&a.opts);
3475
3565
  if (!NIL_P(payload)) {
3476
3566
  zrb_bytes_from_str(&a.payload, payload, "payload");
@@ -3493,13 +3583,13 @@ static VALUE zrb_querier_get_m(int argc, VALUE *argv, VALUE self) {
3493
3583
  z_drop(z_move(a.att));
3494
3584
  z_drop(z_move(a.enc));
3495
3585
  if (zrb_session_check_link(a.z)) {
3496
- rb_raise(eZenohError, "get failed: the connection is lost (%d)", (int)a.ret);
3586
+ zrb_raise_code(eClosedError, (int)a.ret, "get failed: the connection is lost (%d)", (int)a.ret);
3497
3587
  }
3498
3588
  if (a.undeclared) {
3499
3589
  rb_raise(eZenohError, "get failed: the querier is closed");
3500
3590
  }
3501
3591
  if (a.ret != Z_OK) {
3502
- rb_raise(eZenohError, "get failed (%d)", (int)a.ret);
3592
+ zrb_raise_code(eZenohError, (int)a.ret, "get failed (%d)", (int)a.ret);
3503
3593
  }
3504
3594
  rb_ivar_set(obj, id_iv_session, rb_ivar_get(self, id_iv_session));
3505
3595
  return obj;
@@ -3510,7 +3600,7 @@ static VALUE zrb_querier_matching_p(VALUE self) {
3510
3600
  z_matching_status_t st = {false};
3511
3601
  z_result_t r = z_querier_get_matching_status(z_loan(q->q), &st);
3512
3602
  if (r != Z_OK) {
3513
- rb_raise(eZenohError, "cannot get the matching status (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3603
+ zrb_raise_code(eZenohError, (int)r, "cannot get the matching status (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3514
3604
  }
3515
3605
  return st.matching ? Qtrue : Qfalse;
3516
3606
  }
@@ -3618,7 +3708,7 @@ static VALUE zrb_pub_matching_listener(int argc, VALUE *argv, VALUE self) {
3618
3708
  ? ze_advanced_publisher_declare_matching_listener(z_loan(p->u.a), &l->u.m, z_move(cb))
3619
3709
  : z_publisher_declare_matching_listener(z_loan(p->u.p), &l->u.m, z_move(cb));
3620
3710
  if (r != Z_OK) {
3621
- rb_raise(eZenohError, "cannot declare a matching listener (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3711
+ zrb_raise_code(eZenohError, (int)r, "cannot declare a matching listener (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3622
3712
  }
3623
3713
  zrb_lst_linked(obj, l, p->ent.owner, self);
3624
3714
  return obj;
@@ -3634,7 +3724,7 @@ static VALUE zrb_querier_matching_listener(int argc, VALUE *argv, VALUE self) {
3634
3724
  z_closure_matching_status(&cb, zev_on_matching, zevq_on_drop, zevq_ref(l->q));
3635
3725
  z_result_t r = z_querier_declare_matching_listener(z_loan(q->q), &l->u.m, z_move(cb));
3636
3726
  if (r != Z_OK) {
3637
- rb_raise(eZenohError, "cannot declare a matching listener (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3727
+ zrb_raise_code(eZenohError, (int)r, "cannot declare a matching listener (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3638
3728
  }
3639
3729
  zrb_lst_linked(obj, l, q->ent.owner, self);
3640
3730
  return obj;
@@ -3666,7 +3756,7 @@ static VALUE zrb_session_transport_events(int argc, VALUE *argv, VALUE self) {
3666
3756
  to.history = history;
3667
3757
  z_result_t r = z_declare_transport_events_listener(z_loan(z->session), &l->u.t, z_move(cb), &to);
3668
3758
  if (r != Z_OK) {
3669
- rb_raise(eZenohError, "cannot declare a transport events listener (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3759
+ zrb_raise_code(eZenohError, (int)r, "cannot declare a transport events listener (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3670
3760
  }
3671
3761
  zrb_lst_linked(obj, l, z, self);
3672
3762
  return obj;
@@ -3689,7 +3779,7 @@ static VALUE zrb_session_link_events(int argc, VALUE *argv, VALUE self) {
3689
3779
  lo.history = history;
3690
3780
  z_result_t r = z_declare_link_events_listener(z_loan(z->session), &l->u.l, z_move(cb), &lo);
3691
3781
  if (r != Z_OK) {
3692
- rb_raise(eZenohError, "cannot declare a link events listener (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3782
+ zrb_raise_code(eZenohError, (int)r, "cannot declare a link events listener (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3693
3783
  }
3694
3784
  zrb_lst_linked(obj, l, z, self);
3695
3785
  return obj;
@@ -3777,7 +3867,7 @@ static VALUE zrb_session_advanced_subscriber(int argc, VALUE *argv, VALUE self)
3777
3867
  zch_closure_sample(s->ch, &cb);
3778
3868
  z_result_t ret = ze_declare_advanced_subscriber(z_loan(z->session), &s->u.adv, ke, z_move(cb), &ao);
3779
3869
  if (ret != Z_OK) {
3780
- rb_raise(eZenohError, "cannot declare an advanced subscriber on %" PRIsVALUE " (%d)%" PRIsVALUE, key_str, (int)ret, zrb_last_error());
3870
+ zrb_raise_code(eZenohError, (int)ret, "cannot declare an advanced subscriber on %" PRIsVALUE " (%d)%" PRIsVALUE, key_str, (int)ret, zrb_last_error());
3781
3871
  }
3782
3872
  s->declared = true;
3783
3873
  s->owner = z;
@@ -3793,7 +3883,7 @@ static zrb_sub *zrb_advsub_get_open(VALUE self) {
3793
3883
  rb_raise(eZenohError, "the subscriber is closed");
3794
3884
  }
3795
3885
  if (zrb_session_check_link(s->owner)) {
3796
- rb_raise(eZenohError, "session is closed");
3886
+ rb_raise(eClosedError, "session is closed");
3797
3887
  }
3798
3888
  return s;
3799
3889
  }
@@ -3811,7 +3901,7 @@ static VALUE zrb_advsub_miss_listener(int argc, VALUE *argv, VALUE self) {
3811
3901
  ze_closure_miss(&cb, zev_on_miss, zevq_on_drop, zevq_ref(l->q));
3812
3902
  z_result_t r = ze_advanced_subscriber_declare_sample_miss_listener(z_loan(s->u.adv), &l->u.s, z_move(cb));
3813
3903
  if (r != Z_OK) {
3814
- rb_raise(eZenohError, "cannot declare a sample miss listener (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3904
+ zrb_raise_code(eZenohError, (int)r, "cannot declare a sample miss listener (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3815
3905
  }
3816
3906
  zrb_lst_linked(obj, l, s->owner, self);
3817
3907
  return obj;
@@ -3842,7 +3932,7 @@ static VALUE zrb_advsub_detect_publishers(int argc, VALUE *argv, VALUE self) {
3842
3932
  lo.history = history;
3843
3933
  z_result_t ret = ze_advanced_subscriber_detect_publishers(z_loan(as->u.adv), &s->u.sub, z_move(cb), &lo);
3844
3934
  if (ret != Z_OK) {
3845
- rb_raise(eZenohError, "cannot detect publishers (%d)%" PRIsVALUE, (int)ret, zrb_last_error());
3935
+ zrb_raise_code(eZenohError, (int)ret, "cannot detect publishers (%d)%" PRIsVALUE, (int)ret, zrb_last_error());
3846
3936
  }
3847
3937
  s->declared = true;
3848
3938
  s->owner = as->owner;
@@ -3970,7 +4060,7 @@ static VALUE zrb_session_declare_keyexpr(VALUE self, VALUE key_v) {
3970
4060
  z_internal_keyexpr_null(&k->decl);
3971
4061
  z_result_t r = z_declare_keyexpr(z_loan(z->session), &k->decl, ke);
3972
4062
  if (r != Z_OK) {
3973
- rb_raise(eZenohError, "cannot declare the key expression (%d)%" PRIsVALUE, (int)r, zrb_last_error());
4063
+ zrb_raise_code(eZenohError, (int)r, "cannot declare the key expression (%d)%" PRIsVALUE, (int)r, zrb_last_error());
3974
4064
  }
3975
4065
  zrb_ent_link(&k->ent, z, ZENT_KEYEXPR, true, zrb_ke_undeclare);
3976
4066
  rb_ivar_set(obj, id_iv_session, self);
@@ -4032,7 +4122,7 @@ static VALUE zrb_s_scout(VALUE mod, VALUE what_v, VALUE timeout_v, VALUE config_
4032
4122
  VALUE out = zevq_each_pending_noblock(q, false);
4033
4123
  zevq_unref(q);
4034
4124
  if (a.ret != Z_OK) {
4035
- rb_raise(eZenohError, "scouting failed (%d)", (int)a.ret);
4125
+ zrb_raise_code(eZenohError, (int)a.ret, "scouting failed (%d)", (int)a.ret);
4036
4126
  }
4037
4127
  return out;
4038
4128
  }
@@ -4063,11 +4153,25 @@ void Init_asterism_zenoh(void) {
4063
4153
  /* Asterism::Zenoh. No top-level Zenoh (require "asterism/zenoh/global"). */
4064
4154
  mAsterism = rb_define_module("Asterism");
4065
4155
  mZenoh = rb_define_module_under(mAsterism, "Zenoh");
4066
- eZenohError = rb_define_class_under(mZenoh, "Error", rb_eStandardError);
4156
+ /* The root of every Asterism error. Defined here because this binding
4157
+ * loads first; the asterism gem reopens it (same superclass). */
4158
+ eAsterismError = rb_define_class_under(mAsterism, "Error", rb_eStandardError);
4159
+ eZenohError = rb_define_class_under(mZenoh, "Error", eAsterismError);
4160
+ /* The session is closed, or its connection was lost. */
4161
+ eClosedError = rb_define_class_under(mZenoh, "ClosedError", eZenohError);
4067
4162
  rb_define_const(mZenoh, "C_VERSION", rb_str_freeze(rb_str_new_cstr(ZENOH_C)));
4068
4163
  rb_define_const(mZenoh, "CONNECT_TIMEOUT_MS", INT2FIX(ZRB_CONNECT_TIMEOUT_MS));
4069
4164
  rb_define_const(mZenoh, "SEND_TIMEOUT_MS", INT2FIX(ZRB_SEND_TIMEOUT_MS));
4070
4165
  rb_define_const(mZenoh, "PEER", Qtrue);
4166
+ /* A client session opens a lost router connection again (zenoh-c). */
4167
+ rb_define_const(mZenoh, "AUTO_RECONNECT", Qtrue);
4168
+ /* Queue depths: the default of subscribe, queryable and the listeners;
4169
+ * of get, liveliness_get and querier.get; of liveliness_watch; and the
4170
+ * largest accepted. */
4171
+ rb_define_const(mZenoh, "DEFAULT_DEPTH", INT2FIX(ZRB_DEFAULT_DEPTH));
4172
+ rb_define_const(mZenoh, "DEFAULT_GET_DEPTH", INT2FIX(ZRB_DEFAULT_GET_DEPTH));
4173
+ rb_define_const(mZenoh, "DEFAULT_WATCH_DEPTH", INT2FIX(ZRB_DEFAULT_WATCH_DEPTH));
4174
+ rb_define_const(mZenoh, "MAX_DEPTH", INT2FIX(ZRB_MAX_DEPTH));
4071
4175
  rb_define_const(mZenoh, "MAX_PEERS", LONG2NUM(zrb_default_max_sessions()));
4072
4176
 
4073
4177
  id_attachment = rb_intern("attachment");
@@ -4078,6 +4182,8 @@ void Init_asterism_zenoh(void) {
4078
4182
  id_listen = rb_intern("listen");
4079
4183
  id_iv_session = rb_intern("@session");
4080
4184
  id_iv_key = rb_intern("@key");
4185
+ id_iv_code = rb_intern("@code");
4186
+ id_iv_qkey = rb_intern("@asterism_queryable_key");
4081
4187
  id_iv_parent = rb_intern("@parent");
4082
4188
  id_config = rb_intern("config");
4083
4189
  id_config_file = rb_intern("config_file");
@@ -4119,9 +4225,10 @@ void Init_asterism_zenoh(void) {
4119
4225
  rb_define_method(cSession, "liveliness_watch", zrb_session_liveliness_watch, -1);
4120
4226
  rb_define_method(cSession, "liveliness_get", zrb_session_liveliness_get, -1);
4121
4227
  rb_define_method(cSession, "poll", zrb_session_poll, -1);
4122
- rb_define_method(cSession, "peers", zrb_session_peers, 0);
4228
+ rb_define_method(cSession, "connection_count", zrb_session_peers, 0);
4123
4229
  rb_define_method(cSession, "zid", zrb_session_zid, 0);
4124
4230
  rb_define_method(cSession, "closed?", zrb_session_closed_p, 0);
4231
+ rb_define_method(cSession, "reconnecting?", zrb_session_reconnecting_p, 0);
4125
4232
  rb_define_method(cSession, "close", zrb_session_close, 0);
4126
4233
 
4127
4234
  /* Subscriber and LivelinessWatch share one C structure. */