iodine 0.7.8 → 0.7.9
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +66 -4
- data/exe/iodine +9 -60
- data/ext/iodine/fio.c +7 -11
- data/ext/iodine/fio.h +32 -9
- data/ext/iodine/fio_cli.c +25 -13
- data/ext/iodine/fio_cli.h +2 -0
- data/ext/iodine/iodine.c +185 -2
- data/ext/iodine/iodine_connection.c +3 -1
- data/ext/iodine/iodine_defer.c +101 -92
- data/ext/iodine/iodine_http.c +3 -2
- data/ext/iodine/iodine_http.h +1 -1
- data/lib/iodine.rb +91 -47
- data/lib/iodine/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: abc090d98d50e754078aabdc8e46065b7e56b5d79f563866e563365c133ba088
|
4
|
+
data.tar.gz: 03e59675043f7b7f7e31aa640bff7f8ef01912cc074bc0d576461dc31f3a38ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 513c570195901322763ec71076bc9697a8d365885db8b4b10fbd072aee9f36500cecaf33b5b9cd0df198558a512fdef318b83c72c7e1d452fece7c2ca70bf22d
|
7
|
+
data.tar.gz: a6ab97b226cca87200be69507910951ba7f0036e2283fbc8b4b9d67926e4c4ddaa1b87e193aa7776e87d411b7ea371987a1e434c1017c804f17cd9eba6872a92
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,17 @@ Please notice that this change log contains changes for upcoming releases as wel
|
|
6
6
|
|
7
7
|
## Changes:
|
8
8
|
|
9
|
+
#### Change log v.0.7.9
|
10
|
+
|
11
|
+
**Fix**: fixed the background IO backup thread initialization and sleep interval. This thread isn't critical. It's only used to (slowly) flush sockets when all the actual threads are blocked by long running Ruby application code.
|
12
|
+
|
13
|
+
**Feature**: added the `Iodine.worker?` and `Iodine.master?` methods, for process identification.
|
14
|
+
|
15
|
+
**Update**: Updated the automatic ActiveRecord `fork` handling code and added automatic Sequel `fork` handling, to protect against possible database communication errors related to the risk of connection sharing across worker processes.
|
16
|
+
|
17
|
+
**Update**: Moved the command line option parsing code, to leverage facil.io's `fio_cli`... It appears more flexible than Ruby's `optparse` (where command line naming is concerned).
|
18
|
+
|
19
|
+
**Deprecation**: deprecated the global namespace DSL (`after_fork`, etc'). Use the new `Iodine.on_state(:after_fork)` method instead.
|
9
20
|
|
10
21
|
#### Change log v.0.7.8
|
11
22
|
|
data/README.md
CHANGED
@@ -17,6 +17,7 @@ Iodine is a fast concurrent web server for real-time Ruby applications, with nat
|
|
17
17
|
* Hot Restart (using the USR1 signal);
|
18
18
|
* Client connectivity (attach client sockets to make them evented);
|
19
19
|
* Custom protocol authoring;
|
20
|
+
* Optimized Logging to `stderr`.
|
20
21
|
* and more!
|
21
22
|
|
22
23
|
Iodine is an **evented** framework with a simple API that ports much of the [C facil.io framework](https://github.com/boazsegev/facil.io) to Ruby. This means that:
|
@@ -53,6 +54,12 @@ The common model of 16 threads and 4 processes can be easily adopted:
|
|
53
54
|
bundler exec iodine -p $PORT -t 16 -w 4
|
54
55
|
```
|
55
56
|
|
57
|
+
During development, it's more common to use a single process and a few threads:
|
58
|
+
|
59
|
+
```bash
|
60
|
+
bundler exec iodine -p $PORT -t 16 -w 1
|
61
|
+
```
|
62
|
+
|
56
63
|
### Static file serving support
|
57
64
|
|
58
65
|
Iodine supports an internal static file service that bypasses the Ruby layer and serves static files directly from "C-land".
|
@@ -118,7 +125,9 @@ Go to [localhost:3000/source](http://localhost:3000/source) to experience the `X
|
|
118
125
|
|
119
126
|
#### Pre-Compressed assets / files
|
120
127
|
|
121
|
-
|
128
|
+
Rails does this automatically when compiling assets - simply `gzip` your static files.
|
129
|
+
|
130
|
+
Iodine will automatically recognize and send the `gz` version if the client (browser) supports the `gzip` transfer-encoding.
|
122
131
|
|
123
132
|
For example, to offer a compressed version of `style.css`, run (in the terminal):
|
124
133
|
|
@@ -184,8 +193,7 @@ APP = Proc.new do |env|
|
|
184
193
|
end
|
185
194
|
end
|
186
195
|
# Pus/Sub can be server oriented as well as connection bound
|
187
|
-
|
188
|
-
Iodine.subscribe(:chat) {|ch, msg| puts msg if Process.pid == root_pid }
|
196
|
+
Iodine.subscribe(:chat) {|ch, msg| puts msg if Iodine.master? }
|
189
197
|
# By default, Pub/Sub performs in process cluster mode.
|
190
198
|
Iodine.workers = 4
|
191
199
|
# # in irb:
|
@@ -226,9 +234,63 @@ end
|
|
226
234
|
|
227
235
|
* The iodine Redis client will use a single Redis connection per process (for publishing data) and an extra Redis connection for subscriptions (owned by the master process). Connections will be automatically re-established if timeouts or errors occur.
|
228
236
|
|
237
|
+
#### Hot Restart
|
238
|
+
|
239
|
+
Iodine will "hot-restart" the application by shutting down and re-spawning the worker processes.
|
240
|
+
|
241
|
+
This will clear away any memory fragmentation concerns and other issues that might plague a long running worker process or ruby application.
|
242
|
+
|
243
|
+
To hot-restart iodine, send the `SIGUSR1` signal to the root process.
|
244
|
+
|
245
|
+
The following code will hot-restart iodine every 4 hours when iodine is running in cluster mode:
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
Iodine.run_every(2 * 60 * 60 * 1000) do
|
249
|
+
Process.kill("SIGUSR1", Process.pid) unless Iodine.worker?
|
250
|
+
end
|
251
|
+
```
|
252
|
+
|
253
|
+
Since the master / root process doesn't handle any requests (it only handles pub/sub and house-keeping), it's memory map and process data shouldn't be as affected and the new worker processes should be healthier and more performant.
|
254
|
+
|
255
|
+
**Note**: This will **not** re-load the application (any changes to the Ruby code require an actual restart).
|
256
|
+
|
257
|
+
#### Optimized HTTP logging
|
258
|
+
|
259
|
+
By default, iodine is pretty quite. Some messages are logged to `stderr`, but not many.
|
260
|
+
|
261
|
+
However, HTTP requests can be logged using iodine's optimized logger to `stderr`. Iodine will optimize the log output by caching the output time string which updates every second rather than every request.
|
262
|
+
|
263
|
+
This can be performed by setting the `-v` flag during startup, i.e.:
|
264
|
+
|
265
|
+
```bash
|
266
|
+
bundler exec iodine -p $PORT -t 16 -w 4 -v -www /my/public/folder
|
267
|
+
```
|
268
|
+
|
269
|
+
The log output can be redirected to a file:
|
270
|
+
|
271
|
+
```bash
|
272
|
+
bundler exec iodine -p $PORT -v 2>my_log.log
|
273
|
+
```
|
274
|
+
|
275
|
+
The log output can also be redirected to a `stdout`:
|
276
|
+
|
277
|
+
```bash
|
278
|
+
bundler exec iodine -p $PORT -v 2>&1
|
279
|
+
```
|
280
|
+
|
281
|
+
#### Built-in support for Sequel and ActiveRecord
|
282
|
+
|
283
|
+
It's a well known fact that Database connections require special attention when using `fork`-ing servers (multi-process servers) such as Puma, Passenger and iodine.
|
284
|
+
|
285
|
+
However, it's also true that these issues go unnoticed by many developers, since application developers are (rightfully) focused on the application rather than the infrastructure.
|
286
|
+
|
287
|
+
With iodine, there's no need to worry.
|
288
|
+
|
289
|
+
Iodine provides built-in `fork` handling for both ActiveRecord and Sequel, in order to protect against these possible errors.
|
290
|
+
|
229
291
|
#### TCP/IP (raw) sockets
|
230
292
|
|
231
|
-
Upgrading to a custom protocol (i.e., in order to implement your own
|
293
|
+
Upgrading to a custom protocol (i.e., in order to implement your own WebSocket protocol with special extensions) is available when neither WebSockets nor SSE connection upgrades were requested. In the following (terminal) example, we'll use an echo server without direct socket echo:
|
232
294
|
|
233
295
|
```ruby
|
234
296
|
require 'iodine'
|
data/exe/iodine
CHANGED
@@ -8,59 +8,17 @@ module Iodine
|
|
8
8
|
# Command line interface. The Ruby CLI might be changed in future versions.
|
9
9
|
module CLI
|
10
10
|
|
11
|
-
def
|
12
|
-
puts <<-EOS
|
13
|
-
|
14
|
-
Iodine's HTTP/Websocket server version #{Iodine::VERSION}
|
15
|
-
|
16
|
-
Use:
|
17
|
-
|
18
|
-
iodine <options> <filename>
|
19
|
-
|
20
|
-
Both <options> and <filename> are optional.
|
21
|
-
|
22
|
-
Available options:
|
23
|
-
-b Binding address. Default: nil (same as 0.0.0.0).
|
24
|
-
-p Port number. Default: 3000.
|
25
|
-
-t Number of threads. Default: CPU core count.
|
26
|
-
-w Number of worker processes. Default: CPU core count.
|
27
|
-
-www Public folder for static file serving. Default: nil (none).
|
28
|
-
-v Log responses. Default: never log responses.
|
29
|
-
-warmup Warmup invokes autoloading (lazy loading) during server startup.
|
30
|
-
-tout HTTP inactivity connection timeout. Default: 40 seconds.
|
31
|
-
-maxhead Maximum total headers length per HTTP request. Default: 32Kb.
|
32
|
-
-maxbd Maximum Mb per HTTP message (max body size). Default: 50Mb.
|
33
|
-
-maxms Maximum Bytes per Websocket message. Default: 250Kb.
|
34
|
-
-ping WebSocket / SSE ping interval in seconds. Default: 40 seconds.
|
35
|
-
-logging Server level logging (not HTTP), values between 0..5. Defaults to 4.
|
36
|
-
<filename> Defaults to: config.ru
|
37
|
-
|
38
|
-
Example:
|
39
|
-
|
40
|
-
iodine -p 80
|
41
|
-
|
42
|
-
iodine -p 8080 path/to/app/conf.ru
|
43
|
-
|
44
|
-
iodine -p 8080 -w 4 -t 16
|
45
|
-
|
46
|
-
EOS
|
47
|
-
end
|
48
|
-
|
49
|
-
|
50
|
-
def try_file filename
|
11
|
+
def self.try_file filename
|
51
12
|
return nil unless File.exist? filename
|
52
13
|
return ::Rack::Builder.parse_file filename
|
53
14
|
end
|
54
15
|
|
55
|
-
def
|
56
|
-
return ((ARGV[-2].to_s[0] != '-' || ARGV[-2].to_s == '-warmup' || ARGV[-2].to_s == '-v' || ARGV[-2].to_s == '-q' || (ARGV[-2].to_s[0] == '-' && ARGV[-2].to_i.to_s == ARGV[-2].to_s)) && ARGV[-1].to_s[0] != '-' && ARGV[-1])
|
57
|
-
end
|
58
|
-
|
59
|
-
def get_app_opts
|
16
|
+
def self.get_app_opts
|
60
17
|
app, opt = nil, nil
|
61
|
-
filename =
|
18
|
+
filename = Iodine::DEFAULT_HTTP_ARGS[:filename_]
|
62
19
|
if filename
|
63
|
-
app, opt = try_file filename
|
20
|
+
app, opt = try_file filename
|
21
|
+
app, opt = try_file "#{filename}.ru" unless opt
|
64
22
|
unless opt
|
65
23
|
puts "* Couldn't find #{filename}\n testing for config.ru\n"
|
66
24
|
app, opt = try_file "config.ru"
|
@@ -75,15 +33,14 @@ EOS
|
|
75
33
|
puts " Running only static file service."
|
76
34
|
opt = ::Rack::Server::Options.new.parse!([])
|
77
35
|
else
|
78
|
-
puts
|
79
|
-
puts " iodine -?"
|
36
|
+
puts cli_parser
|
80
37
|
exit(0);
|
81
38
|
end
|
82
39
|
end
|
83
40
|
return app, opt
|
84
41
|
end
|
85
42
|
|
86
|
-
def perform_warmup
|
43
|
+
def self.perform_warmup
|
87
44
|
# load anything marked with `autoload`, since autoload isn't thread safe nor fork friendly.
|
88
45
|
Iodine.run do
|
89
46
|
Module.constants.each do |n|
|
@@ -101,19 +58,11 @@ EOS
|
|
101
58
|
end
|
102
59
|
end
|
103
60
|
|
104
|
-
def call
|
105
|
-
if ARGV[0] =~ /(\-\?)|(help)|(\?)|(h)|(\-h)$/
|
106
|
-
return print_help
|
107
|
-
end
|
108
|
-
|
61
|
+
def self.call
|
109
62
|
app, opt = get_app_opts
|
110
|
-
|
111
|
-
perform_warmup if ARGV.index('-warmup')
|
112
|
-
|
63
|
+
perform_warmup if Iodine::DEFAULT_HTTP_ARGS[:warmup_]
|
113
64
|
Iodine::Rack.run(app, opt)
|
114
65
|
end
|
115
|
-
|
116
|
-
extend self
|
117
66
|
end
|
118
67
|
end
|
119
68
|
end
|
data/ext/iodine/fio.c
CHANGED
@@ -2685,13 +2685,15 @@ flushed:
|
|
2685
2685
|
}
|
2686
2686
|
|
2687
2687
|
/** `fio_flush_all` attempts flush all the open connections. */
|
2688
|
-
|
2688
|
+
size_t fio_flush_all(void) {
|
2689
2689
|
if (!fio_data)
|
2690
|
-
return;
|
2690
|
+
return 0;
|
2691
|
+
size_t count = 0;
|
2691
2692
|
for (uintptr_t i = 0; i < fio_data->max_protocol_fd; ++i) {
|
2692
|
-
if (fd_data(i).open || fd_data(i).packet)
|
2693
|
-
|
2693
|
+
if ((fd_data(i).open || fd_data(i).packet) && fio_flush(fd2uuid(i)) > 0)
|
2694
|
+
++count;
|
2694
2695
|
}
|
2696
|
+
return count;
|
2695
2697
|
}
|
2696
2698
|
|
2697
2699
|
/* *****************************************************************************
|
@@ -3283,12 +3285,6 @@ reschedule:
|
|
3283
3285
|
fio_defer(fio_review_timeout, (void *)fd, NULL);
|
3284
3286
|
}
|
3285
3287
|
|
3286
|
-
static void fio_perform_idle(void *arg, void *ignr) {
|
3287
|
-
fio_state_callback_force(FIO_CALL_ON_IDLE);
|
3288
|
-
(void)arg;
|
3289
|
-
(void)ignr;
|
3290
|
-
}
|
3291
|
-
|
3292
3288
|
/* reactor pattern cycling - common actions */
|
3293
3289
|
static void fio_cycle_schedule_events(void) {
|
3294
3290
|
static int idle = 0;
|
@@ -3309,7 +3305,7 @@ static void fio_cycle_schedule_events(void) {
|
|
3309
3305
|
} else {
|
3310
3306
|
/* events == 0 */
|
3311
3307
|
if (idle) {
|
3312
|
-
|
3308
|
+
fio_state_callback_force(FIO_CALL_ON_IDLE);
|
3313
3309
|
idle = 0;
|
3314
3310
|
}
|
3315
3311
|
}
|
data/ext/iodine/fio.h
CHANGED
@@ -109,15 +109,23 @@ Version and helper macros
|
|
109
109
|
#define FIO_VERSION_MAJOR 0
|
110
110
|
#define FIO_VERSION_MINOR 7
|
111
111
|
#define FIO_VERSION_PATCH 0
|
112
|
+
#define FIO_VERSION_BETA 2
|
112
113
|
|
113
114
|
/* Automatically convert version data to a string constant - ignore these two */
|
114
115
|
#define FIO_MACRO2STR_STEP2(macro) #macro
|
115
116
|
#define FIO_MACRO2STR(macro) FIO_MACRO2STR_STEP2(macro)
|
116
117
|
|
117
118
|
/** The facil.io version as a String literal */
|
119
|
+
#if FIO_VERSION_BETA
|
120
|
+
#define FIO_VERSION_STRING \
|
121
|
+
FIO_MACRO2STR(FIO_VERSION_MAJOR) \
|
122
|
+
"." FIO_MACRO2STR(FIO_VERSION_MINOR) "." FIO_MACRO2STR( \
|
123
|
+
FIO_VERSION_PATCH) ".beta" FIO_MACRO2STR(FIO_VERSION_BETA)
|
124
|
+
#else
|
118
125
|
#define FIO_VERSION_STRING \
|
119
126
|
FIO_MACRO2STR(FIO_VERSION_MAJOR) \
|
120
127
|
"." FIO_MACRO2STR(FIO_VERSION_MINOR) "." FIO_MACRO2STR(FIO_VERSION_PATCH)
|
128
|
+
#endif
|
121
129
|
|
122
130
|
#ifndef FIO_MAX_SOCK_CAPACITY
|
123
131
|
/**
|
@@ -1102,8 +1110,12 @@ ssize_t fio_flush(intptr_t uuid);
|
|
1102
1110
|
errno = 0; \
|
1103
1111
|
} while (fio_flush(uuid) > 0 || errno == EWOULDBLOCK)
|
1104
1112
|
|
1105
|
-
/**
|
1106
|
-
|
1113
|
+
/**
|
1114
|
+
* `fio_flush_all` attempts flush all the open connections.
|
1115
|
+
*
|
1116
|
+
* Returns the number of sockets still in need to be flushed.
|
1117
|
+
*/
|
1118
|
+
size_t fio_flush_all(void);
|
1107
1119
|
|
1108
1120
|
/**
|
1109
1121
|
* Convert between a facil.io connection's identifier (uuid) and system's fd.
|
@@ -1872,11 +1884,16 @@ typedef uint8_t volatile fio_lock_i;
|
|
1872
1884
|
/** The initail value of an unlocked spinlock. */
|
1873
1885
|
#define FIO_LOCK_INIT 0
|
1874
1886
|
|
1875
|
-
/** returns 0 if the lock was acquired and -
|
1887
|
+
/** returns 0 if the lock was acquired and a non-zero value on failure. */
|
1876
1888
|
FIO_FUNC inline int fio_trylock(fio_lock_i *lock);
|
1877
1889
|
|
1878
|
-
/**
|
1879
|
-
|
1890
|
+
/**
|
1891
|
+
* Releases a spinlock. Releasing an unacquired lock will break it.
|
1892
|
+
*
|
1893
|
+
* Returns a non-zero value on success, or 0 if the lock was in an unloacked
|
1894
|
+
* state.
|
1895
|
+
*/
|
1896
|
+
FIO_FUNC inline int fio_unlock(fio_lock_i *lock);
|
1880
1897
|
|
1881
1898
|
/** Returns a spinlock's state (non 0 == Busy). */
|
1882
1899
|
FIO_FUNC inline int fio_is_locked(fio_lock_i *lock);
|
@@ -2611,7 +2628,7 @@ FIO_FUNC inline void fio_throttle_thread(size_t nano_sec) {
|
|
2611
2628
|
nanosleep(&tm, NULL);
|
2612
2629
|
}
|
2613
2630
|
|
2614
|
-
/** returns 0 if the lock was acquired and
|
2631
|
+
/** returns 0 if the lock was acquired and another value on failure. */
|
2615
2632
|
FIO_FUNC inline int fio_trylock(fio_lock_i *lock) {
|
2616
2633
|
__asm__ volatile("" ::: "memory");
|
2617
2634
|
fio_lock_i ret = fio_atomic_xchange(lock, 1);
|
@@ -2619,10 +2636,16 @@ FIO_FUNC inline int fio_trylock(fio_lock_i *lock) {
|
|
2619
2636
|
return ret;
|
2620
2637
|
}
|
2621
2638
|
|
2622
|
-
/**
|
2623
|
-
|
2639
|
+
/**
|
2640
|
+
* Releases a spinlock. Releasing an unacquired lock will break it.
|
2641
|
+
*
|
2642
|
+
* Returns a non-zero value on success, or 0 if the lock was in an unloacked
|
2643
|
+
* state.
|
2644
|
+
*/
|
2645
|
+
FIO_FUNC inline int fio_unlock(fio_lock_i *lock) {
|
2624
2646
|
__asm__ volatile("" ::: "memory");
|
2625
|
-
fio_atomic_xchange(lock, 0);
|
2647
|
+
fio_lock_i ret = fio_atomic_xchange(lock, 0);
|
2648
|
+
return ret;
|
2626
2649
|
}
|
2627
2650
|
|
2628
2651
|
/** Returns a spinlock's state (non 0 == Busy). */
|
data/ext/iodine/fio_cli.c
CHANGED
@@ -92,6 +92,7 @@ char const *fio_cli_get_line_type(fio_cli_parser_data_s *parser,
|
|
92
92
|
case /* FIO_CLI_TYPE_STRING */ 0x1: /* fallthrough */
|
93
93
|
case /* FIO_CLI_TYPE_BOOL */ 0x2: /* fallthrough */
|
94
94
|
case /* FIO_CLI_TYPE_INT */ 0x3: /* fallthrough */
|
95
|
+
case /* FIO_CLI_TYPE_PRINT */ 0x4: /* fallthrough */
|
95
96
|
++pos;
|
96
97
|
continue;
|
97
98
|
}
|
@@ -106,6 +107,7 @@ found:
|
|
106
107
|
case /* FIO_CLI_TYPE_STRING */ 0x1: /* fallthrough */
|
107
108
|
case /* FIO_CLI_TYPE_BOOL */ 0x2: /* fallthrough */
|
108
109
|
case /* FIO_CLI_TYPE_INT */ 0x3: /* fallthrough */
|
110
|
+
case /* FIO_CLI_TYPE_PRINT */ 0x4: /* fallthrough */
|
109
111
|
return pos[1];
|
110
112
|
}
|
111
113
|
return NULL;
|
@@ -206,11 +208,17 @@ print_help:
|
|
206
208
|
case /* FIO_CLI_TYPE_STRING */ 0x1: /* fallthrough */
|
207
209
|
case /* FIO_CLI_TYPE_BOOL */ 0x2: /* fallthrough */
|
208
210
|
case /* FIO_CLI_TYPE_INT */ 0x3: /* fallthrough */
|
211
|
+
case /* FIO_CLI_TYPE_PRINT */ 0x4: /* fallthrough */
|
209
212
|
++pos;
|
210
213
|
continue;
|
211
214
|
}
|
212
215
|
type = FIO_CLI_TYPE_STRING;
|
213
216
|
switch ((intptr_t)pos[1]) {
|
217
|
+
case /* FIO_CLI_TYPE_PRINT */ 0x4:
|
218
|
+
fprintf(stderr, "%s\n", pos[0]);
|
219
|
+
pos += 2;
|
220
|
+
continue;
|
221
|
+
|
214
222
|
case /* FIO_CLI_TYPE_STRING */ 0x1: /* fallthrough */
|
215
223
|
case /* FIO_CLI_TYPE_BOOL */ 0x2: /* fallthrough */
|
216
224
|
case /* FIO_CLI_TYPE_INT */ 0x3: /* fallthrough */
|
@@ -234,16 +242,15 @@ print_help:
|
|
234
242
|
}
|
235
243
|
switch ((size_t)type) {
|
236
244
|
case /* FIO_CLI_TYPE_STRING */ 0x1:
|
237
|
-
fprintf(stderr, "\
|
238
|
-
|
245
|
+
fprintf(stderr, " \x1B[1m%.*s\x1B[0m\x1B[2m <>\x1B[0m\t%s\n", first_len,
|
246
|
+
p, p + tmp);
|
239
247
|
break;
|
240
248
|
case /* FIO_CLI_TYPE_BOOL */ 0x2:
|
241
|
-
fprintf(stderr, "\
|
242
|
-
p + tmp);
|
249
|
+
fprintf(stderr, " \x1B[1m%.*s\x1B[0m \t%s\n", first_len, p, p + tmp);
|
243
250
|
break;
|
244
251
|
case /* FIO_CLI_TYPE_INT */ 0x3:
|
245
|
-
fprintf(stderr, "\
|
246
|
-
|
252
|
+
fprintf(stderr, " \x1B[1m%.*s\x1B[0m\x1B[2m ##\x1B[0m\t%s\n", first_len,
|
253
|
+
p, p + tmp);
|
247
254
|
break;
|
248
255
|
}
|
249
256
|
/* print aliase information */
|
@@ -256,23 +263,26 @@ print_help:
|
|
256
263
|
while (p[tmp] && p[tmp] != ' ' && p[tmp] != ',') {
|
257
264
|
++tmp;
|
258
265
|
}
|
266
|
+
int padding = first_len - (tmp - start);
|
267
|
+
if (padding < 0)
|
268
|
+
padding = 0;
|
259
269
|
switch ((size_t)type) {
|
260
270
|
case /* FIO_CLI_TYPE_STRING */ 0x1:
|
261
271
|
fprintf(stderr,
|
262
|
-
"\
|
272
|
+
" \x1B[1m%.*s\x1B[0m\x1B[2m <>\x1B[0m%*s\t(same as "
|
263
273
|
"\x1B[1m%.*s\x1B[0m)\n",
|
264
|
-
(int)(tmp - start), p + start, first_len, p);
|
274
|
+
(int)(tmp - start), p + start, padding, "", first_len, p);
|
265
275
|
break;
|
266
276
|
case /* FIO_CLI_TYPE_BOOL */ 0x2:
|
267
277
|
fprintf(stderr,
|
268
|
-
"\
|
269
|
-
(int)(tmp - start), p + start, first_len, p);
|
278
|
+
" \x1B[1m%.*s\x1B[0m %*s\t(same as \x1B[1m%.*s\x1B[0m)\n",
|
279
|
+
(int)(tmp - start), p + start, padding, "", first_len, p);
|
270
280
|
break;
|
271
281
|
case /* FIO_CLI_TYPE_INT */ 0x3:
|
272
282
|
fprintf(stderr,
|
273
|
-
"\
|
283
|
+
" \x1B[1m%.*s\x1B[0m\x1B[2m ##\x1B[0m%*s\t(same as "
|
274
284
|
"\x1B[1m%.*s\x1B[0m)\n",
|
275
|
-
(int)(tmp - start), p + start, first_len, p);
|
285
|
+
(int)(tmp - start), p + start, padding, "", first_len, p);
|
276
286
|
break;
|
277
287
|
}
|
278
288
|
}
|
@@ -313,10 +323,12 @@ void fio_cli_start AVOID_MACRO(int argc, char const *argv[], int unnamed_min,
|
|
313
323
|
case /* FIO_CLI_TYPE_STRING */ 0x1: /* fallthrough */
|
314
324
|
case /* FIO_CLI_TYPE_BOOL */ 0x2: /* fallthrough */
|
315
325
|
case /* FIO_CLI_TYPE_INT */ 0x3: /* fallthrough */
|
326
|
+
case /* FIO_CLI_TYPE_PRINT */ 0x4: /* fallthrough */
|
316
327
|
++line;
|
317
328
|
continue;
|
318
329
|
}
|
319
|
-
|
330
|
+
if (line[1] != FIO_CLI_TYPE_PRINT)
|
331
|
+
fio_cli_map_line2alias(*line);
|
320
332
|
++line;
|
321
333
|
}
|
322
334
|
|
data/ext/iodine/fio_cli.h
CHANGED
@@ -22,6 +22,8 @@ CLI API
|
|
22
22
|
#define FIO_CLI_TYPE_BOOL ((char *)0x2)
|
23
23
|
/** Indicates the previous CLI argument should be an Integer (numerical). */
|
24
24
|
#define FIO_CLI_TYPE_INT ((char *)0x3)
|
25
|
+
/** Indicates the previous CLI argument should be an Integer (numerical). */
|
26
|
+
#define FIO_CLI_TYPE_PRINT ((char *)0x4)
|
25
27
|
|
26
28
|
/**
|
27
29
|
* This function parses the Command Line Interface (CLI), creating a temporary
|
data/ext/iodine/iodine.c
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
|
5
5
|
#define FIO_INCLUDE_LINKED_LIST
|
6
6
|
#include "fio.h"
|
7
|
+
#include "fio_cli.h"
|
7
8
|
/* *****************************************************************************
|
8
9
|
OS specific patches
|
9
10
|
***************************************************************************** */
|
@@ -161,13 +162,15 @@ static VALUE iodine_logging_set(VALUE self, VALUE val) {
|
|
161
162
|
}
|
162
163
|
|
163
164
|
/**
|
164
|
-
*Returns the number of worker processes that will be used when {Iodine.start}
|
165
|
+
* Returns the number of worker processes that will be used when {Iodine.start}
|
165
166
|
* is called.
|
166
167
|
*
|
167
168
|
* Negative numbers are translated as fractions of the number of CPU cores.
|
168
169
|
* i.e., -2 == half the number of detected CPU cores.
|
169
170
|
*
|
170
171
|
* Zero values promise nothing (iodine will decide what to do with them).
|
172
|
+
*
|
173
|
+
* 1 == single process mode, the msater process acts as a worker process.
|
171
174
|
*/
|
172
175
|
static VALUE iodine_workers_get(VALUE self) {
|
173
176
|
VALUE i = rb_ivar_get(self, rb_intern2("@workers", 8));
|
@@ -184,6 +187,8 @@ static VALUE iodine_workers_get(VALUE self) {
|
|
184
187
|
* i.e., -2 == half the number of detected CPU cores.
|
185
188
|
*
|
186
189
|
* Zero values promise nothing (iodine will decide what to do with them).
|
190
|
+
*
|
191
|
+
* 1 == single process mode, the msater process acts as a worker process.
|
187
192
|
*/
|
188
193
|
static VALUE iodine_workers_set(VALUE self, VALUE val) {
|
189
194
|
Check_Type(val, T_FIXNUM);
|
@@ -194,7 +199,7 @@ static VALUE iodine_workers_set(VALUE self, VALUE val) {
|
|
194
199
|
return val;
|
195
200
|
}
|
196
201
|
|
197
|
-
/**
|
202
|
+
/** Logs the Iodine startup message */
|
198
203
|
static void iodine_print_startup_message(iodine_start_params_s params) {
|
199
204
|
VALUE iodine_version = rb_const_get(IodineModule, rb_intern("VERSION"));
|
200
205
|
VALUE ruby_version = rb_const_get(IodineModule, rb_intern("RUBY_VERSION"));
|
@@ -251,6 +256,178 @@ static VALUE iodine_stop(VALUE self) {
|
|
251
256
|
return self;
|
252
257
|
}
|
253
258
|
|
259
|
+
/**
|
260
|
+
* Returns `true` if this process is the master / root process, `false`
|
261
|
+
* otherwise.
|
262
|
+
*
|
263
|
+
* Note that the master process might be a worker process as well, when running
|
264
|
+
* in single process mode (see {Iodine.workers}).
|
265
|
+
*/
|
266
|
+
static VALUE iodine_master_is(VALUE self) {
|
267
|
+
return fio_is_master() ? Qtrue : Qfalse;
|
268
|
+
}
|
269
|
+
|
270
|
+
/**
|
271
|
+
* Returns `true` if this process is a worker process or if iodine is running in
|
272
|
+
* a single process mode (the master is also a worker), `false` otherwise.
|
273
|
+
*/
|
274
|
+
static VALUE iodine_worker_is(VALUE self) {
|
275
|
+
return fio_is_master() ? Qtrue : Qfalse;
|
276
|
+
}
|
277
|
+
|
278
|
+
/* *****************************************************************************
|
279
|
+
CLI parser (Ruby's OptParser is more limiting than I knew...)
|
280
|
+
***************************************************************************** */
|
281
|
+
|
282
|
+
/**
|
283
|
+
* Parses the CLI argnumnents, returning the Rack filename (if provided).
|
284
|
+
*
|
285
|
+
* Unknown arguments are ignored.
|
286
|
+
*
|
287
|
+
* @params [String] desc a String containg the iodine server's description.
|
288
|
+
*/
|
289
|
+
static VALUE iodine_cli_parse(VALUE self, VALUE desc) {
|
290
|
+
(void)self;
|
291
|
+
Check_Type(desc, T_STRING);
|
292
|
+
VALUE ARGV = rb_get_argv();
|
293
|
+
VALUE ret = Qtrue;
|
294
|
+
VALUE defaults = iodine_default_args;
|
295
|
+
if (!defaults || !ARGV || TYPE(ARGV) != T_ARRAY || TYPE(defaults) != T_HASH) {
|
296
|
+
FIO_LOG_ERROR("CLI parsing initialization error "
|
297
|
+
"ARGV=%p, Array?(%d), defaults == %p (%d)",
|
298
|
+
(void *)ARGV, (int)(TYPE(ARGV) == T_ARRAY), (void *)defaults,
|
299
|
+
(int)(TYPE(defaults) == T_HASH));
|
300
|
+
return Qnil;
|
301
|
+
}
|
302
|
+
/* Copy the Ruby ARGV to a C valid ARGV */
|
303
|
+
int argc = (int)rb_array_len(ARGV) + 1;
|
304
|
+
if (argc <= 1) {
|
305
|
+
FIO_LOG_DEBUG("CLI: No arguments to parse...\n");
|
306
|
+
return Qnil;
|
307
|
+
} else {
|
308
|
+
FIO_LOG_DEBUG("Iodine CLI parsing %d arguments", argc);
|
309
|
+
}
|
310
|
+
char **argv = calloc(argc, sizeof(*argv));
|
311
|
+
FIO_ASSERT_ALLOC(argv);
|
312
|
+
argv[0] = "iodine";
|
313
|
+
for (int i = 1; i < argc; ++i) {
|
314
|
+
VALUE tmp = rb_ary_entry(ARGV, (long)(i - 1));
|
315
|
+
if (TYPE(tmp) != T_STRING) {
|
316
|
+
FIO_LOG_ERROR("ARGV Array contains a non-String object.");
|
317
|
+
ret = Qnil;
|
318
|
+
goto finish;
|
319
|
+
}
|
320
|
+
fio_str_info_s s = IODINE_RSTRINFO(tmp);
|
321
|
+
argv[i] = malloc(s.len + 1);
|
322
|
+
FIO_ASSERT_ALLOC(argv[i]);
|
323
|
+
memcpy(argv[i], s.data, s.len);
|
324
|
+
argv[i][s.len] = 0;
|
325
|
+
}
|
326
|
+
/* Levarage the facil.io CLI library */
|
327
|
+
fio_cli_start(
|
328
|
+
argc, (const char **)argv, 0, -1, StringValueCStr(desc),
|
329
|
+
"\x1B[1m\x1B[4mAddress Binding:\x1B[0m", FIO_CLI_TYPE_PRINT,
|
330
|
+
"-bind -b -address address to listen to. defaults any available.",
|
331
|
+
"-port -p port number to listen to. defaults port 3000", FIO_CLI_TYPE_INT,
|
332
|
+
"\n\x1B[1m\x1B[4mConcurrency:\x1B[0m", FIO_CLI_TYPE_PRINT,
|
333
|
+
"-workers -w number of processes to use.", FIO_CLI_TYPE_INT,
|
334
|
+
"-threads -t number of threads per process.", FIO_CLI_TYPE_INT,
|
335
|
+
"\n\x1B[1m\x1B[4mHTTP Server:\x1B[0m", FIO_CLI_TYPE_PRINT,
|
336
|
+
"-public -www public folder, for static file service.",
|
337
|
+
"-log -v HTTP request logging.", FIO_CLI_TYPE_BOOL,
|
338
|
+
"-keep-alive -k -tout HTTP keep-alive timeout (0..255). Default: 40s",
|
339
|
+
FIO_CLI_TYPE_INT, "-ping websocket ping interval (0..255). Default: 40s",
|
340
|
+
FIO_CLI_TYPE_INT,
|
341
|
+
"-max-body -maxbd HTTP upload limit in Mega-Bytes. Default: 50Mb",
|
342
|
+
FIO_CLI_TYPE_INT,
|
343
|
+
"-max-header -maxhd header limit per HTTP request in Kb."
|
344
|
+
" Default: 32Kb.",
|
345
|
+
FIO_CLI_TYPE_INT, "\n\x1B[1m\x1B[4mWebSocket Server:\x1B[0m",
|
346
|
+
FIO_CLI_TYPE_PRINT,
|
347
|
+
"-max-msg -maxms incoming WebSocket message limit in Kb. "
|
348
|
+
"Default: 250Kb",
|
349
|
+
FIO_CLI_TYPE_INT, "\n\x1B[1m\x1B[4mConnecting Iodine to Redis:\x1B[0m",
|
350
|
+
FIO_CLI_TYPE_PRINT,
|
351
|
+
"-redis -r an optional Redis URL server address. Default: none.",
|
352
|
+
"-redis-ping -rp websocket ping interval (0..255). Default: 5 minutes",
|
353
|
+
FIO_CLI_TYPE_INT, "\n\x1B[1m\x1B[4mMisc:\x1B[0m", FIO_CLI_TYPE_PRINT,
|
354
|
+
"-warmup warm up the application. CAREFUL! iodine might fork.",
|
355
|
+
FIO_CLI_TYPE_BOOL,
|
356
|
+
"-verbosity -V 0..5 server verbosity level. Default: 4",
|
357
|
+
FIO_CLI_TYPE_INT);
|
358
|
+
/* copy values from CLI library to iodine */
|
359
|
+
if (fio_cli_get("-V")) {
|
360
|
+
int level = fio_cli_get_i("-V");
|
361
|
+
if (level > 0 && level < 100)
|
362
|
+
FIO_LOG_LEVEL = level;
|
363
|
+
}
|
364
|
+
if (fio_cli_get("-w")) {
|
365
|
+
iodine_workers_set(IodineModule, INT2NUM(fio_cli_get_i("-w")));
|
366
|
+
}
|
367
|
+
if (fio_cli_get("-t")) {
|
368
|
+
iodine_threads_set(IodineModule, INT2NUM(fio_cli_get_i("-t")));
|
369
|
+
}
|
370
|
+
if (fio_cli_get_bool("-v")) {
|
371
|
+
rb_hash_aset(defaults, ID2SYM(rb_intern("log")), Qtrue);
|
372
|
+
}
|
373
|
+
if (fio_cli_get_bool("-warmup")) {
|
374
|
+
rb_hash_aset(defaults, ID2SYM(rb_intern("warmup_")), Qtrue);
|
375
|
+
}
|
376
|
+
if (fio_cli_get("-p")) {
|
377
|
+
rb_hash_aset(defaults, ID2SYM(rb_intern("port")),
|
378
|
+
rb_str_new_cstr(fio_cli_get("-p")));
|
379
|
+
}
|
380
|
+
if (fio_cli_get("-b")) {
|
381
|
+
rb_hash_aset(defaults, ID2SYM(rb_intern("address")),
|
382
|
+
rb_str_new_cstr(fio_cli_get("-b")));
|
383
|
+
}
|
384
|
+
if (fio_cli_get("-www")) {
|
385
|
+
rb_hash_aset(defaults, ID2SYM(rb_intern("public")),
|
386
|
+
rb_str_new_cstr(fio_cli_get("-www")));
|
387
|
+
}
|
388
|
+
if (fio_cli_get("-redis")) {
|
389
|
+
rb_hash_aset(defaults, ID2SYM(rb_intern("redis_")),
|
390
|
+
rb_str_new_cstr(fio_cli_get("-redis")));
|
391
|
+
}
|
392
|
+
if (fio_cli_get("-k")) {
|
393
|
+
rb_hash_aset(defaults, ID2SYM(rb_intern("timeout")),
|
394
|
+
INT2NUM(fio_cli_get_i("-k")));
|
395
|
+
}
|
396
|
+
if (fio_cli_get("-ping")) {
|
397
|
+
rb_hash_aset(defaults, ID2SYM(rb_intern("ping")),
|
398
|
+
INT2NUM(fio_cli_get_i("-ping")));
|
399
|
+
}
|
400
|
+
if (fio_cli_get("-redis-ping")) {
|
401
|
+
rb_hash_aset(defaults, ID2SYM(rb_intern("redis_ping_")),
|
402
|
+
INT2NUM(fio_cli_get_i("-redis-ping")));
|
403
|
+
}
|
404
|
+
if (fio_cli_get("-max-body")) {
|
405
|
+
rb_hash_aset(defaults, ID2SYM(rb_intern("max_body")),
|
406
|
+
INT2NUM((fio_cli_get_i("-max-body") * 1024 * 1024)));
|
407
|
+
}
|
408
|
+
if (fio_cli_get("-max-message")) {
|
409
|
+
rb_hash_aset(defaults, ID2SYM(rb_intern("max_msg")),
|
410
|
+
INT2NUM((fio_cli_get_i("-max-message") * 1024)));
|
411
|
+
}
|
412
|
+
if (fio_cli_get("-max-headers")) {
|
413
|
+
rb_hash_aset(defaults, ID2SYM(rb_intern("max_headers")),
|
414
|
+
INT2NUM((fio_cli_get_i("-max-headers") * 1024)));
|
415
|
+
}
|
416
|
+
if (fio_cli_unnamed_count()) {
|
417
|
+
rb_hash_aset(defaults, ID2SYM(rb_intern("filename_")),
|
418
|
+
rb_str_new_cstr(fio_cli_unnamed(0)));
|
419
|
+
}
|
420
|
+
|
421
|
+
/* create `filename` String, cleanup and return */
|
422
|
+
fio_cli_end();
|
423
|
+
finish:
|
424
|
+
for (int i = 1; i < argc; ++i) {
|
425
|
+
free(argv[i]);
|
426
|
+
}
|
427
|
+
free(argv);
|
428
|
+
return ret;
|
429
|
+
}
|
430
|
+
|
254
431
|
/* *****************************************************************************
|
255
432
|
Ruby loads the library and invokes the Init_<lib_name> function...
|
256
433
|
|
@@ -267,6 +444,7 @@ void Init_iodine(void) {
|
|
267
444
|
// Create the Iodine module (namespace)
|
268
445
|
IodineModule = rb_define_module("Iodine");
|
269
446
|
IodineBaseModule = rb_define_module_under(IodineModule, "Base");
|
447
|
+
VALUE IodineCLIModule = rb_define_module_under(IodineBaseModule, "CLI");
|
270
448
|
call_id = rb_intern2("call", 4);
|
271
449
|
|
272
450
|
// register core methods
|
@@ -279,6 +457,11 @@ void Init_iodine(void) {
|
|
279
457
|
rb_define_module_function(IodineModule, "start", iodine_start, 0);
|
280
458
|
rb_define_module_function(IodineModule, "stop", iodine_stop, 0);
|
281
459
|
rb_define_module_function(IodineModule, "on_idle", iodine_sched_on_idle, 0);
|
460
|
+
rb_define_module_function(IodineModule, "master?", iodine_master_is, 0);
|
461
|
+
rb_define_module_function(IodineModule, "worker?", iodine_worker_is, 0);
|
462
|
+
|
463
|
+
// register CLI methods
|
464
|
+
rb_define_module_function(IodineCLIModule, "parse", iodine_cli_parse, 1);
|
282
465
|
|
283
466
|
// initialize Object storage for GC protection
|
284
467
|
iodine_storage_init();
|
@@ -249,8 +249,10 @@ static VALUE iodine_connection_pending(VALUE self) {
|
|
249
249
|
return SIZET2NUM((fio_pending(c->info.uuid)));
|
250
250
|
}
|
251
251
|
|
252
|
-
|
252
|
+
// clang-format off
|
253
|
+
/** Returns the connection's protocol Symbol (`:sse`, `:websocket`, etc'), if originated in HTTP (`rack.upgrade`). */
|
253
254
|
static VALUE iodine_connection_protocol_name(VALUE self) {
|
255
|
+
// clang-format on
|
254
256
|
iodine_connection_data_s *c = iodine_connection_validate_data(self);
|
255
257
|
if (c) {
|
256
258
|
switch (c->info.type) {
|
data/ext/iodine/iodine_defer.c
CHANGED
@@ -10,11 +10,22 @@
|
|
10
10
|
|
11
11
|
#include <pthread.h>
|
12
12
|
|
13
|
+
static ID STATE_PRE_START;
|
14
|
+
static ID STATE_BEFORE_FORK;
|
15
|
+
static ID STATE_AFTER_FORK;
|
16
|
+
static ID STATE_ENTER_CHILD;
|
17
|
+
static ID STATE_ENTER_MASTER;
|
18
|
+
static ID STATE_ON_START;
|
19
|
+
static ID STATE_ON_PARENT_CRUSH;
|
20
|
+
static ID STATE_ON_CHILD_CRUSH;
|
21
|
+
static ID STATE_START_SHUTDOWN;
|
22
|
+
static ID STATE_ON_FINISH;
|
23
|
+
|
13
24
|
/* *****************************************************************************
|
14
25
|
IO flushing dedicated thread for protection against blocking code
|
15
26
|
***************************************************************************** */
|
16
27
|
|
17
|
-
static fio_lock_i
|
28
|
+
static fio_lock_i sock_io_thread_flag = 0;
|
18
29
|
static pthread_t sock_io_pthread;
|
19
30
|
typedef struct {
|
20
31
|
size_t threads;
|
@@ -23,29 +34,29 @@ typedef struct {
|
|
23
34
|
|
24
35
|
static void *iodine_io_thread(void *arg) {
|
25
36
|
(void)arg;
|
26
|
-
while (
|
27
|
-
fio_flush_all()
|
28
|
-
|
37
|
+
while (sock_io_thread_flag) {
|
38
|
+
if (fio_flush_all() > 1)
|
39
|
+
fio_throttle_thread(500000UL);
|
40
|
+
else
|
41
|
+
fio_throttle_thread(150000000UL);
|
29
42
|
}
|
30
43
|
return NULL;
|
31
44
|
}
|
32
45
|
static void iodine_start_io_thread(void *a_) {
|
33
|
-
if (!
|
34
|
-
pthread_create(&sock_io_pthread, NULL, iodine_io_thread, NULL)
|
46
|
+
if (!fio_trylock(&sock_io_thread_flag)) {
|
47
|
+
if (pthread_create(&sock_io_pthread, NULL, iodine_io_thread, NULL)) {
|
48
|
+
FIO_LOG_ERROR("Couldn't spawn IO thread.");
|
49
|
+
};
|
50
|
+
FIO_LOG_DEBUG("IO thread started.");
|
35
51
|
}
|
36
52
|
(void)a_;
|
37
53
|
}
|
38
54
|
|
39
|
-
static void iodine_start_io_thread2(void *a_, void *b_) {
|
40
|
-
iodine_start_io_thread(a_);
|
41
|
-
(void)b_;
|
42
|
-
}
|
43
|
-
|
44
55
|
static void iodine_join_io_thread(void) {
|
45
|
-
if (
|
46
|
-
sock_io_thread = 0;
|
56
|
+
if (fio_unlock(&sock_io_thread_flag) && sock_io_pthread) {
|
47
57
|
pthread_join(sock_io_pthread, NULL);
|
48
58
|
sock_io_pthread = (pthread_t)NULL;
|
59
|
+
FIO_LOG_DEBUG("IO thread stopped and joined.");
|
49
60
|
}
|
50
61
|
}
|
51
62
|
|
@@ -108,8 +119,6 @@ static void *fork_using_ruby(void *ignr) {
|
|
108
119
|
if (!pid) {
|
109
120
|
IodineStore.after_fork();
|
110
121
|
}
|
111
|
-
// re-initiate IO thread
|
112
|
-
fio_defer(iodine_start_io_thread2, NULL, NULL);
|
113
122
|
return (void *)pid;
|
114
123
|
(void)ignr;
|
115
124
|
}
|
@@ -288,83 +297,78 @@ static void iodine_perform_state_callback_persist(void *blk_) {
|
|
288
297
|
|
289
298
|
// clang-format off
|
290
299
|
/**
|
291
|
-
Sets a block of code to run
|
300
|
+
Sets a block of code to run when Iodine's core state is updated.
|
292
301
|
|
293
|
-
|
294
|
-
|
295
|
-
static VALUE iodine_before_fork_add(VALUE self) {
|
296
|
-
// clang-format on
|
297
|
-
rb_need_block();
|
298
|
-
VALUE block = rb_block_proc();
|
299
|
-
IodineStore.add(block);
|
300
|
-
fio_state_callback_add(FIO_CALL_BEFORE_FORK,
|
301
|
-
iodine_perform_state_callback_persist, (void *)block);
|
302
|
-
return block;
|
303
|
-
(void)self;
|
304
|
-
}
|
302
|
+
@param [Symbol] event the state event for which the block should run (see list).
|
303
|
+
@since 0.7.9
|
305
304
|
|
306
|
-
|
307
|
-
/**
|
308
|
-
Sets a block of code to run after a new worker process is forked (cluster mode only).
|
305
|
+
The state event Symbol can be any of the following:
|
309
306
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
(void)self;
|
321
|
-
}
|
322
|
-
|
323
|
-
// clang-format off
|
324
|
-
/**
|
325
|
-
Sets a block of code to run after a new worker process is forked (cluster mode only).
|
307
|
+
:pre_start :: the block will be called once before starting up the IO reactor.
|
308
|
+
:before_fork :: the block will be called before each time the IO reactor forks a new worker.
|
309
|
+
:after_fork :: the block will be called after each fork (both in parent and workers).
|
310
|
+
:enter_child :: the block will be called by a worker process right after forking.
|
311
|
+
:enter_master :: the block will be called by the master process after spawning a worker (after forking).
|
312
|
+
:on_start :: the block will be called every time a *worker* proceess starts. In single process mode, the master process is also a worker.
|
313
|
+
:on_parent_crush :: the block will be called by each worker the moment it detects the master process crashed.
|
314
|
+
:on_child_crush :: the block will be called by the parent (master) after a worker process crashed.
|
315
|
+
:start_shutdown :: the block will be called before starting the shutdown sequence.
|
316
|
+
:on_finish :: the block will be called just before finishing up (both on chlid and parent processes).
|
326
317
|
|
327
318
|
Code runs in both the parent and the child.
|
328
319
|
*/
|
329
|
-
static VALUE
|
320
|
+
static VALUE iodine_on_state(VALUE self, VALUE event) {
|
330
321
|
// clang-format on
|
331
322
|
rb_need_block();
|
323
|
+
Check_Type(event, T_SYMBOL);
|
332
324
|
VALUE block = rb_block_proc();
|
333
325
|
IodineStore.add(block);
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
326
|
+
ID state = rb_sym2id(event);
|
327
|
+
|
328
|
+
if (state == STATE_PRE_START) {
|
329
|
+
fio_state_callback_add(FIO_CALL_PRE_START,
|
330
|
+
iodine_perform_state_callback_persist,
|
331
|
+
(void *)block);
|
332
|
+
} else if (state == STATE_BEFORE_FORK) {
|
333
|
+
fio_state_callback_add(FIO_CALL_BEFORE_FORK,
|
334
|
+
iodine_perform_state_callback_persist,
|
335
|
+
(void *)block);
|
336
|
+
} else if (state == STATE_AFTER_FORK) {
|
337
|
+
fio_state_callback_add(FIO_CALL_AFTER_FORK,
|
338
|
+
iodine_perform_state_callback_persist,
|
339
|
+
(void *)block);
|
340
|
+
} else if (state == STATE_ENTER_CHILD) {
|
341
|
+
fio_state_callback_add(FIO_CALL_IN_CHILD,
|
342
|
+
iodine_perform_state_callback_persist,
|
343
|
+
(void *)block);
|
344
|
+
} else if (state == STATE_ENTER_MASTER) {
|
345
|
+
fio_state_callback_add(FIO_CALL_IN_MASTER,
|
346
|
+
iodine_perform_state_callback_persist,
|
347
|
+
(void *)block);
|
348
|
+
} else if (state == STATE_ON_START) {
|
349
|
+
fio_state_callback_add(FIO_CALL_ON_START,
|
350
|
+
iodine_perform_state_callback_persist,
|
351
|
+
(void *)block);
|
352
|
+
} else if (state == STATE_ON_PARENT_CRUSH) {
|
353
|
+
fio_state_callback_add(FIO_CALL_ON_PARENT_CRUSH,
|
354
|
+
iodine_perform_state_callback_persist,
|
355
|
+
(void *)block);
|
356
|
+
} else if (state == STATE_ON_CHILD_CRUSH) {
|
357
|
+
fio_state_callback_add(FIO_CALL_ON_CHILD_CRUSH,
|
358
|
+
iodine_perform_state_callback_persist,
|
359
|
+
(void *)block);
|
360
|
+
} else if (state == STATE_START_SHUTDOWN) {
|
361
|
+
fio_state_callback_add(FIO_CALL_ON_SHUTDOWN,
|
362
|
+
iodine_perform_state_callback_persist,
|
363
|
+
(void *)block);
|
364
|
+
} else if (state == STATE_ON_FINISH) {
|
365
|
+
fio_state_callback_add(FIO_CALL_ON_FINISH,
|
366
|
+
iodine_perform_state_callback_persist,
|
367
|
+
(void *)block);
|
368
|
+
} else {
|
369
|
+
IodineStore.remove(block);
|
370
|
+
rb_raise(rb_eTypeError, "unknown event in Iodine.on_state");
|
371
|
+
}
|
368
372
|
return block;
|
369
373
|
(void)self;
|
370
374
|
}
|
@@ -372,6 +376,7 @@ static VALUE iodine_on_shutdown_add(VALUE self) {
|
|
372
376
|
/* Performs any cleanup before worker dies */
|
373
377
|
static void iodine_defer_on_finish(void *ignr) {
|
374
378
|
(void)ignr;
|
379
|
+
|
375
380
|
iodine_join_io_thread();
|
376
381
|
}
|
377
382
|
|
@@ -388,16 +393,20 @@ void iodine_defer_initialize(void) {
|
|
388
393
|
1);
|
389
394
|
rb_define_module_function(IodineModule, "run_every", iodine_defer_run_every,
|
390
395
|
-1);
|
391
|
-
rb_define_module_function(IodineModule, "
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
396
|
+
rb_define_module_function(IodineModule, "on_state", iodine_on_state, 1);
|
397
|
+
|
398
|
+
STATE_PRE_START = rb_intern("pre_start");
|
399
|
+
STATE_BEFORE_FORK = rb_intern("before_fork");
|
400
|
+
STATE_AFTER_FORK = rb_intern("after_fork");
|
401
|
+
STATE_ENTER_CHILD = rb_intern("enter_child");
|
402
|
+
STATE_ENTER_MASTER = rb_intern("enter_master");
|
403
|
+
STATE_ON_START = rb_intern("on_start");
|
404
|
+
STATE_ON_PARENT_CRUSH = rb_intern("on_parent_crush");
|
405
|
+
STATE_ON_CHILD_CRUSH = rb_intern("on_child_crush");
|
406
|
+
STATE_START_SHUTDOWN = rb_intern("start_shutdown");
|
407
|
+
STATE_ON_FINISH = rb_intern("on_finish");
|
408
|
+
|
401
409
|
fio_state_callback_add(FIO_CALL_ON_FINISH, iodine_defer_on_finish, NULL);
|
402
410
|
fio_state_callback_add(FIO_CALL_PRE_START, iodine_start_io_thread, NULL);
|
411
|
+
fio_state_callback_add(FIO_CALL_AFTER_FORK, iodine_start_io_thread, NULL);
|
403
412
|
}
|
data/ext/iodine/iodine_http.c
CHANGED
@@ -55,7 +55,7 @@ static rb_encoding *IodineBinaryEncoding;
|
|
55
55
|
static uint8_t support_xsendfile = 0;
|
56
56
|
|
57
57
|
/** Used by {listen2http} to set missing arguments. */
|
58
|
-
|
58
|
+
VALUE iodine_default_args;
|
59
59
|
|
60
60
|
#define rack_declare(rack_name) static VALUE rack_name
|
61
61
|
|
@@ -803,7 +803,7 @@ address:: the address to bind to. Default: binds to all possible addresses.
|
|
803
803
|
log:: enable response logging (Hijacked sockets aren't logged). Default: off.
|
804
804
|
public:: The root public folder for static file service. Default: none.
|
805
805
|
timeout:: Timeout for inactive HTTP/1.x connections. Defaults: 40 seconds.
|
806
|
-
max_body:: The maximum body size for incoming HTTP messages. Default: ~50Mib.
|
806
|
+
max_body:: The maximum body size for incoming HTTP messages in bytes. Default: ~50Mib.
|
807
807
|
max_headers:: The maximum total header length for incoming HTTP messages. Default: ~64Kib.
|
808
808
|
max_msg:: The maximum Websocket message size allowed. Default: ~250Kib.
|
809
809
|
ping:: The Websocket `ping` interval. Default: 40 seconds.
|
@@ -958,6 +958,7 @@ static VALUE iodine_http_listen(VALUE self, VALUE opt) {
|
|
958
958
|
.public_folder = (www ? StringValueCStr(www) : NULL)) == -1) {
|
959
959
|
FIO_LOG_ERROR("Failed to initialize a listening HTTP socket for port %s",
|
960
960
|
port ? StringValueCStr(port) : "3000");
|
961
|
+
rb_raise(rb_eRuntimeError, "Listening socket initialization failed");
|
961
962
|
return Qfalse;
|
962
963
|
}
|
963
964
|
|
data/ext/iodine/iodine_http.h
CHANGED
data/lib/iodine.rb
CHANGED
@@ -41,7 +41,7 @@ require 'iodine/iodine'
|
|
41
41
|
#
|
42
42
|
# Methods for setting up and starting {Iodine} include {start}, {threads}, {threads=}, {workers} and {workers=}.
|
43
43
|
#
|
44
|
-
# Methods for setting startup / operational callbacks include {on_idle}, {
|
44
|
+
# Methods for setting startup / operational callbacks include {on_idle}, {on_state}.
|
45
45
|
#
|
46
46
|
# Methods for asynchronous execution include {run} (same as {defer}), {run_after} and {run_every}.
|
47
47
|
#
|
@@ -71,94 +71,138 @@ module Iodine
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
-
end
|
75
74
|
|
76
|
-
|
75
|
+
# @deprecated use {Iodine.on_state}.
|
76
|
+
#
|
77
|
+
# Sets a block of code to run before a new worker process is forked (cluster mode only).
|
78
|
+
def self.before_fork(&block)
|
79
|
+
warn "Iodine.before_fork is deprecated, use Iodine.on_state(:before_fork)."
|
80
|
+
Iodine.on_state(:before_fork, &block)
|
81
|
+
end
|
82
|
+
# @deprecated use {Iodine.on_state}.
|
83
|
+
#
|
84
|
+
# Sets a block of code to run after a new worker process is forked (cluster mode only).
|
85
|
+
#
|
86
|
+
# Code runs in both the parent and the child.
|
87
|
+
def self.after_fork(&block)
|
88
|
+
warn "Iodine.after_fork is deprecated, use Iodine.on_state(:after_fork)."
|
89
|
+
Iodine.on_state(:after_fork, &block)
|
90
|
+
end
|
91
|
+
# @deprecated use {Iodine.on_state}.
|
92
|
+
#
|
93
|
+
# Sets a block of code to run in the worker process, after a new worker process is forked (cluster mode only).
|
94
|
+
def self.after_fork_in_worker(&block)
|
95
|
+
warn "Iodine.after_fork_in_worker is deprecated, use Iodine.on_state(:enter_child)."
|
96
|
+
Iodine.on_state(:enter_child, &block)
|
97
|
+
end
|
98
|
+
# @deprecated use {Iodine.on_state}.
|
99
|
+
#
|
100
|
+
# Sets a block of code to run in the master / root process, after a new worker process is forked (cluster mode only).
|
101
|
+
def self.after_fork_in_master(&block)
|
102
|
+
warn "Iodine.after_fork_in_master is deprecated, use Iodine.on_state(:enter_master)."
|
103
|
+
Iodine.on_state(:enter_master, &block)
|
104
|
+
end
|
105
|
+
# @deprecated use {Iodine.on_state}.
|
106
|
+
#
|
107
|
+
# Sets a block of code to run once a Worker process shuts down (both in single process mode and cluster mode).
|
108
|
+
def self.on_shutdown(&block)
|
109
|
+
warn "Iodine.on_shutdown is deprecated, use Iodine.on_state(:on_finish)."
|
110
|
+
Iodine.on_state(:on_finish, &block)
|
111
|
+
end
|
77
112
|
|
113
|
+
end
|
78
114
|
|
79
|
-
|
115
|
+
require 'rack/handler/iodine' unless defined? ::Iodine::Rack::IODINE_RACK_LOADED
|
80
116
|
|
81
|
-
if ARGV.index('-b') && ARGV[ARGV.index('-b') + 1]
|
82
|
-
Iodine::DEFAULT_HTTP_ARGS[:address] = ARGV[ARGV.index('-b') + 1]
|
83
|
-
end
|
84
|
-
if ARGV.index('-p') && ARGV[ARGV.index('-p') + 1]
|
85
|
-
Iodine::DEFAULT_HTTP_ARGS[:port] = ARGV[ARGV.index('-p') + 1]
|
86
|
-
end
|
87
117
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
118
|
+
### Automatic ActiveRecord and Sequel fix
|
119
|
+
Iodine.on_state(:before_fork) do
|
120
|
+
if defined?(ActiveRecord) && defined?(ActiveRecord::Base) && ActiveRecord::Base.respond_to?(:connection)
|
121
|
+
begin
|
122
|
+
ActiveRecord::Base.connection.disconnect!
|
123
|
+
rescue
|
124
|
+
end
|
125
|
+
end
|
126
|
+
if defined?(Sequel)
|
127
|
+
begin
|
128
|
+
Sequel::DATABASES.each { |database| database.disconnect }
|
129
|
+
rescue
|
130
|
+
end
|
131
|
+
end
|
102
132
|
end
|
103
|
-
|
104
|
-
|
105
|
-
|
133
|
+
Iodine.on_state(:after_fork) do
|
134
|
+
if defined?(ActiveRecord) && defined?(ActiveRecord::Base) && ActiveRecord::Base.respond_to?(:establish_connection)
|
135
|
+
begin
|
136
|
+
ActiveRecord::Base.establish_connection
|
137
|
+
rescue
|
138
|
+
end
|
139
|
+
end
|
106
140
|
end
|
107
|
-
Iodine::DEFAULT_HTTP_ARGS[:log] = true if ARGV.index('-v')
|
108
141
|
|
109
|
-
|
110
|
-
|
111
|
-
end
|
142
|
+
### Parse CLI for default HTTP settings
|
143
|
+
Iodine::Base::CLI.parse("Iodine's HTTP/WebSocket server version #{Iodine::VERSION}\r\n\r\nUse:\r\n iodine <options> <filename>\r\n\r\nBoth <options> and <filename> are optional. i.e.,:\r\n iodine -p 0 -b /tmp/my_unix_sock\r\n iodine -p 8080 path/to/app/conf.ru\r\n iodine -p 8080 -w 4 -t 16\r\n iodine -w -1 -t 4 -r redis://usr:pass@localhost:6379/\r\n")
|
112
144
|
|
113
|
-
|
114
|
-
|
115
|
-
end
|
116
|
-
if ARGV.index('-w') && ARGV[ARGV.index('-w') + 1].to_i != 0
|
117
|
-
Iodine.workers = ARGV[ARGV.index('-w') + 1].to_i
|
118
|
-
end
|
145
|
+
### Initialize Redis if set in CLI
|
146
|
+
Iodine::PubSub.default = Iodine::PubSub::Redis.new(Iodine::DEFAULT_HTTP_ARGS[:redis_], ping: Iodine::DEFAULT_HTTP_ARGS[:redis_ping_]) if Iodine::DEFAULT_HTTP_ARGS[:redis_]
|
119
147
|
|
120
|
-
### Puma / Thin DSL compatibility
|
148
|
+
### Puma / Thin DSL compatibility - depracated (DSLs are evil)
|
121
149
|
|
122
150
|
if(!defined?(after_fork))
|
151
|
+
# @deprecated use {Iodine.on_state}.
|
152
|
+
#
|
123
153
|
# Performs a block of code whenever a new worker process spins up (performed once per worker).
|
124
154
|
def after_fork(*args, &block)
|
125
|
-
Iodine.after_fork
|
155
|
+
warn "after_fork is deprecated, use Iodine.on_state(:after_fork)."
|
156
|
+
Iodine.on_state(:after_fork, &block)
|
126
157
|
end
|
127
158
|
end
|
128
159
|
if(!defined?(after_fork_in_worker))
|
160
|
+
# @deprecated use {Iodine.on_state}.
|
161
|
+
#
|
129
162
|
# Performs a block of code whenever a new worker process spins up (performed once per worker).
|
130
163
|
def after_fork_in_worker(*args, &block)
|
131
|
-
Iodine.
|
164
|
+
warn "after_fork_in_worker is deprecated, use Iodine.on_state(:enter_child)."
|
165
|
+
Iodine.on_state(:enter_child, &block)
|
132
166
|
end
|
133
167
|
end
|
134
168
|
if(!defined?(after_fork_in_master))
|
169
|
+
# @deprecated use {Iodine.on_state}.
|
170
|
+
#
|
135
171
|
# Performs a block of code whenever a new worker process spins up (performed once per worker).
|
136
172
|
def after_fork_in_master(*args, &block)
|
137
|
-
Iodine.
|
173
|
+
warn "after_fork_in_master is deprecated, use Iodine.on_state(:enter_master)."
|
174
|
+
Iodine.on_state(:enter_master, &block)
|
138
175
|
end
|
139
176
|
end
|
140
177
|
if(!defined?(on_worker_boot))
|
178
|
+
# @deprecated use {Iodine.on_state}.
|
179
|
+
#
|
141
180
|
# Performs a block of code before a new worker process spins up (performed once per worker).
|
142
181
|
def on_worker_boot(*args, &block)
|
143
|
-
Iodine.after_fork
|
182
|
+
warn "on_worker_boot is deprecated, use Iodine.on_state(:after_fork)."
|
183
|
+
Iodine.on_state(:after_fork, &block)
|
144
184
|
end
|
145
185
|
end
|
146
186
|
if(!defined?(on_worker_fork))
|
187
|
+
# @deprecated use {Iodine.on_state}.
|
188
|
+
#
|
147
189
|
# Performs a block of code before a new worker process spins up (performed once per worker).
|
148
190
|
def on_worker_fork(*args, &block)
|
149
|
-
Iodine.before_fork
|
191
|
+
warn "on_worker_fork is deprecated, use Iodine.on_state(:before_fork)."
|
192
|
+
Iodine.on_state(:before_fork, &block)
|
150
193
|
end
|
151
194
|
end
|
152
195
|
if(!defined?(before_fork))
|
196
|
+
# @deprecated use {Iodine.on_state}.
|
197
|
+
#
|
153
198
|
# Performs a block of code just before a new worker process spins up (performed once per worker, in the master thread).
|
154
199
|
def before_fork(*args, &block)
|
155
|
-
Iodine.before_fork
|
200
|
+
warn "before_fork is deprecated, use Iodine.on_state(:before_fork)."
|
201
|
+
Iodine.on_state(:before_fork, &block)
|
156
202
|
end
|
157
203
|
end
|
158
204
|
|
159
|
-
|
160
|
-
ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
|
161
|
-
end
|
205
|
+
|
162
206
|
|
163
207
|
|
164
208
|
|
data/lib/iodine/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iodine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boaz Segev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-11-
|
11
|
+
date: 2018-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -244,7 +244,7 @@ licenses:
|
|
244
244
|
- MIT
|
245
245
|
metadata:
|
246
246
|
allowed_push_host: https://rubygems.org
|
247
|
-
post_install_message: 'Thank you for installing Iodine 0.7.
|
247
|
+
post_install_message: 'Thank you for installing Iodine 0.7.9.
|
248
248
|
|
249
249
|
'
|
250
250
|
rdoc_options: []
|