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.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +28 -0
- data/CHANGELOG.md +18 -0
- data/Gemfile +2 -0
- data/README.md +17 -17
- data/SECURITY.md +32 -0
- data/ext/iodine/extconf.rb +10 -0
- data/ext/iodine/fio.c +148 -5
- data/ext/iodine/fio.h +12 -14
- data/ext/iodine/fio_json_parser.h +5 -4
- data/ext/iodine/fio_tls_openssl.c +140 -90
- data/ext/iodine/fiobj_data.c +13 -11
- data/ext/iodine/fiobj_str.h +1 -1
- data/ext/iodine/fiobject.h +5 -4
- data/ext/iodine/iodine.c +3 -0
- data/ext/iodine/iodine.h +1 -0
- data/ext/iodine/iodine_caller.c +25 -13
- data/ext/iodine/iodine_http.c +2 -1
- data/ext/iodine/iodine_store.c +24 -18
- data/ext/iodine/iodine_worker_pool.c +569 -0
- data/ext/iodine/iodine_worker_pool.h +19 -0
- data/ext/iodine/iodine_worker_pool_test.c +145 -0
- data/ext/iodine/iodine_worker_pool_test.h +19 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +11 -2
- metadata +9 -4
- data/.github/workflows/ruby.yml +0 -42
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9ef6e88ecbc5910815537ef78329c6bc965261fb4514c479be7594fb6de61dca
|
|
4
|
+
data.tar.gz: '09632895e2ed3a00f6300a418c46b7f10332403f83aceeaffe77af57258f9baf'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ce3edb67d9029b9a9bf73a941dcbcd7378214246cbbb5a09084f085dda5c2b41003dc75338a595f054e28db146cc713e887a3d43fffebefc80567d87348a94a
|
|
7
|
+
data.tar.gz: 3b2f347ecc8732f23ecac55bbc5c8c3d9d51b6e814bbb9b87f23d538f211ab6a605972b879f4449c3ce10fe95bc1920930304e604f53f5f593d5d889f4095c25
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
paths:
|
|
8
|
+
- lib/iodine/version.rb
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
id-token: write
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
release:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v6
|
|
19
|
+
with:
|
|
20
|
+
persist-credentials: false
|
|
21
|
+
|
|
22
|
+
- name: Set up Ruby
|
|
23
|
+
uses: ruby/setup-ruby@v1
|
|
24
|
+
with:
|
|
25
|
+
bundler-cache: true
|
|
26
|
+
ruby-version: 3.4
|
|
27
|
+
|
|
28
|
+
- uses: rubygems/release-gem@v1
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,18 @@ 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.4.0 (2026-06-08)
|
|
10
|
+
|
|
11
|
+
**Update**: Optimize `fio_run_every` timers
|
|
12
|
+
|
|
13
|
+
#### Change log v.5.3.0 (2026-06-03)
|
|
14
|
+
|
|
15
|
+
**Update**: Add Iodine::WorkerPool
|
|
16
|
+
|
|
17
|
+
**Update**: Allow to wake up the reactor from non-reactor threads
|
|
18
|
+
|
|
19
|
+
**Fix**: Correctly process async requests that return empty body
|
|
20
|
+
|
|
9
21
|
#### Change log v.5.2.1 (2026-03-18)
|
|
10
22
|
|
|
11
23
|
**Fix**: Fix memory leak when setting `x-accel-buffering` header
|
|
@@ -144,6 +156,12 @@ Please notice that this change log contains changes for upcoming releases as wel
|
|
|
144
156
|
|
|
145
157
|
**Update**: Allow to pause and resume fiber requests.
|
|
146
158
|
|
|
159
|
+
#### Change log v.0.7.58 (2024-04-28)
|
|
160
|
+
|
|
161
|
+
**Fix**: possible fix for compilation issues on Fedora. Credit to @garytaylor for opening issue #155.
|
|
162
|
+
|
|
163
|
+
**Fix**: possible fix for an OpenSSL certificate chain import issue that would cause certificate chains to be imported incorrectly. Credit to @dwolrdcojp for opening the facil.io repo PR #151.
|
|
164
|
+
|
|
147
165
|
#### Change log v.0.7.57 (2023-09-04)
|
|
148
166
|
|
|
149
167
|
**Fix**: Fixes possible name collision when loading gem (`.rb` vs. `.so` loading). Credit to @noraj (Alexandre ZANNI) for opening issue #148. Credit to @janbiedermann (Jan Biedermann) for discovering the root cause and offering a solution.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -10,42 +10,42 @@
|
|
|
10
10
|
|
|
11
11
|
Iodine is a fast concurrent web application server for real-time Ruby applications, with native support for WebSockets and Pub/Sub services - but it's also so much more.
|
|
12
12
|
|
|
13
|
-
Iodine is a Ruby wrapper for
|
|
13
|
+
Iodine is a Ruby wrapper for much of the [facil.io](https://facil.io) C framework, leveraging the speed of C for many common web application tasks. In addition, iodine abstracts away all network concerns, so you never need to worry about the transport layer, leaving you free to concentrate on your application logic.
|
|
14
14
|
|
|
15
15
|
Iodine includes native support for:
|
|
16
16
|
|
|
17
17
|
* HTTP, WebSockets and EventSource (SSE) Services (server);
|
|
18
18
|
* WebSocket connections (server / client);
|
|
19
19
|
* Pub/Sub (with optional Redis Pub/Sub scaling);
|
|
20
|
-
* Fast(!) builtin Mustache
|
|
20
|
+
* Fast(!) builtin Mustache templating;
|
|
21
21
|
* Static file service (with automatic `gzip` support for pre-compressed assets);
|
|
22
|
-
* Optimized Logging to `stderr
|
|
22
|
+
* Optimized Logging to `stderr`;
|
|
23
23
|
* Asynchronous event scheduling and timers;
|
|
24
24
|
* HTTP/1.1 keep-alive and pipelining;
|
|
25
|
-
* Heap Fragmentation Protection
|
|
26
|
-
* TLS 1.2 and above (
|
|
25
|
+
* Heap Fragmentation Protection;
|
|
26
|
+
* TLS 1.2 and above (Requiring OpenSSL >= 1.1.0);
|
|
27
27
|
* TCP/IP server and client connectivity;
|
|
28
28
|
* Unix Socket server and client connectivity;
|
|
29
|
-
* Hot
|
|
29
|
+
* Hot Restarts (using the USR1 signal and without hot deployment);
|
|
30
30
|
* Custom protocol authoring;
|
|
31
|
-
* [Sequel](https://github.com/jeremyevans/sequel) and ActiveRecord forking protection
|
|
31
|
+
* [Sequel](https://github.com/jeremyevans/sequel) and ActiveRecord forking protection;
|
|
32
32
|
* and more!
|
|
33
33
|
|
|
34
|
-
Since iodine wraps much of the [C facil.io framework](https://github.com/boazsegev/facil.io)
|
|
34
|
+
Since iodine wraps much of the [C facil.io framework](https://github.com/boazsegev/facil.io) for Ruby:
|
|
35
35
|
|
|
36
|
-
* Iodine can handle **thousands of concurrent connections** (tested with more
|
|
36
|
+
* Iodine can handle **thousands of concurrent connections** (tested with more than 20K connections on Linux)!
|
|
37
37
|
|
|
38
38
|
* Iodine is ideal for **Linux/Unix** based systems (i.e. macOS, Ubuntu, FreeBSD etc'), which are ideal for evented IO (while Windows and Solaris are better at IO *completion* events, which are very different).
|
|
39
39
|
|
|
40
40
|
Iodine is a C extension for Ruby, developed and optimized for Ruby MRI 2.3 and up... it should support the whole Ruby 2.x and 3.x MRI family, but CI tests start at Ruby 2.3.
|
|
41
41
|
|
|
42
|
-
**Note**: iodine does **not** support streaming when using Rack. It's recommended to avoid blocking the server when using `body.each` since the `each` loop will block
|
|
42
|
+
**Note**: iodine does **not** support streaming when using Rack. It's recommended to avoid blocking the server when using `body.each` since the `each` loop will block iodine's thread until it's finished and iodine won't send any data before the loop is done.
|
|
43
43
|
|
|
44
44
|
## Iodine - a fast & powerful HTTP + WebSockets server with native Pub/Sub
|
|
45
45
|
|
|
46
46
|
Iodine includes a light and fast HTTP and Websocket server written in C that was written according to the [Rack interface specifications](http://www.rubydoc.info/github/rack/rack/master/file/SPEC) and the [Websocket draft extension](./SPEC-Websocket-Draft.md).
|
|
47
47
|
|
|
48
|
-
With `Iodine.listen service: :http` it's possible to run multiple HTTP applications (please remember not to set more than a single application on a single TCP/IP port).
|
|
48
|
+
With `Iodine.listen service: :http` it's possible to run multiple HTTP applications (but please remember not to set more than a single application on a single TCP/IP port).
|
|
49
49
|
|
|
50
50
|
Iodine also supports native process cluster Pub/Sub and a native RedisEngine to easily scale iodine's Pub/Sub horizontally.
|
|
51
51
|
|
|
@@ -278,11 +278,11 @@ module WebsocketChat
|
|
|
278
278
|
extend self
|
|
279
279
|
end
|
|
280
280
|
APP = Proc.new do |env|
|
|
281
|
-
if env['rack.upgrade?'.freeze] == :websocket
|
|
282
|
-
env['rack.upgrade'.freeze] = WebsocketChat
|
|
281
|
+
if env['rack.upgrade?'.freeze] == :websocket
|
|
282
|
+
env['rack.upgrade'.freeze] = WebsocketChat
|
|
283
283
|
[0,{}, []] # It's possible to set cookies for the response.
|
|
284
284
|
elsif env['rack.upgrade?'.freeze] == :sse
|
|
285
|
-
puts "SSE connections can only receive data from the server, the can't write."
|
|
285
|
+
puts "SSE connections can only receive data from the server, the can't write."
|
|
286
286
|
env['rack.upgrade'.freeze] = WebsocketChat
|
|
287
287
|
[0,{}, []] # It's possible to set cookies for the response.
|
|
288
288
|
else
|
|
@@ -556,7 +556,7 @@ Iodine is written in C and allows some compile-time customizations, such as:
|
|
|
556
556
|
* `FIO_MAX_SOCK_CAPACITY` - limits iodine's maximum client capacity. Defaults to 131,072 clients.
|
|
557
557
|
|
|
558
558
|
* `FIO_USE_RISKY_HASH` - replaces SipHash with RiskyHash for iodine's internal hash maps.
|
|
559
|
-
|
|
559
|
+
|
|
560
560
|
Since iodine hash maps have internal protection against collisions and hash flooding attacks, it's possible for iodine to leverage RiskyHash, which is faster than SipHash.
|
|
561
561
|
|
|
562
562
|
By default, SipHash will be used. This is a community related choice, since the community seems to believe a hash function should protect the hash map rather than it being enough for a hash map implementation to be attack resistance.
|
|
@@ -611,7 +611,7 @@ end
|
|
|
611
611
|
|
|
612
612
|
In pure Ruby (without using C extensions or Java), it's possible to do the same by using `select`... and although `select` has some issues, it could work well for lighter loads.
|
|
613
613
|
|
|
614
|
-
The server events are fairly fast and fragmented (longer code is fragmented across multiple events), so one thread is enough to run the server including it's static file service and everything...
|
|
614
|
+
The server events are fairly fast and fragmented (longer code is fragmented across multiple events), so one thread is enough to run the server including it's static file service and everything...
|
|
615
615
|
|
|
616
616
|
...but single threaded mode should probably be avoided.
|
|
617
617
|
|
|
@@ -642,7 +642,7 @@ If you have the development headers but still can't compile the iodine extension
|
|
|
642
642
|
|
|
643
643
|
## Mr. Sandman, write me a server
|
|
644
644
|
|
|
645
|
-
Iodine allows custom TCP/IP server authoring, for those cases where we need raw TCP/IP (UDP isn't supported just yet).
|
|
645
|
+
Iodine allows custom TCP/IP server authoring, for those cases where we need raw TCP/IP (UDP isn't supported just yet).
|
|
646
646
|
|
|
647
647
|
Here's a short and sweet echo server - No HTTP, just use `telnet`:
|
|
648
648
|
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
Please report any security issues you discover on GitHub using the [`Security and quality`](https://github.com/boazsegev/iodine/security) reporting form.
|
|
4
|
+
|
|
5
|
+
Please remember that this is an open source project that I work on in my free time. Take it as is, I don't promise anything.
|
|
6
|
+
|
|
7
|
+
## Supported Versions
|
|
8
|
+
|
|
9
|
+
I support what I have time to support, with my main focus being:
|
|
10
|
+
|
|
11
|
+
| Version | Support |
|
|
12
|
+
| ------- | ------------------ |
|
|
13
|
+
| 0.8.x | :green_circle: |
|
|
14
|
+
| 0.7.x | :orange_circle: |
|
|
15
|
+
| < 0.7.0 | :red_circle: |
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### [facil.io](https://facil.io) Security Issues
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
| Version | facil.io link |
|
|
23
|
+
| ------- | ------------------ |
|
|
24
|
+
| 0.8.x | https://github.com/facil-io/cstl/security |
|
|
25
|
+
| 0.7.x | https://github.com/boazsegev/facil.io/security |
|
|
26
|
+
| < 0.7.0 | :red_circle: |
|
|
27
|
+
|
|
28
|
+
## Reporting a Vulnerability
|
|
29
|
+
|
|
30
|
+
Please report any security issues you discover on GitHub using the [`Security and quality`](https://github.com/boazsegev/iodine/security) reporting form or privately using email.
|
|
31
|
+
|
|
32
|
+
Usually I implement a security patch for the version reported before porting to the current developer version. Please note that it's usually possible to port the patch manually if you don't want to upgrade an older version.
|
data/ext/iodine/extconf.rb
CHANGED
|
@@ -107,4 +107,14 @@ EOS
|
|
|
107
107
|
end
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
+
# Feature detection for blocking operation support (Ruby 4.0+)
|
|
111
|
+
has_blocking_op_extract = have_func("rb_fiber_scheduler_blocking_operation_extract")
|
|
112
|
+
|
|
113
|
+
if has_blocking_op_extract
|
|
114
|
+
puts "detected blocking_operation APIs - enabling WorkerPool"
|
|
115
|
+
$defs << "-DHAVE_IODINE_WORKER_POOL"
|
|
116
|
+
else
|
|
117
|
+
puts "blocking_operation APIs not available (requires Ruby 4.0+) - WorkerPool disabled"
|
|
118
|
+
end
|
|
119
|
+
|
|
110
120
|
create_makefile 'iodine/iodine_ext'
|
data/ext/iodine/fio.c
CHANGED
|
@@ -1018,6 +1018,15 @@ static void init_static_throttle_key(void) {
|
|
|
1018
1018
|
}
|
|
1019
1019
|
|
|
1020
1020
|
static size_t fio_poll(void);
|
|
1021
|
+
|
|
1022
|
+
/*
|
|
1023
|
+
* Wake reactor when pushing tasks from non-reactor threads.
|
|
1024
|
+
* fio_is_reactor_thread() assumes single-threaded reactor (threads=1).
|
|
1025
|
+
* With multiple threads, fio_cycle can run on any worker, making this check
|
|
1026
|
+
* unreliable. Safe for Rage which enforces single-thread mode.
|
|
1027
|
+
*/
|
|
1028
|
+
FIO_FUNC void fio_reactor_wakeup(void);
|
|
1029
|
+
FIO_FUNC int fio_is_reactor_thread(void);
|
|
1021
1030
|
/**
|
|
1022
1031
|
* A thread entering this function should wait for new events.
|
|
1023
1032
|
*/
|
|
@@ -1200,13 +1209,19 @@ critical_error:
|
|
|
1200
1209
|
(fio_defer_task_s){.func = func_, .arg1 = arg1_, .arg2 = arg2_}, \
|
|
1201
1210
|
&task_queue_normal); \
|
|
1202
1211
|
fio_defer_thread_signal(); \
|
|
1212
|
+
if (!fio_is_reactor_thread()) \
|
|
1213
|
+
fio_reactor_wakeup(); \
|
|
1203
1214
|
} while (0)
|
|
1204
1215
|
|
|
1205
1216
|
#if FIO_USE_URGENT_QUEUE
|
|
1206
1217
|
#define fio_defer_push_urgent(func_, arg1_, arg2_) \
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1218
|
+
do { \
|
|
1219
|
+
fio_defer_push_task_fn( \
|
|
1220
|
+
(fio_defer_task_s){.func = func_, .arg1 = arg1_, .arg2 = arg2_}, \
|
|
1221
|
+
&task_queue_urgent); \
|
|
1222
|
+
if (!fio_is_reactor_thread()) \
|
|
1223
|
+
fio_reactor_wakeup(); \
|
|
1224
|
+
} while (0)
|
|
1210
1225
|
#else
|
|
1211
1226
|
#define fio_defer_push_urgent(func_, arg1_, arg2_) \
|
|
1212
1227
|
fio_defer_push_task(func_, arg1_, arg2_)
|
|
@@ -1458,6 +1473,10 @@ static fio_ls_embd_s fio_timers = FIO_LS_INIT(fio_timers);
|
|
|
1458
1473
|
|
|
1459
1474
|
static fio_lock_i fio_timer_lock = FIO_LOCK_INIT;
|
|
1460
1475
|
|
|
1476
|
+
#ifndef FIO_TIMER_TAIL_PROBE_LIMIT
|
|
1477
|
+
#define FIO_TIMER_TAIL_PROBE_LIMIT 16
|
|
1478
|
+
#endif
|
|
1479
|
+
|
|
1461
1480
|
/** Marks the current time as facil.io's cycle time */
|
|
1462
1481
|
static inline void fio_mark_time(void) {
|
|
1463
1482
|
clock_gettime(CLOCK_REALTIME, &fio_data->last_cycle);
|
|
@@ -1523,6 +1542,33 @@ static void fio_timer_add_order(fio_timer_s *timer) {
|
|
|
1523
1542
|
timer->due = fio_timer_calc_due(timer->interval);
|
|
1524
1543
|
// fio_ls_embd_s *pos = &fio_timers;
|
|
1525
1544
|
fio_lock(&fio_timer_lock);
|
|
1545
|
+
|
|
1546
|
+
if (!fio_ls_embd_any(&fio_timers)) {
|
|
1547
|
+
fio_ls_embd_push(&fio_timers, &timer->node);
|
|
1548
|
+
goto finish;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
fio_timer_s *tail = FIO_LS_EMBD_OBJ(fio_timer_s, node, fio_timers.prev);
|
|
1552
|
+
if (fio_timer_compare(timer->due, tail->due) <= 0) {
|
|
1553
|
+
fio_ls_embd_push(&fio_timers, &timer->node);
|
|
1554
|
+
goto finish;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
fio_timer_s *head = FIO_LS_EMBD_OBJ(fio_timer_s, node, fio_timers.next);
|
|
1558
|
+
if (fio_timer_compare(timer->due, head->due) >= 0) {
|
|
1559
|
+
fio_ls_embd_push(fio_timers.next, &timer->node);
|
|
1560
|
+
goto finish;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
fio_ls_embd_s *node = fio_timers.prev;
|
|
1564
|
+
for (size_t i = 0; i < FIO_TIMER_TAIL_PROBE_LIMIT && node != &fio_timers; ++i, node = node->prev) {
|
|
1565
|
+
fio_timer_s *t2 = FIO_LS_EMBD_OBJ(fio_timer_s, node, node);
|
|
1566
|
+
if (fio_timer_compare(t2->due, timer->due) >= 0) {
|
|
1567
|
+
fio_ls_embd_push(node->next, &timer->node);
|
|
1568
|
+
goto finish;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1526
1572
|
FIO_LS_EMBD_FOR(&fio_timers, node) {
|
|
1527
1573
|
fio_timer_s *t2 = FIO_LS_EMBD_OBJ(fio_timer_s, node, node);
|
|
1528
1574
|
if (fio_timer_compare(timer->due, t2->due) >= 0) {
|
|
@@ -1974,6 +2020,7 @@ Section Start Marker
|
|
|
1974
2020
|
***************************************************************************** */
|
|
1975
2021
|
#if FIO_ENGINE_EPOLL
|
|
1976
2022
|
#include <sys/epoll.h>
|
|
2023
|
+
#include <sys/eventfd.h>
|
|
1977
2024
|
|
|
1978
2025
|
/**
|
|
1979
2026
|
* Returns a C string detailing the IO engine selected during compilation.
|
|
@@ -1984,6 +2031,10 @@ char const *fio_engine(void) { return "epoll"; }
|
|
|
1984
2031
|
|
|
1985
2032
|
/* epoll tester, in and out */
|
|
1986
2033
|
static int evio_fd[3] = {-1, -1, -1};
|
|
2034
|
+
/* eventfd for cross-thread reactor wakeup */
|
|
2035
|
+
static int fio_wakeup_fd = -1;
|
|
2036
|
+
/* reactor thread ID */
|
|
2037
|
+
static pthread_t fio_reactor_thread;
|
|
1987
2038
|
|
|
1988
2039
|
static void fio_poll_close(void) {
|
|
1989
2040
|
for (int i = 0; i < 3; ++i) {
|
|
@@ -1992,6 +2043,10 @@ static void fio_poll_close(void) {
|
|
|
1992
2043
|
evio_fd[i] = -1;
|
|
1993
2044
|
}
|
|
1994
2045
|
}
|
|
2046
|
+
if (fio_wakeup_fd != -1) {
|
|
2047
|
+
close(fio_wakeup_fd);
|
|
2048
|
+
fio_wakeup_fd = -1;
|
|
2049
|
+
}
|
|
1995
2050
|
}
|
|
1996
2051
|
|
|
1997
2052
|
static void fio_poll_init(void) {
|
|
@@ -2009,6 +2064,20 @@ static void fio_poll_init(void) {
|
|
|
2009
2064
|
if (epoll_ctl(evio_fd[0], EPOLL_CTL_ADD, evio_fd[i], &chevent) == -1)
|
|
2010
2065
|
goto error;
|
|
2011
2066
|
}
|
|
2067
|
+
/* initialize eventfd for cross-thread wakeup */
|
|
2068
|
+
fio_wakeup_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
|
2069
|
+
if (fio_wakeup_fd == -1) {
|
|
2070
|
+
FIO_LOG_FATAL("couldn't create eventfd.");
|
|
2071
|
+
goto error;
|
|
2072
|
+
}
|
|
2073
|
+
{
|
|
2074
|
+
struct epoll_event ev = {.events = EPOLLIN, .data.fd = fio_wakeup_fd};
|
|
2075
|
+
if (epoll_ctl(evio_fd[1], EPOLL_CTL_ADD, fio_wakeup_fd, &ev) == -1) {
|
|
2076
|
+
FIO_LOG_FATAL("couldn't register eventfd with epoll.");
|
|
2077
|
+
goto error;
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
fio_reactor_thread = pthread_self();
|
|
2012
2081
|
return;
|
|
2013
2082
|
error:
|
|
2014
2083
|
FIO_LOG_FATAL("couldn't initialize epoll.");
|
|
@@ -2081,6 +2150,11 @@ static size_t fio_poll(void) {
|
|
|
2081
2150
|
epoll_wait(internal[j].data.fd, events, FIO_POLL_MAX_EVENTS, 0);
|
|
2082
2151
|
if (active_count > 0) {
|
|
2083
2152
|
for (int i = 0; i < active_count; i++) {
|
|
2153
|
+
if (events[i].data.fd == fio_wakeup_fd) {
|
|
2154
|
+
uint64_t val;
|
|
2155
|
+
read(fio_wakeup_fd, &val, sizeof(val));
|
|
2156
|
+
continue;
|
|
2157
|
+
}
|
|
2084
2158
|
if (events[i].events & (~(EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLRDHUP | EPOLLERR))) {
|
|
2085
2159
|
// errors are hendled as disconnections (on_close)
|
|
2086
2160
|
fio_force_close_in_poll(fd2uuid(events[i].data.fd));
|
|
@@ -2109,6 +2183,17 @@ static size_t fio_poll(void) {
|
|
|
2109
2183
|
return total;
|
|
2110
2184
|
}
|
|
2111
2185
|
|
|
2186
|
+
FIO_FUNC int fio_is_reactor_thread(void) {
|
|
2187
|
+
return pthread_equal(pthread_self(), fio_reactor_thread);
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
FIO_FUNC void fio_reactor_wakeup(void) {
|
|
2191
|
+
if (fio_wakeup_fd == -1)
|
|
2192
|
+
return;
|
|
2193
|
+
uint64_t val = 1;
|
|
2194
|
+
write(fio_wakeup_fd, &val, sizeof(val));
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2112
2197
|
#endif
|
|
2113
2198
|
/* *****************************************************************************
|
|
2114
2199
|
Section Start Marker
|
|
@@ -2152,8 +2237,24 @@ Section Start Marker
|
|
|
2152
2237
|
char const *fio_engine(void) { return "kqueue"; }
|
|
2153
2238
|
|
|
2154
2239
|
static int evio_fd = -1;
|
|
2240
|
+
/* EVFILT_USER identifier for cross-thread reactor wakeup */
|
|
2241
|
+
#define FIO_WAKEUP_IDENT 0xF10A11EUL
|
|
2242
|
+
static volatile int fio_wakeup_registered = 0;
|
|
2243
|
+
/* reactor thread ID */
|
|
2244
|
+
static pthread_t fio_reactor_thread;
|
|
2155
2245
|
|
|
2156
|
-
static void fio_poll_close(void) {
|
|
2246
|
+
static void fio_poll_close(void) {
|
|
2247
|
+
if (fio_wakeup_registered && evio_fd >= 0) {
|
|
2248
|
+
struct kevent kev;
|
|
2249
|
+
EV_SET(&kev, FIO_WAKEUP_IDENT, EVFILT_USER, EV_DELETE, 0, 0, NULL);
|
|
2250
|
+
kevent(evio_fd, &kev, 1, NULL, 0, NULL);
|
|
2251
|
+
fio_wakeup_registered = 0;
|
|
2252
|
+
}
|
|
2253
|
+
if (evio_fd >= 0) {
|
|
2254
|
+
close(evio_fd);
|
|
2255
|
+
evio_fd = -1;
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2157
2258
|
|
|
2158
2259
|
static void fio_poll_init(void) {
|
|
2159
2260
|
fio_poll_close();
|
|
@@ -2162,6 +2263,17 @@ static void fio_poll_init(void) {
|
|
|
2162
2263
|
FIO_LOG_FATAL("couldn't open kqueue.\n");
|
|
2163
2264
|
exit(errno);
|
|
2164
2265
|
}
|
|
2266
|
+
/* register EVFILT_USER for cross-thread wakeup */
|
|
2267
|
+
{
|
|
2268
|
+
struct kevent kev;
|
|
2269
|
+
EV_SET(&kev, FIO_WAKEUP_IDENT, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, NULL);
|
|
2270
|
+
if (kevent(evio_fd, &kev, 1, NULL, 0, NULL) == -1) {
|
|
2271
|
+
FIO_LOG_FATAL("couldn't register EVFILT_USER.\n");
|
|
2272
|
+
exit(errno);
|
|
2273
|
+
}
|
|
2274
|
+
fio_wakeup_registered = 1;
|
|
2275
|
+
}
|
|
2276
|
+
fio_reactor_thread = pthread_self();
|
|
2165
2277
|
}
|
|
2166
2278
|
|
|
2167
2279
|
static inline void fio_poll_add_read(intptr_t fd) {
|
|
@@ -2226,6 +2338,11 @@ static size_t fio_poll(void) {
|
|
|
2226
2338
|
|
|
2227
2339
|
if (active_count > 0) {
|
|
2228
2340
|
for (int i = 0; i < active_count; i++) {
|
|
2341
|
+
if (events[i].filter == EVFILT_USER &&
|
|
2342
|
+
events[i].ident == FIO_WAKEUP_IDENT) {
|
|
2343
|
+
/* EV_CLEAR auto-resets, nothing to drain */
|
|
2344
|
+
continue;
|
|
2345
|
+
}
|
|
2229
2346
|
// test for event(s) type
|
|
2230
2347
|
if (events[i].filter == EVFILT_WRITE) {
|
|
2231
2348
|
fio_defer_push_urgent(deferred_on_ready,
|
|
@@ -2250,6 +2367,18 @@ static size_t fio_poll(void) {
|
|
|
2250
2367
|
return active_count;
|
|
2251
2368
|
}
|
|
2252
2369
|
|
|
2370
|
+
FIO_FUNC int fio_is_reactor_thread(void) {
|
|
2371
|
+
return pthread_equal(pthread_self(), fio_reactor_thread);
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
FIO_FUNC void fio_reactor_wakeup(void) {
|
|
2375
|
+
if (!fio_wakeup_registered)
|
|
2376
|
+
return;
|
|
2377
|
+
struct kevent kev;
|
|
2378
|
+
EV_SET(&kev, FIO_WAKEUP_IDENT, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
|
|
2379
|
+
kevent(evio_fd, &kev, 1, NULL, 0, NULL);
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2253
2382
|
#endif
|
|
2254
2383
|
/* *****************************************************************************
|
|
2255
2384
|
Section Start Marker
|
|
@@ -2399,6 +2528,13 @@ finish:
|
|
|
2399
2528
|
return count;
|
|
2400
2529
|
}
|
|
2401
2530
|
|
|
2531
|
+
FIO_FUNC int fio_is_reactor_thread(void) {
|
|
2532
|
+
return 1;
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
FIO_FUNC void fio_reactor_wakeup(void) {
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2402
2538
|
#endif /* FIO_ENGINE_POLL */
|
|
2403
2539
|
|
|
2404
2540
|
/* *****************************************************************************
|
|
@@ -2581,6 +2717,13 @@ finish:
|
|
|
2581
2717
|
return count;
|
|
2582
2718
|
}
|
|
2583
2719
|
|
|
2720
|
+
FIO_FUNC int fio_is_reactor_thread(void) {
|
|
2721
|
+
return 1;
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
FIO_FUNC void fio_reactor_wakeup(void) {
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2584
2727
|
#endif /* FIO_ENGINE_WSAPOLL */
|
|
2585
2728
|
|
|
2586
2729
|
/* *****************************************************************************
|
|
@@ -3437,7 +3580,7 @@ read_error:
|
|
|
3437
3580
|
static int fio_sock_sendfile_from_fd(int fd, fio_packet_s *packet) {
|
|
3438
3581
|
ssize_t sent;
|
|
3439
3582
|
sent =
|
|
3440
|
-
|
|
3583
|
+
sendfile(fd, packet->data.fd, (off_t *)&packet->offset, packet->length);
|
|
3441
3584
|
if (sent < 0)
|
|
3442
3585
|
return -1;
|
|
3443
3586
|
packet->length -= sent;
|
data/ext/iodine/fio.h
CHANGED
|
@@ -109,7 +109,7 @@ Version and helper macros
|
|
|
109
109
|
|
|
110
110
|
#define FIO_VERSION_MAJOR 0
|
|
111
111
|
#define FIO_VERSION_MINOR 7
|
|
112
|
-
#define FIO_VERSION_PATCH
|
|
112
|
+
#define FIO_VERSION_PATCH 7
|
|
113
113
|
#define FIO_VERSION_BETA 0
|
|
114
114
|
|
|
115
115
|
/* Automatically convert version data to a string constant - ignore these two */
|
|
@@ -221,6 +221,7 @@ Version and helper macros
|
|
|
221
221
|
#include <unistd.h>
|
|
222
222
|
#ifdef __MINGW32__
|
|
223
223
|
#include <winsock2.h>
|
|
224
|
+
|
|
224
225
|
#include <winsock.h>
|
|
225
226
|
#include <ws2tcpip.h>
|
|
226
227
|
#endif
|
|
@@ -251,10 +252,10 @@ Version and helper macros
|
|
|
251
252
|
#endif
|
|
252
253
|
|
|
253
254
|
#ifdef __MINGW32__
|
|
254
|
-
#define
|
|
255
|
-
#define __S_IFLNK
|
|
256
|
-
#define
|
|
257
|
-
#define S_ISLNK(mode)
|
|
255
|
+
#define __S_IFMT 0170000
|
|
256
|
+
#define __S_IFLNK 0120000
|
|
257
|
+
#define __S_ISTYPE(mode, mask) (((mode)&__S_IFMT) == (mask))
|
|
258
|
+
#define S_ISLNK(mode) __S_ISTYPE((mode), __S_IFLNK)
|
|
258
259
|
|
|
259
260
|
#define SIGKILL 9
|
|
260
261
|
#define SIGTERM 15
|
|
@@ -4781,14 +4782,11 @@ static FIO_ARY_TYPE const FIO_NAME(s___const_invalid_object);
|
|
|
4781
4782
|
/* minimizes allocation "dead space" by alligning allocated length to 16bytes */
|
|
4782
4783
|
#undef FIO_ARY_SIZE2WORDS
|
|
4783
4784
|
#define FIO_ARY_SIZE2WORDS(size) \
|
|
4784
|
-
((sizeof(FIO_ARY_TYPE) & 1)
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
? (((size) & (~3)) + 4) \
|
|
4790
|
-
: (sizeof(FIO_ARY_TYPE) & 8) ? (((size) & (~1)) + 2) \
|
|
4791
|
-
: (size))
|
|
4785
|
+
((sizeof(FIO_ARY_TYPE) & 1) ? (((size) & (~15)) + 16) \
|
|
4786
|
+
: (sizeof(FIO_ARY_TYPE) & 2) ? (((size) & (~7)) + 8) \
|
|
4787
|
+
: (sizeof(FIO_ARY_TYPE) & 4) ? (((size) & (~3)) + 4) \
|
|
4788
|
+
: (sizeof(FIO_ARY_TYPE) & 8) ? (((size) & (~1)) + 2) \
|
|
4789
|
+
: (size))
|
|
4792
4790
|
|
|
4793
4791
|
/* *****************************************************************************
|
|
4794
4792
|
Array API
|
|
@@ -6086,7 +6084,7 @@ FIO_NAME(_insert_or_overwrite_)(FIO_NAME(s) * set, FIO_SET_HASH_TYPE hash_value,
|
|
|
6086
6084
|
pos->hash = hash_value;
|
|
6087
6085
|
pos->pos->hash = hash_value;
|
|
6088
6086
|
FIO_SET_COPY(pos->pos->obj, obj);
|
|
6089
|
-
|
|
6087
|
+
|
|
6090
6088
|
return pos->pos->obj;
|
|
6091
6089
|
}
|
|
6092
6090
|
|
|
@@ -393,7 +393,7 @@ fio_json_parse(json_parser_s *parser, const char *buffer, size_t length) {
|
|
|
393
393
|
goto error;
|
|
394
394
|
break;
|
|
395
395
|
case ']':
|
|
396
|
-
if ((parser->dict & 1))
|
|
396
|
+
if ((parser->dict & 1) || !parser->depth)
|
|
397
397
|
goto error;
|
|
398
398
|
--parser->depth;
|
|
399
399
|
++pos;
|
|
@@ -453,12 +453,12 @@ fio_json_parse(json_parser_s *parser, const char *buffer, size_t length) {
|
|
|
453
453
|
long long i = fio_atol((char **)&tmp);
|
|
454
454
|
if (tmp > limit)
|
|
455
455
|
goto stop;
|
|
456
|
-
if (!tmp || JSON_NUMERAL[*tmp]) {
|
|
456
|
+
if (!tmp || tmp == pos || JSON_NUMERAL[*tmp]) {
|
|
457
457
|
tmp = pos;
|
|
458
458
|
double f = fio_atof((char **)&tmp);
|
|
459
459
|
if (tmp > limit)
|
|
460
460
|
goto stop;
|
|
461
|
-
if (!tmp || JSON_NUMERAL[*tmp])
|
|
461
|
+
if (!tmp || tmp == pos || JSON_NUMERAL[*tmp])
|
|
462
462
|
goto error;
|
|
463
463
|
fio_json_on_float(parser, f);
|
|
464
464
|
pos = tmp;
|
|
@@ -481,8 +481,9 @@ fio_json_parse(json_parser_s *parser, const char *buffer, size_t length) {
|
|
|
481
481
|
if (pos[1] == '*') {
|
|
482
482
|
if (pos + 4 > limit)
|
|
483
483
|
goto stop;
|
|
484
|
-
uint8_t *tmp = pos +
|
|
484
|
+
uint8_t *tmp = pos + 2; /* avoid this: /*/
|
|
485
485
|
do {
|
|
486
|
+
++tmp;
|
|
486
487
|
tmp = memchr(tmp, '/', (uintptr_t)(limit - tmp));
|
|
487
488
|
} while (tmp && tmp[-1] != '*');
|
|
488
489
|
if (!tmp)
|