iodine 0.7.59 → 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.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/.clang-format +15 -0
  3. data/.rspec +1 -0
  4. data/.yardopts +1 -3
  5. data/CHANGELOG.md +132 -12
  6. data/Gemfile +5 -7
  7. data/LICENSE.txt +1 -1
  8. data/README.md +147 -671
  9. data/Rakefile +9 -16
  10. data/examples/chatroom.nru +9 -0
  11. data/examples/client +74 -0
  12. data/examples/config-hello.nru +9 -0
  13. data/examples/config-hello.ru +11 -0
  14. data/examples/config-hijack.ru +13 -0
  15. data/examples/config-shootout.ru +43 -0
  16. data/examples/config.nru +120 -0
  17. data/examples/config.rb +37 -0
  18. data/examples/config.ru +42 -53
  19. data/examples/fake +24 -0
  20. data/examples/hello_neorack +137 -0
  21. data/examples/routes.nru +29 -0
  22. data/examples/routes.ru +42 -0
  23. data/exe/iodine +218 -258
  24. data/ext/iodine/extconf.rb +27 -100
  25. data/ext/iodine/fio-stl.h +128336 -0
  26. data/ext/iodine/fio-stl.md +26396 -0
  27. data/ext/iodine/iodine.c +213 -1393
  28. data/ext/iodine/iodine.h +250 -55
  29. data/ext/iodine/iodine_arg_helper.h +329 -0
  30. data/ext/iodine/iodine_caller.h +234 -25
  31. data/ext/iodine/iodine_cli.h +434 -0
  32. data/ext/iodine/iodine_compression.h +312 -0
  33. data/ext/iodine/iodine_connection.h +3484 -34
  34. data/ext/iodine/iodine_core.h +363 -0
  35. data/ext/iodine/iodine_crypto.h +1441 -0
  36. data/ext/iodine/iodine_defer.h +225 -4
  37. data/ext/iodine/iodine_json.h +738 -4
  38. data/ext/iodine/iodine_listener.h +263 -0
  39. data/ext/iodine/iodine_log.h +297 -0
  40. data/ext/iodine/iodine_minimap.h +543 -0
  41. data/ext/iodine/iodine_mustache.h +461 -4
  42. data/ext/iodine/iodine_pubsub_eng.h +729 -0
  43. data/ext/iodine/iodine_pubsub_history.h +547 -0
  44. data/ext/iodine/iodine_pubsub_msg.h +228 -0
  45. data/ext/iodine/iodine_redis.h +322 -0
  46. data/ext/iodine/iodine_store.h +418 -16
  47. data/ext/iodine/iodine_threads.h +279 -0
  48. data/ext/iodine/iodine_tls.h +515 -6
  49. data/ext/iodine/iodine_utils.h +923 -0
  50. data/iodine.gemspec +37 -42
  51. data/lib/iodine/benchmark.rb +1690 -0
  52. data/lib/iodine/documentation.rb +3085 -0
  53. data/lib/iodine/version.rb +17 -1
  54. data/lib/iodine.rb +54 -255
  55. data/logo.svg +36 -0
  56. data/mustache.md +55 -0
  57. data/sig/iodine.rbs +4 -0
  58. metadata +58 -184
  59. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  60. data/.github/workflows/ruby.yml +0 -42
  61. data/.gitignore +0 -20
  62. data/LIMITS.md +0 -41
  63. data/SPEC-PubSub-Draft.md +0 -159
  64. data/SPEC-WebSocket-Draft.md +0 -239
  65. data/bin/console +0 -22
  66. data/bin/info.md +0 -353
  67. data/bin/mustache_bench.rb +0 -100
  68. data/bin/poc/Gemfile.lock +0 -23
  69. data/bin/poc/README.md +0 -37
  70. data/bin/poc/config.ru +0 -66
  71. data/bin/poc/gemfile +0 -1
  72. data/bin/poc/www/index.html +0 -57
  73. data/examples/async_task.ru +0 -92
  74. data/examples/bates/README.md +0 -3
  75. data/examples/bates/config.ru +0 -342
  76. data/examples/bates/david+bold.pdf +0 -0
  77. data/examples/bates/public/drop-pdf.png +0 -0
  78. data/examples/bates/public/index.html +0 -600
  79. data/examples/echo.ru +0 -59
  80. data/examples/etag.ru +0 -16
  81. data/examples/hello.ru +0 -29
  82. data/examples/pubsub_engine.ru +0 -81
  83. data/examples/rack3.ru +0 -12
  84. data/examples/redis.ru +0 -70
  85. data/examples/shootout.ru +0 -73
  86. data/examples/sub-protocols.ru +0 -90
  87. data/examples/tcp_client.rb +0 -66
  88. data/examples/x-sendfile.ru +0 -14
  89. data/ext/iodine/fio.c +0 -11995
  90. data/ext/iodine/fio.h +0 -6371
  91. data/ext/iodine/fio_cli.c +0 -431
  92. data/ext/iodine/fio_cli.h +0 -189
  93. data/ext/iodine/fio_json_parser.h +0 -688
  94. data/ext/iodine/fio_siphash.c +0 -157
  95. data/ext/iodine/fio_siphash.h +0 -37
  96. data/ext/iodine/fio_tls.h +0 -129
  97. data/ext/iodine/fio_tls_missing.c +0 -649
  98. data/ext/iodine/fio_tls_openssl.c +0 -1106
  99. data/ext/iodine/fio_tmpfile.h +0 -50
  100. data/ext/iodine/fiobj.h +0 -44
  101. data/ext/iodine/fiobj4fio.h +0 -21
  102. data/ext/iodine/fiobj_ary.c +0 -333
  103. data/ext/iodine/fiobj_ary.h +0 -139
  104. data/ext/iodine/fiobj_data.c +0 -1187
  105. data/ext/iodine/fiobj_data.h +0 -167
  106. data/ext/iodine/fiobj_hash.c +0 -409
  107. data/ext/iodine/fiobj_hash.h +0 -176
  108. data/ext/iodine/fiobj_json.c +0 -622
  109. data/ext/iodine/fiobj_json.h +0 -68
  110. data/ext/iodine/fiobj_mem.h +0 -71
  111. data/ext/iodine/fiobj_mustache.c +0 -317
  112. data/ext/iodine/fiobj_mustache.h +0 -62
  113. data/ext/iodine/fiobj_numbers.c +0 -344
  114. data/ext/iodine/fiobj_numbers.h +0 -127
  115. data/ext/iodine/fiobj_str.c +0 -433
  116. data/ext/iodine/fiobj_str.h +0 -172
  117. data/ext/iodine/fiobject.c +0 -620
  118. data/ext/iodine/fiobject.h +0 -655
  119. data/ext/iodine/hpack.h +0 -1923
  120. data/ext/iodine/http.c +0 -2743
  121. data/ext/iodine/http.h +0 -1002
  122. data/ext/iodine/http1.c +0 -910
  123. data/ext/iodine/http1.h +0 -29
  124. data/ext/iodine/http1_parser.h +0 -878
  125. data/ext/iodine/http_internal.c +0 -1279
  126. data/ext/iodine/http_internal.h +0 -237
  127. data/ext/iodine/http_mime_parser.h +0 -350
  128. data/ext/iodine/iodine_caller.c +0 -230
  129. data/ext/iodine/iodine_connection.c +0 -941
  130. data/ext/iodine/iodine_defer.c +0 -420
  131. data/ext/iodine/iodine_fiobj2rb.h +0 -120
  132. data/ext/iodine/iodine_helpers.c +0 -282
  133. data/ext/iodine/iodine_helpers.h +0 -12
  134. data/ext/iodine/iodine_http.c +0 -1181
  135. data/ext/iodine/iodine_http.h +0 -23
  136. data/ext/iodine/iodine_json.c +0 -302
  137. data/ext/iodine/iodine_mustache.c +0 -567
  138. data/ext/iodine/iodine_pubsub.c +0 -580
  139. data/ext/iodine/iodine_pubsub.h +0 -26
  140. data/ext/iodine/iodine_rack_io.c +0 -273
  141. data/ext/iodine/iodine_rack_io.h +0 -20
  142. data/ext/iodine/iodine_store.c +0 -146
  143. data/ext/iodine/iodine_tcp.c +0 -346
  144. data/ext/iodine/iodine_tcp.h +0 -13
  145. data/ext/iodine/iodine_tls.c +0 -261
  146. data/ext/iodine/mustache_parser.h +0 -1546
  147. data/ext/iodine/redis_engine.c +0 -957
  148. data/ext/iodine/redis_engine.h +0 -79
  149. data/ext/iodine/resp_parser.h +0 -317
  150. data/ext/iodine/websocket_parser.h +0 -506
  151. data/ext/iodine/websockets.c +0 -743
  152. data/ext/iodine/websockets.h +0 -185
  153. data/lib/iodine/connection.rb +0 -61
  154. data/lib/iodine/json.rb +0 -42
  155. data/lib/iodine/mustache.rb +0 -113
  156. data/lib/iodine/pubsub.rb +0 -55
  157. data/lib/iodine/rack_utils.rb +0 -43
  158. data/lib/iodine/tls.rb +0 -16
  159. data/lib/rack/handler/iodine.rb +0 -33
  160. 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 */