iodine 0.4.19 → 0.5.0
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/.travis.yml +1 -2
- data/CHANGELOG.md +22 -0
- data/LIMITS.md +19 -9
- data/README.md +92 -77
- data/SPEC-PubSub-Draft.md +113 -0
- data/SPEC-Websocket-Draft.md +127 -143
- data/bin/http-hello +0 -1
- data/bin/raw-rbhttp +1 -1
- data/bin/raw_broadcast +8 -10
- data/bin/updated api +2 -2
- data/bin/ws-broadcast +2 -4
- data/bin/ws-echo +2 -2
- data/examples/config.ru +13 -13
- data/examples/echo.ru +5 -6
- data/examples/hello.ru +2 -3
- data/examples/info.md +316 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/redis.ru +9 -9
- data/examples/shootout.ru +45 -11
- data/ext/iodine/defer.c +194 -297
- data/ext/iodine/defer.h +61 -53
- data/ext/iodine/evio.c +0 -260
- data/ext/iodine/evio.h +50 -22
- data/ext/iodine/evio_callbacks.c +26 -0
- data/ext/iodine/evio_epoll.c +251 -0
- data/ext/iodine/evio_kqueue.c +193 -0
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +1420 -542
- data/ext/iodine/facil.h +151 -64
- data/ext/iodine/fio_ary.h +418 -0
- data/ext/iodine/{base64.c → fio_base64.c} +33 -24
- data/ext/iodine/{base64.h → fio_base64.h} +6 -7
- data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
- data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
- data/ext/iodine/fio_hashmap.h +759 -0
- data/ext/iodine/fio_json_parser.h +651 -0
- data/ext/iodine/fio_llist.h +257 -0
- data/ext/iodine/fio_mem.c +672 -0
- data/ext/iodine/fio_mem.h +140 -0
- data/ext/iodine/fio_random.c +248 -0
- data/ext/iodine/{random.h → fio_random.h} +11 -14
- data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
- data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
- data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
- data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
- data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
- data/ext/iodine/fio_siphash.h +18 -0
- data/ext/iodine/fio_tmpfile.h +38 -0
- data/ext/iodine/fiobj.h +24 -7
- data/ext/iodine/fiobj4sock.h +23 -0
- data/ext/iodine/fiobj_ary.c +143 -226
- data/ext/iodine/fiobj_ary.h +17 -16
- data/ext/iodine/fiobj_data.c +1160 -0
- data/ext/iodine/fiobj_data.h +164 -0
- data/ext/iodine/fiobj_hash.c +298 -406
- data/ext/iodine/fiobj_hash.h +101 -54
- data/ext/iodine/fiobj_json.c +478 -601
- data/ext/iodine/fiobj_json.h +34 -9
- data/ext/iodine/fiobj_numbers.c +383 -51
- data/ext/iodine/fiobj_numbers.h +87 -11
- data/ext/iodine/fiobj_str.c +423 -184
- data/ext/iodine/fiobj_str.h +81 -32
- data/ext/iodine/fiobject.c +273 -522
- data/ext/iodine/fiobject.h +477 -112
- data/ext/iodine/http.c +2243 -83
- data/ext/iodine/http.h +842 -121
- data/ext/iodine/http1.c +810 -385
- data/ext/iodine/http1.h +16 -39
- data/ext/iodine/http1_parser.c +146 -74
- data/ext/iodine/http1_parser.h +15 -4
- data/ext/iodine/http_internal.c +1258 -0
- data/ext/iodine/http_internal.h +226 -0
- data/ext/iodine/http_mime_parser.h +341 -0
- data/ext/iodine/iodine.c +86 -68
- data/ext/iodine/iodine.h +26 -11
- data/ext/iodine/iodine_helpers.c +8 -7
- data/ext/iodine/iodine_http.c +487 -324
- data/ext/iodine/iodine_json.c +304 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_protocol.c +107 -45
- data/ext/iodine/iodine_pubsub.c +526 -225
- data/ext/iodine/iodine_pubsub.h +10 -0
- data/ext/iodine/iodine_websockets.c +268 -510
- data/ext/iodine/iodine_websockets.h +2 -4
- data/ext/iodine/pubsub.c +726 -432
- data/ext/iodine/pubsub.h +85 -103
- data/ext/iodine/rb-call.c +4 -4
- data/ext/iodine/rb-defer.c +46 -22
- data/ext/iodine/rb-fiobj2rb.h +117 -0
- data/ext/iodine/rb-rack-io.c +73 -238
- data/ext/iodine/rb-rack-io.h +2 -2
- data/ext/iodine/rb-registry.c +35 -93
- data/ext/iodine/rb-registry.h +1 -0
- data/ext/iodine/redis_engine.c +742 -304
- data/ext/iodine/redis_engine.h +42 -39
- data/ext/iodine/resp_parser.h +311 -0
- data/ext/iodine/sock.c +627 -490
- data/ext/iodine/sock.h +345 -297
- data/ext/iodine/spnlock.inc +15 -4
- data/ext/iodine/websocket_parser.h +16 -20
- data/ext/iodine/websockets.c +188 -257
- data/ext/iodine/websockets.h +24 -133
- data/lib/iodine.rb +52 -7
- data/lib/iodine/cli.rb +6 -24
- data/lib/iodine/json.rb +40 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +5 -3
- data/lib/rack/handler/iodine.rb +58 -13
- metadata +38 -48
- data/bin/ws-shootout +0 -107
- data/examples/broadcast.ru +0 -56
- data/ext/iodine/bscrypt-common.h +0 -116
- data/ext/iodine/bscrypt.h +0 -49
- data/ext/iodine/fio2resp.c +0 -60
- data/ext/iodine/fio2resp.h +0 -51
- data/ext/iodine/fio_dict.c +0 -446
- data/ext/iodine/fio_dict.h +0 -99
- data/ext/iodine/fio_hash_table.h +0 -370
- data/ext/iodine/fio_list.h +0 -111
- data/ext/iodine/fiobj_internal.h +0 -280
- data/ext/iodine/fiobj_primitives.c +0 -131
- data/ext/iodine/fiobj_primitives.h +0 -55
- data/ext/iodine/fiobj_sym.c +0 -135
- data/ext/iodine/fiobj_sym.h +0 -60
- data/ext/iodine/hex.c +0 -124
- data/ext/iodine/hex.h +0 -70
- data/ext/iodine/http1_request.c +0 -81
- data/ext/iodine/http1_request.h +0 -58
- data/ext/iodine/http1_response.c +0 -417
- data/ext/iodine/http1_response.h +0 -95
- data/ext/iodine/http_request.c +0 -111
- data/ext/iodine/http_request.h +0 -102
- data/ext/iodine/http_response.c +0 -1703
- data/ext/iodine/http_response.h +0 -250
- data/ext/iodine/misc.c +0 -182
- data/ext/iodine/misc.h +0 -74
- data/ext/iodine/random.c +0 -208
- data/ext/iodine/redis_connection.c +0 -278
- data/ext/iodine/redis_connection.h +0 -86
- data/ext/iodine/resp.c +0 -842
- data/ext/iodine/resp.h +0 -261
- data/ext/iodine/siphash.c +0 -154
- data/ext/iodine/siphash.h +0 -22
- data/ext/iodine/xor-crypt.c +0 -193
- data/ext/iodine/xor-crypt.h +0 -107
data/ext/iodine/facil.h
CHANGED
@@ -10,20 +10,73 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
10
10
|
*/
|
11
11
|
#define H_FACIL_H
|
12
12
|
#define FACIL_VERSION_MAJOR 0
|
13
|
-
#define FACIL_VERSION_MINOR
|
14
|
-
#define FACIL_VERSION_PATCH
|
13
|
+
#define FACIL_VERSION_MINOR 6
|
14
|
+
#define FACIL_VERSION_PATCH 4
|
15
15
|
|
16
16
|
#ifndef FACIL_PRINT_STATE
|
17
17
|
/**
|
18
|
-
When FACIL_PRINT_STATE is set to 1, facil.io will print out common messages
|
19
|
-
regarding the server state (start / finish / listen messages).
|
20
|
-
*/
|
18
|
+
* When FACIL_PRINT_STATE is set to 1, facil.io will print out common messages
|
19
|
+
* regarding the server state (start / finish / listen messages).
|
20
|
+
*/
|
21
21
|
#define FACIL_PRINT_STATE 1
|
22
22
|
#endif
|
23
|
+
|
24
|
+
#ifndef FACIL_CPU_CORES_LIMIT
|
25
|
+
/**
|
26
|
+
* If facil.io detects more CPU cores than the number of cores stated in the
|
27
|
+
* FACIL_CPU_CORES_LIMIT, it will assume an error and cap the number of cores
|
28
|
+
* detected to the assigned limit.
|
29
|
+
*
|
30
|
+
* This is only relevant to automated values, when running facil.io with zero
|
31
|
+
* threads and processes, which invokes a large matrix of workers and threads
|
32
|
+
* (see {facil_run})
|
33
|
+
*
|
34
|
+
* The default auto-detection cap is set at 8 cores. The number is arbitrary
|
35
|
+
* (historically the number 7 was used after testing `malloc` race conditions on
|
36
|
+
* a MacBook Pro).
|
37
|
+
*
|
38
|
+
* The does NOT effect manually set (non-zero) worker/thread values.
|
39
|
+
*/
|
40
|
+
#define FACIL_CPU_CORES_LIMIT 8
|
41
|
+
#endif
|
42
|
+
|
43
|
+
#ifndef FIO_DEDICATED_SYSTEM
|
44
|
+
/**
|
45
|
+
* If FIO_DEDICATED_SYSTEM is false, threads will be used (mostly) for
|
46
|
+
* non-prallel concurrency (protection against slow user code / high load) and
|
47
|
+
* processes will be used for parallelism. Otherwise, both threads and processes
|
48
|
+
* will be used for prallel concurrency (at the expense of increased polling).
|
49
|
+
*
|
50
|
+
* If FIO_DEDICATED_SYSTEM is true, facil.io assumes that the whole system is at
|
51
|
+
* it's service and that no other process is using the CPU cores.
|
52
|
+
*
|
53
|
+
* Accordingly, facil.io will poll the IO more often in an attempt to activate
|
54
|
+
* the threads and utilize all the cores whenever events occur.
|
55
|
+
*
|
56
|
+
* My tests show that the non-polling approach is faster, but it may be system
|
57
|
+
* specific.
|
58
|
+
*/
|
59
|
+
#define FIO_DEDICATED_SYSTEM 0
|
60
|
+
#endif
|
61
|
+
|
62
|
+
#ifndef FACIL_DISABLE_HOT_RESTART
|
63
|
+
/**
|
64
|
+
* Disables the hot restart reaction to the SIGUSR1 signal
|
65
|
+
*
|
66
|
+
* The hot restart will attempt to shut down all workers, and spawn new workers,
|
67
|
+
* cleaning up any data cached by any of the workers.
|
68
|
+
*
|
69
|
+
* It's quite useless unless the workers are running their own VMs which are
|
70
|
+
* initialized in the listening socket's `on_start` callback.
|
71
|
+
*/
|
72
|
+
#define FACIL_DISABLE_HOT_RESTART 0
|
73
|
+
#endif
|
74
|
+
|
23
75
|
/* *****************************************************************************
|
24
76
|
Required facil libraries
|
25
77
|
***************************************************************************** */
|
26
78
|
#include "defer.h"
|
79
|
+
#include "fiobj.h"
|
27
80
|
#include "sock.h"
|
28
81
|
|
29
82
|
/* support C++ */
|
@@ -60,14 +113,19 @@ struct FacilIOProtocol {
|
|
60
113
|
* used (not `strcmp`).
|
61
114
|
*/
|
62
115
|
const char *service;
|
63
|
-
/**
|
116
|
+
/** Called when a data is available, but will not run concurrently */
|
64
117
|
void (*on_data)(intptr_t uuid, protocol_s *protocol);
|
65
118
|
/** called when the socket is ready to be written to. */
|
66
119
|
void (*on_ready)(intptr_t uuid, protocol_s *protocol);
|
67
|
-
/**
|
68
|
-
*
|
120
|
+
/**
|
121
|
+
* Called when the server is shutting down, immediately before closing the
|
122
|
+
* connection.
|
123
|
+
*
|
124
|
+
* The callback runs within a {FIO_PR_LOCK_TACK} lock, so it will never run
|
125
|
+
* concurrently wil {on_data} or other connection specific tasks.
|
126
|
+
*/
|
69
127
|
void (*on_shutdown)(intptr_t uuid, protocol_s *protocol);
|
70
|
-
/**
|
128
|
+
/** Called when the connection was closed, but will not run concurrently */
|
71
129
|
void (*on_close)(intptr_t uuid, protocol_s *protocol);
|
72
130
|
/** called when a connection's timeout was reached */
|
73
131
|
void (*ping)(intptr_t uuid, protocol_s *protocol);
|
@@ -144,8 +202,10 @@ static protocol_s *echo_on_open(intptr_t uuid, void *udata) {
|
|
144
202
|
|
145
203
|
int main() {
|
146
204
|
// Setup a listening socket
|
147
|
-
if (facil_listen(.port = "8888", .on_open = echo_on_open))
|
148
|
-
|
205
|
+
if (facil_listen(.port = "8888", .on_open = echo_on_open)) {
|
206
|
+
perror("No listening socket available on port 8888");
|
207
|
+
exit(-1);
|
208
|
+
}
|
149
209
|
// Run the server and hang until a stop signal is received.
|
150
210
|
facil_run(.threads = 4, .processes = 1);
|
151
211
|
}
|
@@ -155,40 +215,30 @@ int main() {
|
|
155
215
|
struct facil_listen_args {
|
156
216
|
/**
|
157
217
|
* Called whenever a new connection is accepted.
|
158
|
-
*
|
218
|
+
*
|
219
|
+
* Should either call `facil_attach` or close the connection.
|
159
220
|
*/
|
160
|
-
|
221
|
+
void (*on_open)(intptr_t fduuid, void *udata);
|
161
222
|
/** The network service / port. Defaults to "3000". */
|
162
223
|
const char *port;
|
163
224
|
/** The socket binding address. Defaults to the recommended NULL. */
|
164
225
|
const char *address;
|
165
226
|
/** Opaque user data. */
|
166
227
|
void *udata;
|
167
|
-
/** Opaque user data for `set_rw_hooks`. */
|
168
|
-
void *rw_udata;
|
169
|
-
/** (optional)
|
170
|
-
* Called before `on_open`, allowing Read/Write hook initialization.
|
171
|
-
* Should return a pointer to the new RW hooks or NULL (to use default hooks).
|
172
|
-
*
|
173
|
-
* This allows a seperation between the transport layer (i.e. TLS) and the
|
174
|
-
* protocol (i.e. HTTP).
|
175
|
-
*/
|
176
|
-
sock_rw_hook_s *(*set_rw_hooks)(intptr_t fduuid, void *rw_udata);
|
177
228
|
/**
|
178
|
-
* Called when the server starts
|
179
|
-
* timed event scheduling
|
229
|
+
* Called when the server starts (or a worker process is respawned), allowing
|
230
|
+
* for further initialization, such as timed event scheduling or VM
|
231
|
+
* initialization.
|
180
232
|
*
|
181
|
-
* This will be called seperately for every process
|
233
|
+
* This will be called seperately for every worker process whenever it is
|
234
|
+
* spawned.
|
235
|
+
*/
|
182
236
|
void (*on_start)(intptr_t uuid, void *udata);
|
183
237
|
/**
|
184
238
|
* Called when the server is done, usable for cleanup.
|
185
239
|
*
|
186
240
|
* This will be called seperately for every process. */
|
187
241
|
void (*on_finish)(intptr_t uuid, void *udata);
|
188
|
-
/**
|
189
|
-
* A cleanup callback for the `rw_udata`.
|
190
|
-
*/
|
191
|
-
void (*on_finish_rw)(intptr_t uuid, void *rw_udata);
|
192
242
|
};
|
193
243
|
|
194
244
|
/** Schedule a network service on a listening socket. */
|
@@ -217,8 +267,10 @@ struct facil_connect_args {
|
|
217
267
|
/**
|
218
268
|
* The `on_connect` callback should return a pointer to a protocol object
|
219
269
|
* that will handle any connection related events.
|
270
|
+
*
|
271
|
+
* Should either call `facil_attach` or close the connection.
|
220
272
|
*/
|
221
|
-
|
273
|
+
void (*on_connect)(intptr_t uuid, void *udata);
|
222
274
|
/**
|
223
275
|
* The `on_fail` is called when a socket fails to connect. The old sock UUID
|
224
276
|
* is passed along.
|
@@ -226,16 +278,8 @@ struct facil_connect_args {
|
|
226
278
|
void (*on_fail)(intptr_t uuid, void *udata);
|
227
279
|
/** Opaque user data. */
|
228
280
|
void *udata;
|
229
|
-
/**
|
230
|
-
|
231
|
-
/** (optional)
|
232
|
-
* Called before `on_connect`, allowing Read/Write hook initialization.
|
233
|
-
* Should return a pointer to the new RW hooks or NULL (to use default hooks).
|
234
|
-
*
|
235
|
-
* This allows a seperation between the transport layer (i.e. TLS) and the
|
236
|
-
* protocol (i.e. HTTP).
|
237
|
-
*/
|
238
|
-
sock_rw_hook_s *(*set_rw_hooks)(intptr_t fduuid, void *udata);
|
281
|
+
/** A non-system timeout after which connection is assumed to have failed. */
|
282
|
+
uint8_t timeout;
|
239
283
|
};
|
240
284
|
|
241
285
|
/**
|
@@ -267,10 +311,26 @@ Core API
|
|
267
311
|
***************************************************************************** */
|
268
312
|
|
269
313
|
struct facil_run_args {
|
270
|
-
/**
|
271
|
-
|
272
|
-
|
273
|
-
|
314
|
+
/**
|
315
|
+
* The number of threads to run in the thread pool. Has "smart" defaults.
|
316
|
+
*
|
317
|
+
*
|
318
|
+
* A positive value will indicate a set number of threads (or processes).
|
319
|
+
*
|
320
|
+
* Zeros and negative values are fun and include an interesting shorthand:
|
321
|
+
*
|
322
|
+
* * Negative values indicate a fraction of the number of CPU cores. i.e.
|
323
|
+
* -2 will normally indicate "half" (1/2) the number of cores.
|
324
|
+
*
|
325
|
+
* * If the other option (i.e. `.processes` when setting `.threads`) is zero,
|
326
|
+
* it will be automatically updated to reflect the option's absolute value.
|
327
|
+
* i.e.:
|
328
|
+
* if .threads == -2 and .processes == 0,
|
329
|
+
* than facil.io will run 2 processes with (cores/2) threads per process.
|
330
|
+
*/
|
331
|
+
int16_t threads;
|
332
|
+
/** The number of processes to run (including this one). See `threads`. */
|
333
|
+
int16_t processes;
|
274
334
|
/** called if the event loop in cycled with no pending events. */
|
275
335
|
void (*on_idle)(void);
|
276
336
|
/** called when the server is done, to clean up any leftovers. */
|
@@ -290,13 +350,21 @@ void facil_run(struct facil_run_args args);
|
|
290
350
|
*/
|
291
351
|
int facil_is_running(void);
|
292
352
|
|
353
|
+
/**
|
354
|
+
OVERRIDE THIS to replace the default `fork` implementation or to inject hooks
|
355
|
+
into the forking function.
|
356
|
+
|
357
|
+
Behaves like the system's `fork`.
|
358
|
+
*/
|
359
|
+
int facil_fork(void);
|
360
|
+
|
293
361
|
/**
|
294
362
|
* Attaches (or updates) a protocol object to a socket UUID.
|
295
363
|
*
|
296
|
-
* The new protocol object can be NULL, which will
|
297
|
-
* socket
|
364
|
+
* The new protocol object can be NULL, which will detach ("hijack"), the
|
365
|
+
* socket.
|
298
366
|
*
|
299
|
-
* The old protocol's `on_close` will be scheduled
|
367
|
+
* The old protocol's `on_close` (if any) will be scheduled.
|
300
368
|
*
|
301
369
|
* Returns -1 on error and 0 on success.
|
302
370
|
*
|
@@ -310,10 +378,7 @@ int facil_attach(intptr_t uuid, protocol_s *protocol);
|
|
310
378
|
* The protocol will be attached in the FIO_PR_LOCK_TASK state, requiring a
|
311
379
|
* furthur call to `facil_protocol_unlock`.
|
312
380
|
*
|
313
|
-
* The
|
314
|
-
* socket away from facil.
|
315
|
-
*
|
316
|
-
* The old protocol's `on_close` will be scheduled, if they both exist.
|
381
|
+
* The old protocol's `on_close` (if any) will be scheduled.
|
317
382
|
*
|
318
383
|
* Returns -1 on error and 0 on success.
|
319
384
|
*
|
@@ -335,14 +400,33 @@ enum facil_io_event {
|
|
335
400
|
/** Schedules an IO event, even id it did not occur. */
|
336
401
|
void facil_force_event(intptr_t uuid, enum facil_io_event);
|
337
402
|
|
403
|
+
/**
|
404
|
+
* Temporarily prevents `on_data` events from firing.
|
405
|
+
*
|
406
|
+
* The `on_data` event will be automatically rescheduled when (if) the socket's
|
407
|
+
* outgoing buffer fills up or when `facil_force_event` is called with
|
408
|
+
* `FIO_EVENT_ON_DATA`.
|
409
|
+
*
|
410
|
+
* Note: the function will work as expected when called within the protocol's
|
411
|
+
* `on_data` callback and the `uuid` refers to a valid socket. Otherwise the
|
412
|
+
* function might quitely fail.
|
413
|
+
*/
|
414
|
+
void facil_quite(intptr_t uuid);
|
415
|
+
|
338
416
|
/* *****************************************************************************
|
339
417
|
Helper API
|
340
418
|
***************************************************************************** */
|
341
419
|
|
342
420
|
/**
|
343
|
-
|
344
|
-
|
345
|
-
|
421
|
+
* Initializes zombie reaping for the process. Call before `facil_run` to enable
|
422
|
+
* global zombie reaping.
|
423
|
+
*/
|
424
|
+
void facil_reap_children(void);
|
425
|
+
|
426
|
+
/**
|
427
|
+
* Returns the last time the server reviewed any pending IO events.
|
428
|
+
*/
|
429
|
+
struct timespec facil_last_tick(void);
|
346
430
|
|
347
431
|
/** Counts all the connections of a specific type `service`. */
|
348
432
|
size_t facil_count(void *service);
|
@@ -392,7 +476,7 @@ struct facil_defer_args_s {
|
|
392
476
|
intptr_t uuid;
|
393
477
|
/** The type of task to be performed. Defaults to `FIO_PR_LOCK_TASK` but could
|
394
478
|
* also be seto to `FIO_PR_LOCK_WRITE`. */
|
395
|
-
enum facil_protocol_lock_e
|
479
|
+
enum facil_protocol_lock_e type;
|
396
480
|
/** The task (function) to be performed. This is required. */
|
397
481
|
void (*task)(intptr_t uuid, protocol_s *, void *arg);
|
398
482
|
/** An opaque user data that will be passed along to the task. */
|
@@ -441,11 +525,14 @@ int facil_each(struct facil_each_args_s args);
|
|
441
525
|
#define facil_each(...) facil_each((struct facil_each_args_s){__VA_ARGS__})
|
442
526
|
|
443
527
|
/* *****************************************************************************
|
444
|
-
Cluster specific API - local cluster messaging.
|
528
|
+
* Cluster specific API - local cluster messaging.
|
529
|
+
*
|
530
|
+
* Facil supports message process clustering, so that a multi-process
|
531
|
+
* application can easily send and receive messages across process boundries.
|
532
|
+
**************************************************************************** */
|
445
533
|
|
446
|
-
|
447
|
-
|
448
|
-
***************************************************************************** */
|
534
|
+
/** returns facil.io's parent (root) process pid. */
|
535
|
+
pid_t facil_parent_pid(void);
|
449
536
|
|
450
537
|
/**
|
451
538
|
Sets a callback / handler for a message of type `msg_type`.
|
@@ -456,9 +543,9 @@ registered callbacks.
|
|
456
543
|
The `msg_type` value can be any positive number up to 2^31-1 (2,147,483,647).
|
457
544
|
All values less than 0 are reserved for internal use.
|
458
545
|
*/
|
459
|
-
void facil_cluster_set_handler(int32_t
|
460
|
-
void (*on_message)(
|
461
|
-
|
546
|
+
void facil_cluster_set_handler(int32_t filter,
|
547
|
+
void (*on_message)(int32_t filter, FIOBJ ch,
|
548
|
+
FIOBJ msg));
|
462
549
|
/** Sends a message of type `msg_type` to the **other** cluster processes.
|
463
550
|
|
464
551
|
`msg_type` should match a message type used when calling
|
@@ -472,7 +559,7 @@ starting at 1,073,741,824 are reserved for internal use.
|
|
472
559
|
Callbacks are invoked using an O(n) matching, where `n` is the number of
|
473
560
|
registered callbacks.
|
474
561
|
*/
|
475
|
-
int facil_cluster_send(int32_t
|
562
|
+
int facil_cluster_send(int32_t filter, FIOBJ ch, FIOBJ msg);
|
476
563
|
|
477
564
|
/* *****************************************************************************
|
478
565
|
Lower Level API - for special circumstances, use with care under .
|
@@ -0,0 +1,418 @@
|
|
1
|
+
#ifndef H_FIO_ARRAY_H
|
2
|
+
/*
|
3
|
+
Copyright: Boaz Segev, 2017-2018
|
4
|
+
License: MIT
|
5
|
+
*/
|
6
|
+
|
7
|
+
/**
|
8
|
+
* A Dynamic Array for general use (void * pointers).
|
9
|
+
*
|
10
|
+
* The file was written to be compatible with C++ as well as C, hence some
|
11
|
+
* pointer casting.
|
12
|
+
*
|
13
|
+
* Use:
|
14
|
+
|
15
|
+
fio_ary_s ary; // a container can be placed on the stack.
|
16
|
+
fio_ary_new(&ary); // initialize the container
|
17
|
+
fio_ary_push(&ary, (void*)1 ); // add / remove / read data...
|
18
|
+
fio_ary_free(&ary) // free any resources, not the container.
|
19
|
+
|
20
|
+
|
21
|
+
*/
|
22
|
+
#define H_FIO_ARRAY_H
|
23
|
+
|
24
|
+
#include <errno.h>
|
25
|
+
#include <stdint.h>
|
26
|
+
#include <stdio.h>
|
27
|
+
#include <stdlib.h>
|
28
|
+
#include <string.h>
|
29
|
+
#ifndef _GNU_SOURCE
|
30
|
+
#define _GNU_SOURCE
|
31
|
+
#endif
|
32
|
+
|
33
|
+
#ifndef FIO_FUNC
|
34
|
+
#define FIO_FUNC static __attribute__((unused))
|
35
|
+
#endif
|
36
|
+
|
37
|
+
#ifdef __cplusplus
|
38
|
+
#define register
|
39
|
+
#endif
|
40
|
+
|
41
|
+
typedef struct fio_ary_s {
|
42
|
+
size_t start;
|
43
|
+
size_t end;
|
44
|
+
size_t capa;
|
45
|
+
void **arry;
|
46
|
+
} fio_ary_s;
|
47
|
+
|
48
|
+
/** this value can be used for lazy initialization. */
|
49
|
+
#define FIO_ARY_INIT ((fio_ary_s){.arry = NULL})
|
50
|
+
|
51
|
+
/* *****************************************************************************
|
52
|
+
Array API
|
53
|
+
***************************************************************************** */
|
54
|
+
|
55
|
+
/** Initializes the array and allocates memory for it's internal data. */
|
56
|
+
FIO_FUNC inline void fio_ary_new(fio_ary_s *ary, size_t capa);
|
57
|
+
|
58
|
+
/** Frees the array's internal data. */
|
59
|
+
FIO_FUNC inline void fio_ary_free(fio_ary_s *ary);
|
60
|
+
|
61
|
+
/** Returns the number of elements in the Array. */
|
62
|
+
FIO_FUNC inline size_t fio_ary_count(fio_ary_s *ary);
|
63
|
+
|
64
|
+
/** Returns the current, temporary, array capacity (it's dynamic). */
|
65
|
+
FIO_FUNC inline size_t fio_ary_capa(fio_ary_s *ary);
|
66
|
+
|
67
|
+
/**
|
68
|
+
* Returns the object placed in the Array, if any. Returns NULL if no data or if
|
69
|
+
* the index is out of bounds.
|
70
|
+
*
|
71
|
+
* Negative values are retrived from the end of the array. i.e., `-1`
|
72
|
+
* is the last item.
|
73
|
+
*/
|
74
|
+
FIO_FUNC inline void *fio_ary_index(fio_ary_s *ary, intptr_t pos);
|
75
|
+
|
76
|
+
/** alias for `fiobj_ary_index` */
|
77
|
+
#define fio_ary_entry(a, p) fiobj_ary_index((a), (p))
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Sets an object at the requested position.
|
81
|
+
*
|
82
|
+
* Returns the old value, if any.
|
83
|
+
*
|
84
|
+
* If an error occurs, the same data passed to the function is returned.
|
85
|
+
*/
|
86
|
+
FIO_FUNC inline void *fio_ary_set(fio_ary_s *ary, void *data, intptr_t pos);
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Pushes an object to the end of the Array. Returns -1 on error.
|
90
|
+
*/
|
91
|
+
FIO_FUNC inline int fio_ary_push(fio_ary_s *ary, void *data);
|
92
|
+
|
93
|
+
/** Pops an object from the end of the Array. */
|
94
|
+
FIO_FUNC inline void *fio_ary_pop(fio_ary_s *ary);
|
95
|
+
|
96
|
+
/**
|
97
|
+
* Unshifts an object to the beginning of the Array. Returns -1 on error.
|
98
|
+
*
|
99
|
+
* This could be expensive, causing `memmove`.
|
100
|
+
*/
|
101
|
+
FIO_FUNC inline int fio_ary_unshift(fio_ary_s *ary, void *data);
|
102
|
+
|
103
|
+
/** Shifts an object from the beginning of the Array. */
|
104
|
+
FIO_FUNC inline void *fio_ary_shift(fio_ary_s *ary);
|
105
|
+
|
106
|
+
/**
|
107
|
+
* Iteration using a callback for each entry in the array.
|
108
|
+
*
|
109
|
+
* The callback task function must accept an the entry data as well as an opaque
|
110
|
+
* user pointer.
|
111
|
+
*
|
112
|
+
* If the callback returns -1, the loop is broken. Any other value is ignored.
|
113
|
+
*
|
114
|
+
* Returns the relative "stop" position, i.e., the number of items processed +
|
115
|
+
* the starting point.
|
116
|
+
*/
|
117
|
+
FIO_FUNC inline size_t fio_ary_each(fio_ary_s *ary, size_t start_at,
|
118
|
+
int (*task)(void *pt, void *arg),
|
119
|
+
void *arg);
|
120
|
+
/**
|
121
|
+
* Removes any NULL *pointers* from an Array, keeping all other data in the
|
122
|
+
* array.
|
123
|
+
*
|
124
|
+
* This action is O(n) where n in the length of the array.
|
125
|
+
* It could get expensive.
|
126
|
+
*/
|
127
|
+
FIO_FUNC inline void fio_ary_compact(fio_ary_s *ary);
|
128
|
+
|
129
|
+
/**
|
130
|
+
* Iterates through the list using a `for` loop.
|
131
|
+
*
|
132
|
+
* Access the data with `pos.obj` and it's index with `pos.i`. the `pos`
|
133
|
+
* variable can be named however you please.
|
134
|
+
*/
|
135
|
+
#define FIO_ARY_FOR(ary, pos) \
|
136
|
+
if ((ary)->arry) \
|
137
|
+
for (struct fio_ary_pos_for_loop_s pos = {0, (ary)->arry[(ary)->start]}; \
|
138
|
+
(pos.i + (ary)->start) < (ary)->end && \
|
139
|
+
((pos.obj = (ary)->arry[pos.i + (ary)->start]), 1); \
|
140
|
+
(++pos.i))
|
141
|
+
struct fio_ary_pos_for_loop_s {
|
142
|
+
unsigned long i;
|
143
|
+
void *obj;
|
144
|
+
};
|
145
|
+
|
146
|
+
/* *****************************************************************************
|
147
|
+
Array creation API
|
148
|
+
***************************************************************************** */
|
149
|
+
|
150
|
+
FIO_FUNC inline void fio_ary_new(fio_ary_s *ary, size_t capa) {
|
151
|
+
if (!capa)
|
152
|
+
capa = 32;
|
153
|
+
*ary = (fio_ary_s){.arry = (void **)malloc(capa * sizeof(*ary->arry)),
|
154
|
+
.capa = capa};
|
155
|
+
if (!ary->arry) {
|
156
|
+
perror("ERROR: facil.io dynamic array couldn't be allocated");
|
157
|
+
exit(errno);
|
158
|
+
}
|
159
|
+
}
|
160
|
+
FIO_FUNC inline void fio_ary_free(fio_ary_s *ary) {
|
161
|
+
if (ary)
|
162
|
+
free(ary->arry);
|
163
|
+
}
|
164
|
+
|
165
|
+
/* *****************************************************************************
|
166
|
+
Array memory management
|
167
|
+
***************************************************************************** */
|
168
|
+
|
169
|
+
/* This funcation manages the Array's memory. */
|
170
|
+
FIO_FUNC void fio_ary_getmem(fio_ary_s *ary, intptr_t needed) {
|
171
|
+
/* we have enough memory, but we need to re-organize it. */
|
172
|
+
if (needed == -1) {
|
173
|
+
if (ary->end < ary->capa) {
|
174
|
+
/* since allocation can be cheaper than memmove (depending on size),
|
175
|
+
* we'll just shove everything to the end...
|
176
|
+
*/
|
177
|
+
size_t len = ary->end - ary->start;
|
178
|
+
memmove(ary->arry + ary->capa - len, ary->arry + ary->start,
|
179
|
+
len * sizeof(*ary->arry));
|
180
|
+
ary->start = ary->capa - len;
|
181
|
+
ary->end = ary->capa;
|
182
|
+
return;
|
183
|
+
}
|
184
|
+
/* add some breathing room for future `unshift`s */
|
185
|
+
needed = 0 - ((ary->capa < 1024) ? (ary->capa >> 1) : 1024);
|
186
|
+
|
187
|
+
} else if (needed == 1 && ary->start >= (ary->capa >> 1)) {
|
188
|
+
/* FIFO support optimizes smaller FIFO ranges over bloating allocations. */
|
189
|
+
size_t len = ary->end - ary->start;
|
190
|
+
memmove(ary->arry + 2, ary->arry + ary->start, len * sizeof(*ary->arry));
|
191
|
+
ary->start = 2;
|
192
|
+
ary->end = len + 2;
|
193
|
+
return;
|
194
|
+
}
|
195
|
+
|
196
|
+
/* alocate using exponential growth, up to single page size. */
|
197
|
+
size_t updated_capa = ary->capa;
|
198
|
+
size_t minimum = ary->capa + ((needed < 0) ? (0 - needed) : needed);
|
199
|
+
while (updated_capa <= minimum)
|
200
|
+
updated_capa =
|
201
|
+
(updated_capa <= 4096) ? (updated_capa << 1) : (updated_capa + 4096);
|
202
|
+
|
203
|
+
/* we assume memory allocation works. it's better to crash than to continue
|
204
|
+
* living without memory... besides, malloc is optimistic these days. */
|
205
|
+
ary->arry = (void **)realloc(ary->arry, updated_capa * sizeof(*ary->arry));
|
206
|
+
ary->capa = updated_capa;
|
207
|
+
if (!ary->arry) {
|
208
|
+
perror("ERROR: facil.io dynamic array couldn't be reallocated");
|
209
|
+
exit(errno);
|
210
|
+
}
|
211
|
+
|
212
|
+
if (needed >= 0) /* we're done, realloc grows the top of the address space*/
|
213
|
+
return;
|
214
|
+
|
215
|
+
/* move everything to the max, since memmove could get expensive */
|
216
|
+
size_t len = ary->end - ary->start;
|
217
|
+
memmove((ary->arry + ary->capa) - len, ary->arry + ary->start,
|
218
|
+
len * sizeof(*ary->arry));
|
219
|
+
ary->end = ary->capa;
|
220
|
+
ary->start = ary->capa - len;
|
221
|
+
}
|
222
|
+
/** Creates a mutable empty Array object with the requested capacity. */
|
223
|
+
FIO_FUNC inline void fiobj_ary_init(fio_ary_s *ary) {
|
224
|
+
*ary = (fio_ary_s){.arry = NULL};
|
225
|
+
}
|
226
|
+
|
227
|
+
/* *****************************************************************************
|
228
|
+
Array direct entry access API
|
229
|
+
***************************************************************************** */
|
230
|
+
|
231
|
+
/** Returns the number of elements in the Array. */
|
232
|
+
FIO_FUNC inline size_t fio_ary_count(fio_ary_s *ary) {
|
233
|
+
return ary->end - ary->start;
|
234
|
+
}
|
235
|
+
|
236
|
+
/** Returns the current, temporary, array capacity (it's dynamic). */
|
237
|
+
FIO_FUNC inline size_t fio_ary_capa(fio_ary_s *ary) { return ary->capa; }
|
238
|
+
|
239
|
+
/**
|
240
|
+
* Returns a temporary object owned by the Array.
|
241
|
+
*
|
242
|
+
* Wrap this function call within `fiobj_dup` to get a persistent handle. i.e.:
|
243
|
+
*
|
244
|
+
* fiobj_dup(fiobj_ary_index(array, 0));
|
245
|
+
*
|
246
|
+
* Negative values are retrived from the end of the array. i.e., `-1`
|
247
|
+
* is the last item.
|
248
|
+
*/
|
249
|
+
FIO_FUNC inline void *fio_ary_index(fio_ary_s *ary, intptr_t pos) {
|
250
|
+
if (!ary || !ary->arry)
|
251
|
+
return NULL;
|
252
|
+
/* position is relative to `start`*/
|
253
|
+
if (pos >= 0) {
|
254
|
+
pos = pos + ary->start;
|
255
|
+
if ((size_t)pos >= ary->end)
|
256
|
+
return NULL;
|
257
|
+
return ary->arry[pos];
|
258
|
+
}
|
259
|
+
/* position is relative to `end`*/
|
260
|
+
pos = (intptr_t)ary->end + pos;
|
261
|
+
if (pos < 0 || (size_t)pos < ary->start)
|
262
|
+
return NULL;
|
263
|
+
return ary->arry[pos];
|
264
|
+
}
|
265
|
+
|
266
|
+
/** alias for `fiobj_ary_index` */
|
267
|
+
#define fio_ary_entry(a, p) fiobj_ary_index((a), (p))
|
268
|
+
|
269
|
+
/**
|
270
|
+
* Sets an object at the requested position.
|
271
|
+
*
|
272
|
+
* Returns the old value, if any.
|
273
|
+
*
|
274
|
+
* If an error occurs, the same data passed to the function is returned.
|
275
|
+
*/
|
276
|
+
FIO_FUNC inline void *fio_ary_set(fio_ary_s *ary, void *data, intptr_t pos) {
|
277
|
+
if (!ary->arry) {
|
278
|
+
fio_ary_new(ary, 0);
|
279
|
+
}
|
280
|
+
void *old = NULL;
|
281
|
+
/* test for memory and request memory if missing, promises valid bounds. */
|
282
|
+
if (pos >= 0) {
|
283
|
+
if ((size_t)pos + ary->start >= ary->capa)
|
284
|
+
fio_ary_getmem(ary, (((size_t)pos + ary->start) - (ary->capa - 1)));
|
285
|
+
} else if (pos + (intptr_t)ary->end < 0)
|
286
|
+
fio_ary_getmem(ary, pos + ary->end);
|
287
|
+
|
288
|
+
if (pos >= 0) {
|
289
|
+
/* position relative to start */
|
290
|
+
pos = pos + ary->start;
|
291
|
+
/* initialize empty spaces, if any, setting new boundries */
|
292
|
+
while ((size_t)pos >= ary->end)
|
293
|
+
ary->arry[(ary->end)++] = NULL;
|
294
|
+
} else {
|
295
|
+
/* position relative to end */
|
296
|
+
pos = pos + (intptr_t)ary->end;
|
297
|
+
/* initialize empty spaces, if any, setting new boundries */
|
298
|
+
while (ary->start > (size_t)pos)
|
299
|
+
ary->arry[--(ary->start)] = NULL;
|
300
|
+
}
|
301
|
+
|
302
|
+
/* check for an existing object and set new objects */
|
303
|
+
if (ary->arry[pos])
|
304
|
+
old = ary->arry[pos];
|
305
|
+
ary->arry[pos] = data;
|
306
|
+
return old;
|
307
|
+
}
|
308
|
+
|
309
|
+
/* *****************************************************************************
|
310
|
+
Array push / shift API
|
311
|
+
***************************************************************************** */
|
312
|
+
|
313
|
+
/**
|
314
|
+
* Pushes an object to the end of the Array. Returns -1 on error.
|
315
|
+
*/
|
316
|
+
FIO_FUNC inline int fio_ary_push(fio_ary_s *ary, void *data) {
|
317
|
+
if (!ary->arry)
|
318
|
+
fio_ary_new(ary, 0);
|
319
|
+
else if (ary->capa <= ary->end)
|
320
|
+
fio_ary_getmem(ary, 1);
|
321
|
+
ary->arry[ary->end] = data;
|
322
|
+
ary->end += 1;
|
323
|
+
return 0;
|
324
|
+
}
|
325
|
+
|
326
|
+
/** Pops an object from the end of the Array. */
|
327
|
+
FIO_FUNC inline void *fio_ary_pop(fio_ary_s *ary) {
|
328
|
+
if (!ary || ary->start == ary->end)
|
329
|
+
return NULL;
|
330
|
+
ary->end -= 1;
|
331
|
+
return ary->arry[ary->end];
|
332
|
+
}
|
333
|
+
|
334
|
+
/**
|
335
|
+
* Unshifts an object to the begining of the Array. Returns -1 on error.
|
336
|
+
*
|
337
|
+
* This could be expensive, causing `memmove`.
|
338
|
+
*/
|
339
|
+
FIO_FUNC inline int fio_ary_unshift(fio_ary_s *ary, void *data) {
|
340
|
+
if (!ary->arry)
|
341
|
+
fio_ary_new(ary, 0);
|
342
|
+
else if (!ary->start)
|
343
|
+
fio_ary_getmem(ary, -1);
|
344
|
+
ary->start -= 1;
|
345
|
+
ary->arry[ary->start] = data;
|
346
|
+
return 0;
|
347
|
+
}
|
348
|
+
|
349
|
+
/** Shifts an object from the beginning of the Array. */
|
350
|
+
FIO_FUNC inline void *fio_ary_shift(fio_ary_s *ary) {
|
351
|
+
if (!ary || ary->start == ary->end)
|
352
|
+
return NULL;
|
353
|
+
#ifdef __cplusplus
|
354
|
+
const size_t pos = ary->start;
|
355
|
+
#else
|
356
|
+
register const size_t pos = ary->start;
|
357
|
+
#endif
|
358
|
+
ary->start += 1;
|
359
|
+
return ary->arry[pos];
|
360
|
+
}
|
361
|
+
|
362
|
+
/**
|
363
|
+
* Single layer iteration using a callback for each entry in the array.
|
364
|
+
*
|
365
|
+
* The callback task function must accept an the entry data as well as an opaque
|
366
|
+
* user pointer.
|
367
|
+
*
|
368
|
+
* If the callback returns -1, the loop is broken. Any other value is ignored.
|
369
|
+
*
|
370
|
+
* Returns the relative "stop" position, i.e., the number of items processed +
|
371
|
+
* the starting point.
|
372
|
+
*/
|
373
|
+
FIO_FUNC inline size_t fio_ary_each(fio_ary_s *ary, size_t start_at,
|
374
|
+
int (*task)(void *data, void *arg),
|
375
|
+
void *arg) {
|
376
|
+
if (!ary || ary->start == ary->end)
|
377
|
+
return 0;
|
378
|
+
const size_t start_pos = ary->start;
|
379
|
+
start_at += start_pos;
|
380
|
+
while (start_at < ary->end && task(ary->arry[start_at++], arg) != -1)
|
381
|
+
;
|
382
|
+
return start_at - start_pos;
|
383
|
+
}
|
384
|
+
|
385
|
+
/* *****************************************************************************
|
386
|
+
Array compacting (untested)
|
387
|
+
***************************************************************************** */
|
388
|
+
|
389
|
+
/**
|
390
|
+
* Removes any NULL *pointers* from an Array, keeping all other data in the
|
391
|
+
* array.
|
392
|
+
*
|
393
|
+
* This action is O(n) where n in the length of the array.
|
394
|
+
* It could get expensive.
|
395
|
+
*/
|
396
|
+
FIO_FUNC inline void fio_ary_compact(fio_ary_s *ary) {
|
397
|
+
if (!ary || ary->start == ary->end)
|
398
|
+
return;
|
399
|
+
register void **pos = ary->arry + ary->start;
|
400
|
+
register void **reader = ary->arry + ary->start;
|
401
|
+
register void **stop = ary->arry + ary->end;
|
402
|
+
while (reader < stop) {
|
403
|
+
if (*reader) {
|
404
|
+
*pos = *reader;
|
405
|
+
pos += 1;
|
406
|
+
}
|
407
|
+
reader += 1;
|
408
|
+
}
|
409
|
+
ary->end = (size_t)(pos - ary->arry);
|
410
|
+
}
|
411
|
+
|
412
|
+
#ifdef __cplusplus
|
413
|
+
#undef register
|
414
|
+
#endif
|
415
|
+
|
416
|
+
#undef FIO_FUNC
|
417
|
+
|
418
|
+
#endif
|