rage-iodine 5.2.1 → 5.4.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.4.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.4.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,7 +121,7 @@ extensions:
121
121
  extra_rdoc_files: []
122
122
  files:
123
123
  - ".github/ISSUE_TEMPLATE/bug_report.md"
124
- - ".github/workflows/ruby.yml"
124
+ - ".github/workflows/release.yml"
125
125
  - ".gitignore"
126
126
  - ".rspec"
127
127
  - ".yardopts"
@@ -131,6 +131,7 @@ files:
131
131
  - LIMITS.md
132
132
  - README.md
133
133
  - Rakefile
134
+ - SECURITY.md
134
135
  - SPEC-PubSub-Draft.md
135
136
  - SPEC-WebSocket-Draft.md
136
137
  - bin/console
@@ -226,6 +227,10 @@ files:
226
227
  - ext/iodine/iodine_tcp.h
227
228
  - ext/iodine/iodine_tls.c
228
229
  - ext/iodine/iodine_tls.h
230
+ - ext/iodine/iodine_worker_pool.c
231
+ - ext/iodine/iodine_worker_pool.h
232
+ - ext/iodine/iodine_worker_pool_test.c
233
+ - ext/iodine/iodine_worker_pool_test.h
229
234
  - ext/iodine/mustache_parser.h
230
235
  - ext/iodine/redis_engine.c
231
236
  - ext/iodine/redis_engine.h
@@ -272,7 +277,7 @@ requirements:
272
277
  - Ruby >= 2.5.0 recommended.
273
278
  - TLS requires OpenSSL >= 1.1.0.
274
279
  - Or Windows with Ruby >= 3.0.0 build with MingW and MingW as compiler.
275
- rubygems_version: 3.6.2
280
+ rubygems_version: 3.6.9
276
281
  specification_version: 4
277
282
  summary: iodine - a fast HTTP / Websocket Server with Pub/Sub support, optimized for
278
283
  Ruby MRI on Linux / BSD / Windows
@@ -1,42 +0,0 @@
1
- # This workflow uses actions that are not certified by GitHub.
2
- # They are provided by a third-party and are governed by
3
- # separate terms of service, privacy policy, and support documentation.
4
- # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
5
- # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
6
-
7
- name: Building iodine
8
-
9
- on:
10
- push:
11
- branches: [ "master" ]
12
- pull_request:
13
- branches: [ "master" ]
14
-
15
- permissions:
16
- contents: read
17
-
18
- jobs:
19
- test:
20
- strategy:
21
- fail-fast: false
22
- matrix:
23
- ruby-version: ['2.3', '2.7', '3.0', '3.1', '3.2']
24
- os: [ubuntu-latest, macos-latest] # , windows-latest
25
- runs-on: ${{ matrix.os }}
26
- steps:
27
- - uses: actions/checkout@v3
28
- - name: Set up Ruby # see https://github.com/ruby/setup-ruby#versioning)
29
- uses: ruby/setup-ruby@v1
30
- with:
31
- ruby-version: ${{ matrix.ruby-version }}
32
- bundler-cache: true # runs 'bundle install' and caches installed gems automatically
33
- - name: Build and Test Iodine
34
- run: |
35
- echo CFLAGS = $CFLAGS
36
- echo cflags = $cflags
37
- echo HOME = $HOME
38
- ruby -e 'puts Gem.default_dir'
39
- bundle exec rake install
40
- # env VERBOSE=1 bundle exec rspec --format documentation
41
- # - name: Run tests
42
- # run: bundle exec rake