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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ab6a6656db6e8d26002bb029212f5d5d65b8cfeaddf5550416ce796dc16dc70
4
- data.tar.gz: aa23fa7aab530a3b448c2a52e63dffe52c8d8aee1e9259b53c7523e81b379876
3
+ metadata.gz: abc090d98d50e754078aabdc8e46065b7e56b5d79f563866e563365c133ba088
4
+ data.tar.gz: 03e59675043f7b7f7e31aa640bff7f8ef01912cc074bc0d576461dc31f3a38ca
5
5
  SHA512:
6
- metadata.gz: 51e9ac58a76b225d55ad354de3a1c25b25f7b286e88e819c3d067ddbc568521bdd9b87d1da9b7528b24528ccf7517f4762ef11a39894dfcacda8584d812526ee
7
- data.tar.gz: 2623b6ea0a7957f92d8686ca80c2f8e6bbf61cf1a04dd0b1d49e20f6e25aa8b6dfdf35f2faa68d9929ec67cecdce0618c57abdf584038713b2e26d75a316bcb2
6
+ metadata.gz: 513c570195901322763ec71076bc9697a8d365885db8b4b10fbd072aee9f36500cecaf33b5b9cd0df198558a512fdef318b83c72c7e1d452fece7c2ca70bf22d
7
+ data.tar.gz: a6ab97b226cca87200be69507910951ba7f0036e2283fbc8b4b9d67926e4c4ddaa1b87e193aa7776e87d411b7ea371987a1e434c1017c804f17cd9eba6872a92
@@ -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
- Simply `gzip` your static files and iodine will automatically recognize and send the `gz` version if the client (browser) supports the `gzip` transfer-encoding.
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
- root_pid = Process.pid
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 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:
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 print_help
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 filename_argument
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 = filename_argument
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 "For help run:"
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
@@ -2685,13 +2685,15 @@ flushed:
2685
2685
  }
2686
2686
 
2687
2687
  /** `fio_flush_all` attempts flush all the open connections. */
2688
- void fio_flush_all(void) {
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
- fio_flush(fd2uuid(i));
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
- fio_defer(fio_perform_idle, NULL, NULL);
3308
+ fio_state_callback_force(FIO_CALL_ON_IDLE);
3313
3309
  idle = 0;
3314
3310
  }
3315
3311
  }
@@ -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
- /** `fio_flush_all` attempts flush all the open connections. */
1106
- void fio_flush_all(void);
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 -1 on failure. */
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
- /** Releases a spinlock. Releasing an unacquired lock will break it. */
1879
- FIO_FUNC inline void fio_unlock(fio_lock_i *lock);
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 -1 on failure. */
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
- /** Releases a spinlock. Releasing an unacquired lock will break it. */
2623
- FIO_FUNC inline void fio_unlock(fio_lock_i *lock) {
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). */
@@ -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, "\t\x1B[1m%.*s\x1B[0m\x1B[2m <val>\x1B[0m\t%s\n",
238
- first_len, p, p + tmp);
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, "\t\x1B[1m%.*s\x1B[0m \t%s\n", first_len, p,
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, "\t\x1B[1m%.*s\x1B[0m\x1B[2m ### \x1B[0m\t%s\n",
246
- first_len, p, p + tmp);
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
- "\t\x1B[1m%.*s\x1B[0m\x1B[2m <val>\x1B[0m\t(same as "
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
- "\t\x1B[1m%.*s\x1B[0m \t(same as \x1B[1m%.*s\x1B[0m)\n",
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
- "\t\x1B[1m%.*s\x1B[0m\x1B[2m ### \x1B[0m\t(same as "
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
- fio_cli_map_line2alias(*line);
330
+ if (line[1] != FIO_CLI_TYPE_PRINT)
331
+ fio_cli_map_line2alias(*line);
320
332
  ++line;
321
333
  }
322
334
 
@@ -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
@@ -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
- /** Prints the Iodine startup message */
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
- /** Returns the connection's protocol Symbol (`:sse`, `:websocket`, etc'). */
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) {
@@ -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 sock_io_thread = 0;
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 (sock_io_thread) {
27
- fio_flush_all();
28
- fio_throttle_thread(100);
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 (!fio_atomic_add(&sock_io_thread, 1)) {
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 (fio_atomic_sub(&sock_io_thread, 1) == 0) {
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 before a new worker process is forked (cluster mode only).
300
+ Sets a block of code to run when Iodine's core state is updated.
292
301
 
293
- Code runs within the master (root) process.
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
- // clang-format off
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
- Code runs in both the parent and the child.
311
- */
312
- static VALUE iodine_after_fork_add(VALUE self) {
313
- // clang-format on
314
- rb_need_block();
315
- VALUE block = rb_block_proc();
316
- IodineStore.add(block);
317
- fio_state_callback_add(FIO_CALL_AFTER_FORK,
318
- iodine_perform_state_callback_persist, (void *)block);
319
- return block;
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 iodine_after_fork_in_worker_add(VALUE self) {
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
- fio_state_callback_add(FIO_CALL_IN_CHILD,
335
- iodine_perform_state_callback_persist, (void *)block);
336
- return block;
337
- (void)self;
338
- }
339
-
340
- // clang-format off
341
- /**
342
- Sets a block of code to run after a new worker process is forked (cluster mode only).
343
-
344
- Code runs in both the parent and the child.
345
- */
346
- static VALUE iodine_after_fork_in_master_add(VALUE self) {
347
- // clang-format on
348
- rb_need_block();
349
- VALUE block = rb_block_proc();
350
- IodineStore.add(block);
351
- fio_state_callback_add(FIO_CALL_IN_MASTER,
352
- iodine_perform_state_callback_persist, (void *)block);
353
- return block;
354
- (void)self;
355
- }
356
-
357
- // clang-format off
358
- /**
359
- Sets a block of code to run once a Worker process shuts down (both in single process mode and cluster mode).
360
- */
361
- static VALUE iodine_on_shutdown_add(VALUE self) {
362
- // clang-format on
363
- rb_need_block();
364
- VALUE block = rb_block_proc();
365
- IodineStore.add(block);
366
- fio_state_callback_add(FIO_CALL_ON_FINISH,
367
- iodine_perform_state_callback_persist, (void *)block);
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, "before_fork", iodine_before_fork_add,
392
- 0);
393
- rb_define_module_function(IodineModule, "after_fork", iodine_after_fork_add,
394
- 0);
395
- rb_define_module_function(IodineModule, "after_fork_in_worker",
396
- iodine_after_fork_in_worker_add, 0);
397
- rb_define_module_function(IodineModule, "after_fork_in_master",
398
- iodine_after_fork_in_master_add, 0);
399
- rb_define_module_function(IodineModule, "on_shutdown", iodine_on_shutdown_add,
400
- 0);
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
  }
@@ -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
- static VALUE iodine_default_args;
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
 
@@ -12,7 +12,7 @@ Feel free to copy, use and enjoy according to the license provided.
12
12
  extern VALUE IODINE_R_HIJACK;
13
13
  extern VALUE IODINE_R_HIJACK_IO;
14
14
  extern VALUE IODINE_R_HIJACK_CB;
15
-
15
+ extern VALUE iodine_default_args;
16
16
  void iodine_init_http(void);
17
17
 
18
18
  #endif
@@ -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}, {on_shutdown}, {before_fork} and {after_fork}.
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
- require 'rack/handler/iodine' unless defined? ::Iodine::Rack::IODINE_RACK_LOADED
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
- ### CLI argument parsing
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
- if ARGV.index('-maxbd') && ARGV[ARGV.index('-maxbd') + 1]
89
- Iodine::DEFAULT_HTTP_ARGS[:max_body_size] = ARGV[ARGV.index('-maxbd') + 1].to_i
90
- end
91
- if ARGV.index('-maxms') && ARGV[ARGV.index('-maxms') + 1]
92
- Iodine::DEFAULT_HTTP_ARGS[:max_msg_size] = ARGV[ARGV.index('-maxms') + 1].to_i
93
- end
94
- if ARGV.index('-maxhead') && ARGV[ARGV.index('-maxhead') + 1] && ARGV[ARGV.index('-maxhead') + 1].to_i > 0
95
- Iodine::DEFAULT_HTTP_ARGS[:max_headers] = ARGV[ARGV.index('-maxhead') + 1].to_i
96
- end
97
- if ARGV.index('-ping') && ARGV[ARGV.index('-ping') + 1]
98
- Iodine::DEFAULT_HTTP_ARGS[:ping] = ARGV[ARGV.index('-ping') + 1].to_i
99
- end
100
- if ARGV.index('-www') && ARGV[ARGV.index('-www') + 1]
101
- Iodine::DEFAULT_HTTP_ARGS[:public] = ARGV[ARGV.index('-www') + 1]
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
- if ARGV.index('-tout') && ARGV[ARGV.index('-tout') + 1]
104
- Iodine::DEFAULT_HTTP_ARGS[:timeout] = ARGV[ARGV.index('-tout') + 1].to_i
105
- puts "WARNNING: timeout set to 0 (ignored, timeout will be ~5 seconds)." if (Iodine::DEFAULT_HTTP_ARGS[:timeout].to_i <= 0 || Iodine::DEFAULT_HTTP_ARGS[:timeout].to_i > 255)
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
- if ARGV.index('-logging') && ARGV[ARGV.index('-logging') + 1] && ARGV[ARGV.index('-logging') + 1].to_s[0] >= '0' && ARGV[ARGV.index('-logging') + 1].to_s[0] <= '9'
110
- Iodine.verbosity = ARGV[ARGV.index('-logging') + 1].to_i
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
- if ARGV.index('-t') && ARGV[ARGV.index('-t') + 1].to_i != 0
114
- Iodine.threads = ARGV[ARGV.index('-t') + 1].to_i
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(*args, &block)
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.after_fork_in_worker(*args, &block)
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.after_fork_in_master(*args, &block)
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(*args, &block)
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(*args, &block)
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(*args, &block)
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
- Iodine.after_fork_in_worker do
160
- ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
161
- end
205
+
162
206
 
163
207
 
164
208
 
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.7.8'.freeze
2
+ VERSION = '0.7.9'.freeze
3
3
  end
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.8
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-12 00:00:00.000000000 Z
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.8.
247
+ post_install_message: 'Thank you for installing Iodine 0.7.9.
248
248
 
249
249
  '
250
250
  rdoc_options: []