rage-iodine 5.5.0 → 5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce6be54873c53f93352aa7c739c74e73f5510eaeaab015bd2afc5e0fe261ff38
4
- data.tar.gz: 45bba38d8c14f66e780981c13a05a7ca1d036a6809f71f33833e8743e285c32b
3
+ metadata.gz: 7ffa46cab7e0fb7fc7550dd142f42b0850287fee60cc0d72b7f52757f52720f7
4
+ data.tar.gz: c51b8ad9e316c21ed06e6803dcfb5af7bf70fc3eee84a0fd179b2730d2572473
5
5
  SHA512:
6
- metadata.gz: be4a8fd787b970fb21e30d6ec21e590ca27da1122d62f7efeac532f5b2fffd920c7016d489bd5c1e748e9d559058c23c3f567edc0e99e1da34a4a81b2bdde70c
7
- data.tar.gz: 25e51f20a4a572caab13708ca5496e73cbcbb90810c13bf1000d8cbb0a1ec75bf797ade820d756a2c30237ac37c254e89303d0adeb7b6f060133a6cc1edc708e
6
+ metadata.gz: fa99ee4e7abb10b55b6f14aeeaef6bf03e85cf8aff9b33a66f7b4f64097c5ef824e0c3de6663caf00f5404b2d08b663debb60c796a0a44490138a96ac92a832c
7
+ data.tar.gz: ca2a4ac6f633955b202dccf09661cb7799ae92e16078ef3776c1648e18e2b1ab620f470f4b99b3c75b7d0e8f9b064fad180fd5c9b32ab6ae0ddd60c9d601b58d
data/CHANGELOG.md CHANGED
@@ -6,6 +6,10 @@ Please notice that this change log contains changes for upcoming releases as wel
6
6
 
7
7
  ## Changes:
8
8
 
9
+ #### Change log v.5.6.0 (2026-09-06)
10
+
11
+ **Update**: TCP Socket Listen: Accept Queue
12
+
9
13
  #### Change log v.5.5.0 (2026-07-06)
10
14
 
11
15
  **Update**: Update `pre_start` callbacks to fail the server launch in case of errors
data/Gemfile CHANGED
@@ -4,7 +4,8 @@ group :test do
4
4
  gem 'pry'
5
5
  gem 'rspec'
6
6
  gem 'rack'
7
- gem 'http'
7
+ gem 'http', '~> 5.0'
8
+ gem 'ostruct', '~> 0.6.3'
8
9
  gem 'base64'
9
10
  gem 'benchmark'
10
11
  end
data/ext/iodine/fio.c CHANGED
@@ -4532,10 +4532,22 @@ Initialize the library
4532
4532
 
4533
4533
  static void fio_pubsub_on_fork(void);
4534
4534
 
4535
+ #if defined(__linux__)
4536
+ /* accept-queue tracking lock for the listener registry (defined alongside
4537
+ * fio_accept_queue_listeners in the listener section below); declared here
4538
+ * so fio_on_fork can reset it across children. */
4539
+ static fio_lock_i fio_accept_queue_lock;
4540
+ #endif
4541
+
4535
4542
  /* Called within a child process after it starts. */
4536
4543
  static void fio_on_fork(void) {
4537
4544
  fio_timer_lock = FIO_LOCK_INIT;
4538
4545
  fio_data->lock = FIO_LOCK_INIT;
4546
+ #if defined(__linux__)
4547
+ /* a forked child may inherit the accept-queue lock while a parent thread
4548
+ * held it mid-walk; that thread won't exist in the child, so reset it. */
4549
+ fio_accept_queue_lock = FIO_LOCK_INIT;
4550
+ #endif
4539
4551
  fio_defer_on_fork();
4540
4552
  fio_malloc_after_fork();
4541
4553
  fio_poll_init();
@@ -5571,6 +5583,14 @@ The listening protocol (use the facil.io API to make a socket and attach it)
5571
5583
  typedef struct {
5572
5584
  fio_protocol_s pr;
5573
5585
  intptr_t uuid;
5586
+ #if defined(__linux__)
5587
+ /**
5588
+ * Listener node adding this listener into to fio_accept_queue_listeners,
5589
+ * so Iodine::Perf.queued_connections can find its socket's fd and read its
5590
+ * kernel length accept-queue(AcceptQ):backlog where (backlog <= somaxconn) (TCP_INFO).
5591
+ */
5592
+ fio_ls_embd_s listener_node;
5593
+ #endif
5574
5594
  void *udata;
5575
5595
  void (*on_open)(intptr_t uuid, void *udata);
5576
5596
  void (*on_start)(intptr_t uuid, void *udata);
@@ -5582,8 +5602,20 @@ typedef struct {
5582
5602
  void *tls;
5583
5603
  } fio_listen_protocol_s;
5584
5604
 
5605
+ #if defined(__linux__)
5606
+ /* tracks iodine's listening sockets for the accept-queue backlog query.
5607
+ * guarded against concurrent teardown (reactor thread, no GVL) */
5608
+ static fio_ls_embd_s fio_accept_queue_listeners = FIO_LS_INIT(fio_accept_queue_listeners);
5609
+ static fio_lock_i fio_accept_queue_lock = FIO_LOCK_INIT;
5610
+ #endif
5611
+
5585
5612
  static void fio_listen_cleanup_task(void *pr_) {
5586
5613
  fio_listen_protocol_s *pr = pr_;
5614
+ #if defined(__linux__)
5615
+ fio_lock(&fio_accept_queue_lock);
5616
+ fio_ls_embd_remove(&pr->listener_node);
5617
+ fio_unlock(&fio_accept_queue_lock);
5618
+ #endif
5587
5619
  #ifndef __MINGW32__
5588
5620
  if (pr->tls)
5589
5621
  fio_tls_destroy(pr->tls);
@@ -5733,6 +5765,12 @@ intptr_t fio_listen FIO_IGNORE_MACRO(struct fio_listen_args args) {
5733
5765
  if (port_len)
5734
5766
  memcpy(pr->port, args.port, port_len + 1);
5735
5767
 
5768
+ #if defined(__linux__)
5769
+ fio_lock(&fio_accept_queue_lock);
5770
+ fio_ls_embd_push(&fio_accept_queue_listeners, &pr->listener_node);
5771
+ fio_unlock(&fio_accept_queue_lock);
5772
+ #endif
5773
+
5736
5774
  if (fio_is_running()) {
5737
5775
  fio_attach(pr->uuid, &pr->pr);
5738
5776
  } else {
@@ -5753,6 +5791,65 @@ error:
5753
5791
  return -1;
5754
5792
  }
5755
5793
 
5794
+ /* -------------------------------------------------------------------------
5795
+ * Kernel accept-queue(AcceptQ) which is Queue length: backlog, where backlog <= somaxconn
5796
+ * "backlog are established connections waiting for accept()" = sk_ack_backlog.
5797
+ * Returns the sum of the accept-queues of all iodine listening sockets, or 0.
5798
+ * ---------------------------------------------------------------------- */
5799
+ #if defined(__linux__)
5800
+ intptr_t fio_queued_connections(void) {
5801
+ intptr_t backlog = 0;
5802
+
5803
+ fio_lock(&fio_accept_queue_lock);
5804
+ /**
5805
+ * Walk every registered listener and sum its kernel accept-queue depth/length.
5806
+ * The list always begins with a permanent head node (fio_accept_queue_listeners)
5807
+ * that works like a fixed bookmark: it holds no listener of its own, real nodes
5808
+ * are chained after it, and the last node links back to it, forming a circle
5809
+ * i.e a circular linked list also it's a doubly linked list.
5810
+ *
5811
+ * So the loop:
5812
+ * - starts at `.next`, i.e. the FIRST REAL node starting at the head
5813
+ * itself would be wrong, since it owns no listener and converting it
5814
+ * with FIO_LS_EMBD_OBJ would produce a bogus pointer;
5815
+ * - stops when it arrives back at the head, meaning every real node
5816
+ * has been visited;
5817
+ * - runs zero times on an empty list, because a lone head simply
5818
+ * points at itself.
5819
+ */
5820
+ for (fio_ls_embd_s *pos = fio_accept_queue_listeners.next; pos != &fio_accept_queue_listeners; pos = pos->next) {
5821
+ fio_listen_protocol_s *pr = FIO_LS_EMBD_OBJ(fio_listen_protocol_s, listener_node, pos);
5822
+ struct tcp_info info;
5823
+ socklen_t tlen = sizeof(info);
5824
+ int fd = fio_uuid2fd(pr->uuid);
5825
+
5826
+ fio_lock(&fd_data(fd).protocol_lock);
5827
+
5828
+ /* Skip sockets that were closed or reused before we could query them.
5829
+ * Callers must run this after listeners are attached (e.g. from within
5830
+ * Iodine.run / a background thread), so the listener's protocol slot is
5831
+ * populated and the checks below are meaningful. */
5832
+ if (!uuid_is_valid(pr->uuid) || fd_data(fd).protocol != &pr->pr) {
5833
+ fio_unlock(&fd_data(fd).protocol_lock);
5834
+ continue;
5835
+ }
5836
+
5837
+ int ok = !getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, &tlen);
5838
+
5839
+ fio_unlock(&fd_data(fd).protocol_lock);
5840
+
5841
+ if (!ok) {
5842
+ continue;
5843
+ }
5844
+
5845
+ backlog += (intptr_t)info.tcpi_unacked;
5846
+ }
5847
+ fio_unlock(&fio_accept_queue_lock);
5848
+
5849
+ return backlog;
5850
+ }
5851
+ #endif
5852
+
5756
5853
  /* *****************************************************************************
5757
5854
  Section Start Marker
5758
5855
 
data/ext/iodine/fio.h CHANGED
@@ -1111,6 +1111,19 @@ intptr_t fio_socket(const char *address, const char *port, uint8_t is_server);
1111
1111
  */
1112
1112
  intptr_t fio_accept(intptr_t srv_uuid);
1113
1113
 
1114
+ /**
1115
+ * Returns the number of established connections currently parked in the
1116
+ * kernel's accept queue(s) (sk_ack_backlog) of all iodine listening sockets.
1117
+ *
1118
+ * The query goes straight to the kernel via getsockopt(TCP_INFO).
1119
+ *
1120
+ * Returns the total accept queue length across all iodine listeners, or 0
1121
+ * when there are no listeners.
1122
+ */
1123
+ #if defined(__linux__)
1124
+ intptr_t fio_queued_connections(void);
1125
+ #endif /* linux only */
1126
+
1114
1127
  /**
1115
1128
  * Returns 1 if the uuid refers to a valid and open, socket.
1116
1129
  *
data/ext/iodine/iodine.c CHANGED
@@ -1401,6 +1401,9 @@ void Init_iodine_ext(void) {
1401
1401
  // initialize concurrency related methods
1402
1402
  iodine_defer_initialize();
1403
1403
 
1404
+ // initialize performance metrics
1405
+ iodine_perf_initialize();
1406
+
1404
1407
  // initialize the connection class
1405
1408
  iodine_connection_init();
1406
1409
 
data/ext/iodine/iodine.h CHANGED
@@ -42,6 +42,7 @@ typedef struct {
42
42
  #include "iodine_http.h"
43
43
  #include "iodine_json.h"
44
44
  #include "iodine_mustache.h"
45
+ #include "iodine_perf.h"
45
46
  #include "iodine_pubsub.h"
46
47
  #include "iodine_rack_io.h"
47
48
  #include "iodine_store.h"
@@ -415,6 +415,7 @@ static void iodine_defer_on_finish(void *ignr) {
415
415
  iodine_join_io_thread();
416
416
  }
417
417
 
418
+
418
419
  /* *****************************************************************************
419
420
  Add defer API to Iodine
420
421
  ***************************************************************************** */
@@ -0,0 +1,28 @@
1
+ #include "iodine.h"
2
+
3
+ /* *****************************************************************************
4
+ Performance metrics
5
+ ***************************************************************************** */
6
+
7
+ /**
8
+ * Iodine::Perf.queued_connections
9
+ *
10
+ * Returns the total number of established connections currently parked in
11
+ * the kernel's accept queue(s) (sk_ack_backlog) for all iodine listening
12
+ * sockets. Returns 0 when there are no listeners and nil on non-Linux systems.
13
+ */
14
+ static VALUE iodine_perf_queued_connections(VALUE self) {
15
+ (void)self;
16
+ #if defined(__linux__)
17
+ intptr_t backlog = fio_queued_connections();
18
+ return INT2NUM((long)backlog);
19
+ #else
20
+ return Qnil;
21
+ #endif
22
+ }
23
+
24
+ void iodine_perf_initialize(void) {
25
+ VALUE IodinePerfModule = rb_define_module_under(IodineModule, "Perf");
26
+ rb_define_module_function(IodinePerfModule, "queued_connections",
27
+ iodine_perf_queued_connections, 0);
28
+ }
@@ -0,0 +1,6 @@
1
+ #ifndef H_IODINE_PERF_H
2
+ #define H_IODINE_PERF_H
3
+
4
+ void iodine_perf_initialize(void);
5
+
6
+ #endif
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '5.5.0'.freeze
2
+ VERSION = '5.6.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rage-iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.0
4
+ version: 5.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
@@ -217,6 +217,8 @@ files:
217
217
  - ext/iodine/iodine_json.h
218
218
  - ext/iodine/iodine_mustache.c
219
219
  - ext/iodine/iodine_mustache.h
220
+ - ext/iodine/iodine_perf.c
221
+ - ext/iodine/iodine_perf.h
220
222
  - ext/iodine/iodine_pubsub.c
221
223
  - ext/iodine/iodine_pubsub.h
222
224
  - ext/iodine/iodine_rack_io.c