rage-iodine 5.2.1 → 5.3.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.
@@ -0,0 +1,145 @@
1
+ /*
2
+ Test helpers for Iodine::WorkerPool.
3
+
4
+ Provides:
5
+ - busy(duration:): Blocking operation that releases the GVL.
6
+ When called within a fiber scheduler context, this will trigger
7
+ blocking_operation_wait and use the worker pool.
8
+
9
+ Based on io-event worker_pool_test.c by Samuel Williams.
10
+ */
11
+
12
+ #include "iodine.h"
13
+
14
+ #ifdef HAVE_IODINE_WORKER_POOL
15
+
16
+ #include "iodine_worker_pool_test.h"
17
+
18
+ #include <ruby/thread.h>
19
+ #include <ruby/fiber/scheduler.h>
20
+ #include <unistd.h>
21
+ #include <sys/select.h>
22
+
23
+ static ID id_duration;
24
+
25
+ struct busy_operation_data {
26
+ int read_fd;
27
+ int write_fd;
28
+ double duration;
29
+ };
30
+
31
+ /* The actual blocking operation that can be cancelled */
32
+ static void *busy_blocking_operation(void *data) {
33
+ struct busy_operation_data *busy_data = (struct busy_operation_data *)data;
34
+
35
+ /* Use select() to wait for the pipe to become readable */
36
+ fd_set read_fds;
37
+ struct timeval timeout;
38
+
39
+ FD_ZERO(&read_fds);
40
+ FD_SET(busy_data->read_fd, &read_fds);
41
+
42
+ timeout.tv_sec = (long)busy_data->duration;
43
+ timeout.tv_usec = (int)((busy_data->duration - timeout.tv_sec) * 1000000);
44
+
45
+ /* This will block until:
46
+ 1. The pipe becomes readable (cancellation)
47
+ 2. The timeout expires
48
+ 3. An error occurs */
49
+ int result = select(busy_data->read_fd + 1, &read_fds, NULL, NULL, &timeout);
50
+
51
+ if (result > 0 && FD_ISSET(busy_data->read_fd, &read_fds)) {
52
+ /* Pipe became readable - we were cancelled */
53
+ char buffer;
54
+ read(busy_data->read_fd, &buffer, 1);
55
+ }
56
+
57
+ return NULL;
58
+ }
59
+
60
+ /* Unblock function that writes to the pipe to cancel the operation */
61
+ static void busy_unblock_function(void *data) {
62
+ struct busy_operation_data *busy_data = (struct busy_operation_data *)data;
63
+ char wake_byte = 1;
64
+ write(busy_data->write_fd, &wake_byte, 1);
65
+ }
66
+
67
+ /* Cleanup function for rb_ensure */
68
+ static VALUE busy_operation_cleanup(VALUE data_value) {
69
+ struct busy_operation_data *busy_data =
70
+ (struct busy_operation_data *)data_value;
71
+ close(busy_data->read_fd);
72
+ close(busy_data->write_fd);
73
+ return Qnil;
74
+ }
75
+
76
+ /* The main operation execution */
77
+ static VALUE busy_operation_execute(VALUE data_value) {
78
+ struct busy_operation_data *busy_data =
79
+ (struct busy_operation_data *)data_value;
80
+
81
+ rb_nogvl(busy_blocking_operation, busy_data, busy_unblock_function, busy_data,
82
+ RB_NOGVL_UBF_ASYNC_SAFE | RB_NOGVL_OFFLOAD_SAFE);
83
+
84
+ return Qnil;
85
+ }
86
+
87
+ /**
88
+ * Iodine::WorkerPool.busy(duration: 1.0) -> nil
89
+ *
90
+ * Creates a blocking operation for testing that releases the GVL.
91
+ * When called within a fiber scheduler context with a worker pool,
92
+ * this will trigger blocking_operation_wait.
93
+ *
94
+ * @param duration [Float] How long to block (default: 1.0 second)
95
+ * @return [nil]
96
+ */
97
+ static VALUE worker_pool_test_busy(int argc, VALUE *argv, VALUE self) {
98
+ double duration = 1.0;
99
+
100
+ VALUE kwargs = Qnil;
101
+ VALUE rb_duration = Qundef;
102
+
103
+ rb_scan_args(argc, argv, "0:", &kwargs);
104
+
105
+ if (!NIL_P(kwargs)) {
106
+ VALUE kwvals[1];
107
+ ID kwkeys[1] = {id_duration};
108
+ rb_get_kwargs(kwargs, kwkeys, 0, 1, kwvals);
109
+ rb_duration = kwvals[0];
110
+ }
111
+
112
+ if (rb_duration != Qundef && !NIL_P(rb_duration)) {
113
+ duration = NUM2DBL(rb_duration);
114
+ }
115
+
116
+ /* Create pipe for cancellation */
117
+ int pipe_fds[2];
118
+ if (pipe(pipe_fds) != 0) {
119
+ rb_sys_fail("pipe creation failed");
120
+ }
121
+
122
+ struct busy_operation_data busy_data = {
123
+ .read_fd = pipe_fds[0], .write_fd = pipe_fds[1], .duration = duration};
124
+
125
+ return rb_ensure(busy_operation_execute, (VALUE)&busy_data,
126
+ busy_operation_cleanup, (VALUE)&busy_data);
127
+ (void)self;
128
+ }
129
+
130
+ /* Initialize the test functions */
131
+ void iodine_worker_pool_test_init(VALUE WorkerPoolKlass) {
132
+ id_duration = rb_intern("duration");
133
+
134
+ rb_define_singleton_method(WorkerPoolKlass, "__busy", worker_pool_test_busy,
135
+ -1);
136
+ }
137
+
138
+ #else /* !HAVE_IODINE_WORKER_POOL */
139
+
140
+ void iodine_worker_pool_test_init(VALUE WorkerPoolKlass) {
141
+ /* WorkerPool not available */
142
+ (void)WorkerPoolKlass;
143
+ }
144
+
145
+ #endif /* HAVE_IODINE_WORKER_POOL */
@@ -0,0 +1,19 @@
1
+ /*
2
+ Test helpers for Iodine::WorkerPool.
3
+ Provides a busy() class method for testing GVL release and cancellation.
4
+ */
5
+
6
+ #ifndef H_IODINE_WORKER_POOL_TEST_H
7
+ #define H_IODINE_WORKER_POOL_TEST_H
8
+
9
+ #include <ruby.h>
10
+
11
+ /**
12
+ * Initializes test methods on the WorkerPool class.
13
+ * Called from iodine_worker_pool_init() when HAVE_IODINE_WORKER_POOL is defined.
14
+ *
15
+ * @param WorkerPoolKlass The Iodine::WorkerPool class
16
+ */
17
+ void iodine_worker_pool_test_init(VALUE WorkerPoolKlass);
18
+
19
+ #endif
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '5.2.1'.freeze
2
+ VERSION = '5.3.0'.freeze
3
3
  end
@@ -1,5 +1,8 @@
1
1
  require 'iodine' unless defined?(::Iodine::VERSION)
2
- require 'rage/cli'
2
+ begin
3
+ require 'rage/cli'
4
+ rescue LoadError
5
+ end
3
6
 
4
7
  module Iodine
5
8
  # Iodine's {Iodine::Rack} module provides a Rack compliant interface (connecting Iodine to Rack) for an HTTP and Websocket Server.
@@ -7,7 +10,13 @@ module Iodine
7
10
 
8
11
  # Runs a Rack app, as par the Rack handler requirements.
9
12
  def self.run(app, options = {})
10
- Rage::CLI.new([], { port: options[:Port], binding: options[:Host], environment: options[:environment] }).server
13
+ if !defined?(Rage::CLI) && ENV["RSPEC_TEST_ENV"]
14
+ Iodine.listen(service: :http, handler: app, port: options[:Port], address: options[:Host])
15
+ Iodine.start
16
+ else
17
+ Rage::CLI.new([], { port: options[:Port], binding: options[:Host], environment: options[:environment] }).server
18
+ end
19
+
11
20
  true
12
21
  end
13
22
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rage-iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.1
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-03-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rake
@@ -121,6 +121,7 @@ extensions:
121
121
  extra_rdoc_files: []
122
122
  files:
123
123
  - ".github/ISSUE_TEMPLATE/bug_report.md"
124
+ - ".github/workflows/release.yml"
124
125
  - ".github/workflows/ruby.yml"
125
126
  - ".gitignore"
126
127
  - ".rspec"
@@ -131,6 +132,7 @@ files:
131
132
  - LIMITS.md
132
133
  - README.md
133
134
  - Rakefile
135
+ - SECURITY.md
134
136
  - SPEC-PubSub-Draft.md
135
137
  - SPEC-WebSocket-Draft.md
136
138
  - bin/console
@@ -226,6 +228,10 @@ files:
226
228
  - ext/iodine/iodine_tcp.h
227
229
  - ext/iodine/iodine_tls.c
228
230
  - ext/iodine/iodine_tls.h
231
+ - ext/iodine/iodine_worker_pool.c
232
+ - ext/iodine/iodine_worker_pool.h
233
+ - ext/iodine/iodine_worker_pool_test.c
234
+ - ext/iodine/iodine_worker_pool_test.h
229
235
  - ext/iodine/mustache_parser.h
230
236
  - ext/iodine/redis_engine.c
231
237
  - ext/iodine/redis_engine.h
@@ -272,7 +278,7 @@ requirements:
272
278
  - Ruby >= 2.5.0 recommended.
273
279
  - TLS requires OpenSSL >= 1.1.0.
274
280
  - Or Windows with Ruby >= 3.0.0 build with MingW and MingW as compiler.
275
- rubygems_version: 3.6.2
281
+ rubygems_version: 3.6.9
276
282
  specification_version: 4
277
283
  summary: iodine - a fast HTTP / Websocket Server with Pub/Sub support, optimized for
278
284
  Ruby MRI on Linux / BSD / Windows