iodine 0.7.60 → 0.8.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 +4 -4
- data/.clang-format +15 -0
- data/.rspec +1 -0
- data/.yardopts +1 -3
- data/CHANGELOG.md +132 -12
- data/Gemfile +5 -7
- data/LICENSE.txt +1 -1
- data/README.md +147 -671
- data/Rakefile +9 -16
- data/examples/chatroom.nru +9 -0
- data/examples/client +74 -0
- data/examples/config-hello.nru +9 -0
- data/examples/config-hello.ru +11 -0
- data/examples/config-hijack.ru +13 -0
- data/examples/config-shootout.ru +43 -0
- data/examples/config.nru +120 -0
- data/examples/config.rb +37 -0
- data/examples/config.ru +42 -53
- data/examples/fake +24 -0
- data/examples/hello_neorack +137 -0
- data/examples/routes.nru +29 -0
- data/examples/routes.ru +42 -0
- data/exe/iodine +218 -258
- data/ext/iodine/extconf.rb +27 -100
- data/ext/iodine/fio-stl.h +128336 -0
- data/ext/iodine/fio-stl.md +26396 -0
- data/ext/iodine/iodine.c +213 -1393
- data/ext/iodine/iodine.h +250 -55
- data/ext/iodine/iodine_arg_helper.h +329 -0
- data/ext/iodine/iodine_caller.h +234 -25
- data/ext/iodine/iodine_cli.h +434 -0
- data/ext/iodine/iodine_compression.h +312 -0
- data/ext/iodine/iodine_connection.h +3484 -34
- data/ext/iodine/iodine_core.h +363 -0
- data/ext/iodine/iodine_crypto.h +1441 -0
- data/ext/iodine/iodine_defer.h +225 -4
- data/ext/iodine/iodine_json.h +738 -4
- data/ext/iodine/iodine_listener.h +263 -0
- data/ext/iodine/iodine_log.h +297 -0
- data/ext/iodine/iodine_minimap.h +543 -0
- data/ext/iodine/iodine_mustache.h +461 -4
- data/ext/iodine/iodine_pubsub_eng.h +729 -0
- data/ext/iodine/iodine_pubsub_history.h +547 -0
- data/ext/iodine/iodine_pubsub_msg.h +228 -0
- data/ext/iodine/iodine_redis.h +322 -0
- data/ext/iodine/iodine_store.h +418 -16
- data/ext/iodine/iodine_threads.h +279 -0
- data/ext/iodine/iodine_tls.h +515 -6
- data/ext/iodine/iodine_utils.h +923 -0
- data/iodine.gemspec +37 -42
- data/lib/iodine/benchmark.rb +1690 -0
- data/lib/iodine/documentation.rb +3085 -0
- data/lib/iodine/version.rb +17 -1
- data/lib/iodine.rb +54 -255
- data/logo.svg +36 -0
- data/mustache.md +55 -0
- data/sig/iodine.rbs +4 -0
- metadata +58 -184
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- data/.github/workflows/ruby.yml +0 -42
- data/.gitignore +0 -21
- data/LIMITS.md +0 -41
- data/SPEC-PubSub-Draft.md +0 -159
- data/SPEC-WebSocket-Draft.md +0 -239
- data/bin/console +0 -22
- data/bin/info.md +0 -353
- data/bin/mustache_bench.rb +0 -100
- data/bin/poc/Gemfile.lock +0 -23
- data/bin/poc/README.md +0 -37
- data/bin/poc/config.ru +0 -66
- data/bin/poc/gemfile +0 -1
- data/bin/poc/www/index.html +0 -57
- data/examples/async_task.ru +0 -92
- data/examples/bates/README.md +0 -3
- data/examples/bates/config.ru +0 -342
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +0 -600
- data/examples/echo.ru +0 -59
- data/examples/etag.ru +0 -16
- data/examples/hello.ru +0 -29
- data/examples/pubsub_engine.ru +0 -81
- data/examples/rack3.ru +0 -12
- data/examples/redis.ru +0 -70
- data/examples/shootout.ru +0 -73
- data/examples/sub-protocols.ru +0 -90
- data/examples/tcp_client.rb +0 -66
- data/examples/x-sendfile.ru +0 -14
- data/ext/iodine/fio.c +0 -11995
- data/ext/iodine/fio.h +0 -6371
- data/ext/iodine/fio_cli.c +0 -431
- data/ext/iodine/fio_cli.h +0 -189
- data/ext/iodine/fio_json_parser.h +0 -688
- data/ext/iodine/fio_siphash.c +0 -157
- data/ext/iodine/fio_siphash.h +0 -37
- data/ext/iodine/fio_tls.h +0 -129
- data/ext/iodine/fio_tls_missing.c +0 -649
- data/ext/iodine/fio_tls_openssl.c +0 -1106
- data/ext/iodine/fio_tmpfile.h +0 -50
- data/ext/iodine/fiobj.h +0 -44
- data/ext/iodine/fiobj4fio.h +0 -21
- data/ext/iodine/fiobj_ary.c +0 -333
- data/ext/iodine/fiobj_ary.h +0 -139
- data/ext/iodine/fiobj_data.c +0 -1187
- data/ext/iodine/fiobj_data.h +0 -167
- data/ext/iodine/fiobj_hash.c +0 -409
- data/ext/iodine/fiobj_hash.h +0 -176
- data/ext/iodine/fiobj_json.c +0 -622
- data/ext/iodine/fiobj_json.h +0 -68
- data/ext/iodine/fiobj_mem.h +0 -71
- data/ext/iodine/fiobj_mustache.c +0 -317
- data/ext/iodine/fiobj_mustache.h +0 -62
- data/ext/iodine/fiobj_numbers.c +0 -344
- data/ext/iodine/fiobj_numbers.h +0 -127
- data/ext/iodine/fiobj_str.c +0 -433
- data/ext/iodine/fiobj_str.h +0 -172
- data/ext/iodine/fiobject.c +0 -620
- data/ext/iodine/fiobject.h +0 -655
- data/ext/iodine/hpack.h +0 -1923
- data/ext/iodine/http.c +0 -2743
- data/ext/iodine/http.h +0 -1002
- data/ext/iodine/http1.c +0 -910
- data/ext/iodine/http1.h +0 -29
- data/ext/iodine/http1_parser.h +0 -878
- data/ext/iodine/http_internal.c +0 -1279
- data/ext/iodine/http_internal.h +0 -237
- data/ext/iodine/http_mime_parser.h +0 -350
- data/ext/iodine/iodine_caller.c +0 -230
- data/ext/iodine/iodine_connection.c +0 -941
- data/ext/iodine/iodine_defer.c +0 -420
- data/ext/iodine/iodine_fiobj2rb.h +0 -120
- data/ext/iodine/iodine_helpers.c +0 -282
- data/ext/iodine/iodine_helpers.h +0 -12
- data/ext/iodine/iodine_http.c +0 -1181
- data/ext/iodine/iodine_http.h +0 -23
- data/ext/iodine/iodine_json.c +0 -302
- data/ext/iodine/iodine_mustache.c +0 -567
- data/ext/iodine/iodine_pubsub.c +0 -580
- data/ext/iodine/iodine_pubsub.h +0 -26
- data/ext/iodine/iodine_rack_io.c +0 -273
- data/ext/iodine/iodine_rack_io.h +0 -20
- data/ext/iodine/iodine_store.c +0 -148
- data/ext/iodine/iodine_tcp.c +0 -346
- data/ext/iodine/iodine_tcp.h +0 -13
- data/ext/iodine/iodine_tls.c +0 -261
- data/ext/iodine/mustache_parser.h +0 -1546
- data/ext/iodine/redis_engine.c +0 -957
- data/ext/iodine/redis_engine.h +0 -79
- data/ext/iodine/resp_parser.h +0 -317
- data/ext/iodine/websocket_parser.h +0 -506
- data/ext/iodine/websockets.c +0 -743
- data/ext/iodine/websockets.h +0 -185
- data/lib/iodine/connection.rb +0 -61
- data/lib/iodine/json.rb +0 -42
- data/lib/iodine/mustache.rb +0 -113
- data/lib/iodine/pubsub.rb +0 -55
- data/lib/iodine/rack_utils.rb +0 -43
- data/lib/iodine/tls.rb +0 -16
- data/lib/rack/handler/iodine.rb +0 -33
- data/logo.png +0 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
#ifndef H___IODINE_THREADS___H
|
|
2
|
+
#include "iodine.h"
|
|
3
|
+
|
|
4
|
+
/* *****************************************************************************
|
|
5
|
+
Iodine Threads - Ruby-Aware Threading and Process Management
|
|
6
|
+
|
|
7
|
+
This module provides Ruby-aware implementations of threading and process
|
|
8
|
+
primitives that facil.io uses internally. These implementations ensure
|
|
9
|
+
proper interaction with Ruby's Global VM Lock (GVL) and garbage collector.
|
|
10
|
+
|
|
11
|
+
Key features:
|
|
12
|
+
- Process forking via Ruby's Process.fork (preserves Ruby state)
|
|
13
|
+
- Thread creation via Ruby's Thread.new (GVL-aware)
|
|
14
|
+
- Proper GVL release/acquisition for blocking operations
|
|
15
|
+
- Thread-safe signal handling via Ruby's Process.kill
|
|
16
|
+
|
|
17
|
+
The functions in this module replace the default POSIX implementations
|
|
18
|
+
to ensure Ruby compatibility. They're used by facil.io's internal
|
|
19
|
+
worker/thread management.
|
|
20
|
+
|
|
21
|
+
Threading Model:
|
|
22
|
+
- Threads are Ruby Thread objects (VALUE) stored as fio_thread_t
|
|
23
|
+
- Thread functions run outside the GVL for I/O operations
|
|
24
|
+
- GVL is acquired when calling Ruby code
|
|
25
|
+
|
|
26
|
+
Process Model:
|
|
27
|
+
- Workers are forked via Ruby's Process.fork
|
|
28
|
+
- Child processes inherit Ruby state properly
|
|
29
|
+
- waitpid runs with GVL for proper signal handling
|
|
30
|
+
***************************************************************************** */
|
|
31
|
+
|
|
32
|
+
/* *****************************************************************************
|
|
33
|
+
API for Forking Processes
|
|
34
|
+
***************************************************************************** */
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Forks a new process using Ruby's Process.fork.
|
|
38
|
+
*
|
|
39
|
+
* This ensures Ruby state is properly preserved in the child process.
|
|
40
|
+
* Behaves like POSIX fork(): returns 0 in child, PID in parent, -1 on error.
|
|
41
|
+
*
|
|
42
|
+
* @return Child PID in parent, 0 in child, -1 on error
|
|
43
|
+
*/
|
|
44
|
+
FIO_IFUNC fio_thread_pid_t fio_thread_fork(void) {
|
|
45
|
+
iodine_caller_result_s r =
|
|
46
|
+
iodine_ruby_call_anywhere(rb_mProcess, rb_intern2("fork", 4), 0, NULL);
|
|
47
|
+
if (r.exception)
|
|
48
|
+
return -1;
|
|
49
|
+
if (r.result == Qnil)
|
|
50
|
+
return 0;
|
|
51
|
+
return NUM2PIDT(r.result);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Returns the current process ID.
|
|
56
|
+
* Wrapper around fio_getpid() for consistency.
|
|
57
|
+
*
|
|
58
|
+
* @return Current process ID
|
|
59
|
+
*/
|
|
60
|
+
FIO_IFUNC fio_thread_pid_t fio_thread_getpid(void) {
|
|
61
|
+
return (fio_thread_pid_t)fio_getpid();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Sends a signal to a process using Ruby's Process.kill.
|
|
66
|
+
*
|
|
67
|
+
* This ensures proper signal handling within Ruby's runtime.
|
|
68
|
+
*
|
|
69
|
+
* @param i Target process ID
|
|
70
|
+
* @param s Signal number to send
|
|
71
|
+
* @return 0 on success, -1 on error
|
|
72
|
+
*/
|
|
73
|
+
FIO_IFUNC int fio_thread_kill(fio_thread_pid_t i, int s) {
|
|
74
|
+
VALUE args[] = {INT2NUM(s), PIDT2NUM(i)};
|
|
75
|
+
iodine_caller_result_s r = iodine_ruby_call_anywhere(rb_mProcess,
|
|
76
|
+
rb_intern2("kill", 4),
|
|
77
|
+
2,
|
|
78
|
+
args,
|
|
79
|
+
.ignore_exceptions = 1);
|
|
80
|
+
if (r.exception)
|
|
81
|
+
return -1;
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
typedef struct {
|
|
86
|
+
fio_thread_pid_t pid;
|
|
87
|
+
int *status;
|
|
88
|
+
int flags;
|
|
89
|
+
int ret;
|
|
90
|
+
} iodine___wait_pid_args_s;
|
|
91
|
+
|
|
92
|
+
FIO_SFUNC void *fio___thread_waitpid_in_gvl(void *args_) {
|
|
93
|
+
iodine___wait_pid_args_s *args = (iodine___wait_pid_args_s *)args_;
|
|
94
|
+
args->ret = rb_waitpid(args->pid, args->status, args->flags);
|
|
95
|
+
return NULL;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Waits for a child process using Ruby's rb_waitpid.
|
|
100
|
+
*
|
|
101
|
+
* Runs with GVL held to ensure proper Ruby signal handling.
|
|
102
|
+
*
|
|
103
|
+
* @param i Process ID to wait for (-1 for any child)
|
|
104
|
+
* @param s Pointer to store exit status
|
|
105
|
+
* @param o Wait options (WNOHANG, etc.)
|
|
106
|
+
* @return Process ID on success, -1 on error
|
|
107
|
+
*/
|
|
108
|
+
FIO_IFUNC int fio_thread_waitpid(fio_thread_pid_t i, int *s, int o) {
|
|
109
|
+
iodine___wait_pid_args_s args = {i, s, o};
|
|
110
|
+
iodine_c_call_with(fio___thread_waitpid_in_gvl, (void *)&args);
|
|
111
|
+
return args.ret;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* *****************************************************************************
|
|
115
|
+
API for Spawning Threads - Ruby Thread Integration
|
|
116
|
+
***************************************************************************** */
|
|
117
|
+
|
|
118
|
+
typedef struct {
|
|
119
|
+
fio_lock_i lock;
|
|
120
|
+
fio_thread_t *t;
|
|
121
|
+
void *(*fn)(void *);
|
|
122
|
+
void *arg;
|
|
123
|
+
} iodine___thread_starter_s;
|
|
124
|
+
|
|
125
|
+
static VALUE iodine___thread_start_in_gvl(void *args_) {
|
|
126
|
+
iodine___thread_starter_s *args = (iodine___thread_starter_s *)args_;
|
|
127
|
+
iodine___thread_starter_s cpy = *args;
|
|
128
|
+
fio_unlock(&args->lock);
|
|
129
|
+
return (VALUE)iodine_c_call_without(cpy.fn, cpy.arg);
|
|
130
|
+
}
|
|
131
|
+
static void *iodine___thread_create_in_gvl(void *args_) {
|
|
132
|
+
iodine___thread_starter_s *args = (iodine___thread_starter_s *)args_;
|
|
133
|
+
args->t[0] = rb_thread_create(iodine___thread_start_in_gvl, args_);
|
|
134
|
+
if (args->t[0] == Qnil)
|
|
135
|
+
fio_unlock(&args->lock);
|
|
136
|
+
else
|
|
137
|
+
STORE.hold(args->t[0]);
|
|
138
|
+
return NULL;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Creates a new thread using Ruby's Thread.new.
|
|
143
|
+
*
|
|
144
|
+
* The thread function runs outside the GVL for I/O operations.
|
|
145
|
+
* The thread is held in STORE to prevent GC until joined/detached.
|
|
146
|
+
*
|
|
147
|
+
* @param t Pointer to store the thread handle (Ruby VALUE)
|
|
148
|
+
* @param fn Thread function to execute
|
|
149
|
+
* @param arg Argument to pass to thread function
|
|
150
|
+
* @return 0 on success, -1 on failure
|
|
151
|
+
*/
|
|
152
|
+
FIO_IFUNC int fio_thread_create(fio_thread_t *t,
|
|
153
|
+
void *(*fn)(void *),
|
|
154
|
+
void *arg) {
|
|
155
|
+
iodine___thread_starter_s starter = {.lock = FIO_LOCK_INIT,
|
|
156
|
+
.t = t,
|
|
157
|
+
.fn = fn,
|
|
158
|
+
.arg = arg};
|
|
159
|
+
fio_lock(&starter.lock);
|
|
160
|
+
iodine_c_call_with(iodine___thread_create_in_gvl, &starter);
|
|
161
|
+
fio_lock(&starter.lock); /* wait for other thread to unlock */
|
|
162
|
+
if (*starter.t == Qnil)
|
|
163
|
+
goto error_starting_thread;
|
|
164
|
+
return 0;
|
|
165
|
+
error_starting_thread:
|
|
166
|
+
FIO_LOG_ERROR("(%d) couldn't start thread!", fio_io_pid());
|
|
167
|
+
return -1;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Waits for a thread to finish and releases it from STORE.
|
|
172
|
+
*
|
|
173
|
+
* Calls Ruby's Thread#join to wait for completion.
|
|
174
|
+
*
|
|
175
|
+
* @param t Pointer to thread handle
|
|
176
|
+
* @return 0 on success, -1 on error
|
|
177
|
+
*/
|
|
178
|
+
FIO_IFUNC int fio_thread_join(fio_thread_t *t) {
|
|
179
|
+
/* The handle pointer may belong to the joining thread's stack and become
|
|
180
|
+
* invalid as soon as join returns. Copy the VALUE while it is still valid,
|
|
181
|
+
* and keep the copied handle rooted until the Ruby call completes. */
|
|
182
|
+
fio_thread_t thread = t[0];
|
|
183
|
+
iodine_caller_result_s r =
|
|
184
|
+
iodine_ruby_call_anywhere(thread, IODINE_JOIN_ID, 0, NULL);
|
|
185
|
+
STORE.release(thread);
|
|
186
|
+
if (r.exception)
|
|
187
|
+
return -1;
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Detaches a thread, releasing it from STORE.
|
|
193
|
+
*
|
|
194
|
+
* The thread will continue running but resources are freed
|
|
195
|
+
* when it completes.
|
|
196
|
+
*
|
|
197
|
+
* @param t Pointer to thread handle
|
|
198
|
+
* @return Always returns 0
|
|
199
|
+
*/
|
|
200
|
+
FIO_IFUNC int fio_thread_detach(fio_thread_t *t) {
|
|
201
|
+
STORE.release(t[0]);
|
|
202
|
+
return 0;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Terminates the current thread via Ruby's thread lifecycle.
|
|
207
|
+
*
|
|
208
|
+
* All iodine threads are Ruby Thread objects. They must exit through Ruby's
|
|
209
|
+
* own lifecycle so that blocking-region state (th->blocking, th->unblock) is
|
|
210
|
+
* properly unwound. rb_thread_kill requires the GVL; fio_thread_exit may be
|
|
211
|
+
* called without it, so we acquire the GVL via iodine_c_call_with first.
|
|
212
|
+
*
|
|
213
|
+
* Inside the GVL, rb_thread_current() is safe and returns the correct VALUE
|
|
214
|
+
* for the calling thread. Note: this function is currently dead code —
|
|
215
|
+
* facil.io thread workers exit by returning NULL from their thread function.
|
|
216
|
+
*/
|
|
217
|
+
static void *fio___thread_exit_in_gvl(void *ignr) {
|
|
218
|
+
(void)ignr;
|
|
219
|
+
rb_thread_kill(rb_thread_current());
|
|
220
|
+
return NULL;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
FIO_IFUNC void fio_thread_exit(void) {
|
|
224
|
+
iodine_c_call_with(fio___thread_exit_in_gvl, NULL);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Compares two Ruby Thread handles for equality.
|
|
229
|
+
*
|
|
230
|
+
* fio_thread_t stores a Ruby Thread VALUE, so direct equality is sufficient.
|
|
231
|
+
*/
|
|
232
|
+
FIO_IFUNC int fio_thread_equal(fio_thread_t *a, fio_thread_t *b) {
|
|
233
|
+
return *a == *b;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Returns the current Ruby Thread VALUE as a join-compatible handle.
|
|
238
|
+
*
|
|
239
|
+
* facil.io may call this function with or without the GVL. Thread.current is
|
|
240
|
+
* therefore dispatched through iodine_ruby_call_anywhere, which either calls
|
|
241
|
+
* directly or acquires the GVL according to the current thread state.
|
|
242
|
+
* Native thread identity for allocator arena selection is provided separately
|
|
243
|
+
* by fio_thread_nid().
|
|
244
|
+
*/
|
|
245
|
+
FIO_IFUNC fio_thread_t fio_thread_current(void) {
|
|
246
|
+
iodine_caller_result_s r =
|
|
247
|
+
iodine_ruby_call_anywhere(rb_cThread, IODINE_CURRENT_ID, 0, NULL);
|
|
248
|
+
return (fio_thread_t)r.result;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Returns a process-local numeral ID for the current thread. */
|
|
252
|
+
FIO_IFUNC uintptr_t fio_thread_nid(void) {
|
|
253
|
+
#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
|
|
254
|
+
defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
|
|
255
|
+
defined(__sun) || defined(_AIX)
|
|
256
|
+
return (uintptr_t)pthread_self();
|
|
257
|
+
#elif defined(_WIN32)
|
|
258
|
+
return (uintptr_t)GetCurrentThreadId();
|
|
259
|
+
#else
|
|
260
|
+
/* errno is thread-local on POSIX systems and avoids pthread_t assumptions. */
|
|
261
|
+
return (uintptr_t)&errno;
|
|
262
|
+
#endif
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Yields execution to other threads.
|
|
267
|
+
*
|
|
268
|
+
* Called without the GVL — uses OS-level yield, NOT rb_thread_schedule()
|
|
269
|
+
* which requires the GVL.
|
|
270
|
+
*/
|
|
271
|
+
FIO_IFUNC void fio_thread_yield(void) {
|
|
272
|
+
#ifdef _WIN32
|
|
273
|
+
Sleep(0);
|
|
274
|
+
#else
|
|
275
|
+
sched_yield();
|
|
276
|
+
#endif
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
#endif /* H___IODINE_THREADS___H */
|