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/extconf.rb
CHANGED
@@ -33,7 +33,7 @@ else
|
|
33
33
|
puts 'using an unknown (old?) compiler... who knows if this will work out... we hope.'
|
34
34
|
end
|
35
35
|
|
36
|
-
$CFLAGS =
|
36
|
+
$CFLAGS = "-std=c11 -O2 -Wall #{ENV['CFLAGS']}"
|
37
37
|
RbConfig::MAKEFILE_CONFIG['CC'] = $CC = ENV['CC'] if ENV['CC']
|
38
38
|
RbConfig::MAKEFILE_CONFIG['CPP'] = $CPP = ENV['CPP'] if ENV['CPP']
|
39
39
|
|
data/ext/iodine/facil.c
CHANGED
@@ -8,14 +8,24 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
8
8
|
|
9
9
|
#include "evio.h"
|
10
10
|
#include "facil.h"
|
11
|
-
#include "
|
11
|
+
#include "fio_hashmap.h"
|
12
|
+
#include "fiobj4sock.h"
|
13
|
+
|
14
|
+
#include "fio_mem.h"
|
12
15
|
|
13
16
|
#include <errno.h>
|
17
|
+
#include <pthread.h>
|
14
18
|
#include <signal.h>
|
15
19
|
#include <stdio.h>
|
16
20
|
#include <stdlib.h>
|
17
21
|
#include <string.h>
|
18
22
|
#include <sys/mman.h>
|
23
|
+
#include <sys/stat.h>
|
24
|
+
#include <sys/wait.h>
|
25
|
+
|
26
|
+
#if !defined(__GNUC__) && !defined(__clang__)
|
27
|
+
#define __attribute__(...)
|
28
|
+
#endif
|
19
29
|
|
20
30
|
/* *****************************************************************************
|
21
31
|
Data Structures
|
@@ -35,31 +45,34 @@ struct connection_data_s {
|
|
35
45
|
protocol_s *protocol;
|
36
46
|
time_t active;
|
37
47
|
uint8_t timeout;
|
48
|
+
spn_lock_i scheduled;
|
38
49
|
spn_lock_i lock;
|
39
50
|
};
|
40
51
|
|
41
52
|
static struct facil_data_s {
|
42
53
|
spn_lock_i global_lock;
|
43
54
|
uint8_t need_review;
|
44
|
-
|
45
|
-
|
55
|
+
uint8_t spindown;
|
56
|
+
uint16_t active;
|
57
|
+
uint16_t threads;
|
46
58
|
pid_t parent;
|
59
|
+
pool_pt thread_pool;
|
60
|
+
ssize_t capacity;
|
61
|
+
size_t connection_count;
|
62
|
+
void (*on_idle)(void);
|
63
|
+
void (*on_finish)(void);
|
64
|
+
struct timespec last_cycle;
|
47
65
|
struct connection_data_s conn[];
|
48
66
|
} * facil_data;
|
49
67
|
|
50
68
|
#define fd_data(fd) (facil_data->conn[(fd)])
|
51
69
|
#define uuid_data(uuid) fd_data(sock_uuid2fd((uuid)))
|
52
|
-
#define uuid_prt_meta(uuid) prt_meta(uuid_data((uuid)).protocol)
|
70
|
+
// #define uuid_prt_meta(uuid) prt_meta(uuid_data((uuid)).protocol)
|
53
71
|
|
54
|
-
static inline void clear_connection_data_unsafe(intptr_t uuid,
|
55
|
-
protocol_s *protocol) {
|
56
|
-
uuid_data(uuid) = (struct connection_data_s){.active = facil_data->last_cycle,
|
57
|
-
.protocol = protocol,
|
58
|
-
.lock = uuid_data(uuid).lock};
|
59
|
-
}
|
60
72
|
/** locks a connection's protocol returns a pointer that need to be unlocked. */
|
61
73
|
inline static protocol_s *protocol_try_lock(intptr_t fd,
|
62
74
|
enum facil_protocol_lock_e type) {
|
75
|
+
errno = 0;
|
63
76
|
if (spn_trylock(&fd_data(fd).lock))
|
64
77
|
goto would_block;
|
65
78
|
protocol_s *pr = fd_data(fd).protocol;
|
@@ -84,6 +97,146 @@ inline static void protocol_unlock(protocol_s *pr,
|
|
84
97
|
spn_unlock(&prt_meta(pr).locks[type]);
|
85
98
|
}
|
86
99
|
|
100
|
+
/* *****************************************************************************
|
101
|
+
Internal Protocol Names
|
102
|
+
***************************************************************************** */
|
103
|
+
static const char *LISTENER_PROTOCOL_NAME =
|
104
|
+
"listening protocol __facil_internal__";
|
105
|
+
|
106
|
+
static const char *CONNECTOR_PROTOCOL_NAME = "connect protocol __internal__";
|
107
|
+
|
108
|
+
static const char *TIMER_PROTOCOL_NAME = "timer protocol __facil_internal__";
|
109
|
+
|
110
|
+
static const char *CLUSTER_LISTEN_PROTOCOL_NAME =
|
111
|
+
"cluster listening protocol __facil_internal__";
|
112
|
+
|
113
|
+
static const char *CLUSTER_CONNECTION_PROTOCOL_NAME =
|
114
|
+
"cluster connection __facil_internal__";
|
115
|
+
|
116
|
+
static inline int is_counted_protocol(protocol_s *p) {
|
117
|
+
return p->service != TIMER_PROTOCOL_NAME &&
|
118
|
+
p->service != CLUSTER_LISTEN_PROTOCOL_NAME &&
|
119
|
+
p->service != CLUSTER_CONNECTION_PROTOCOL_NAME;
|
120
|
+
}
|
121
|
+
|
122
|
+
/* *****************************************************************************
|
123
|
+
Event deferring (declarations)
|
124
|
+
***************************************************************************** */
|
125
|
+
|
126
|
+
static void deferred_on_close(void *uuid_, void *pr_);
|
127
|
+
static void deferred_on_shutdown(void *arg, void *arg2);
|
128
|
+
static void deferred_on_ready(void *arg, void *arg2);
|
129
|
+
static void deferred_on_data(void *uuid, void *arg2);
|
130
|
+
static void deferred_ping(void *arg, void *arg2);
|
131
|
+
|
132
|
+
/* *****************************************************************************
|
133
|
+
Overriding `defer` to use `evio` when waiting for events
|
134
|
+
***************************************************************************** */
|
135
|
+
|
136
|
+
/* if the FIO_DEDICATED_SYSTEM is defined threads are activated more often. */
|
137
|
+
#if FIO_DEDICATED_SYSTEM
|
138
|
+
void defer_thread_wait(pool_pt pool, void *p_thr) {
|
139
|
+
(void)pool;
|
140
|
+
(void)p_thr;
|
141
|
+
evio_wait(EVIO_TICK);
|
142
|
+
}
|
143
|
+
#endif
|
144
|
+
|
145
|
+
/* *****************************************************************************
|
146
|
+
Event Handlers (evio)
|
147
|
+
***************************************************************************** */
|
148
|
+
void sock_flush_defer(void *arg, void *ignored) {
|
149
|
+
(void)ignored;
|
150
|
+
switch (sock_flush((intptr_t)arg)) {
|
151
|
+
case 1:
|
152
|
+
evio_add_write(sock_uuid2fd((intptr_t)arg), (void *)arg);
|
153
|
+
break;
|
154
|
+
case 0:
|
155
|
+
defer(deferred_on_ready, arg, NULL);
|
156
|
+
break;
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
void evio_on_ready(void *arg) { defer(sock_flush_defer, arg, NULL); }
|
161
|
+
void evio_on_close(void *arg) { sock_force_close((intptr_t)arg); }
|
162
|
+
void evio_on_error(void *arg) { sock_force_close((intptr_t)arg); }
|
163
|
+
void evio_on_data(void *arg) { defer(deferred_on_data, arg, NULL); }
|
164
|
+
|
165
|
+
/* *****************************************************************************
|
166
|
+
Mock Protocol Callbacks and Service Funcions
|
167
|
+
***************************************************************************** */
|
168
|
+
static void mock_on_ev(intptr_t uuid, protocol_s *protocol) {
|
169
|
+
(void)uuid;
|
170
|
+
(void)protocol;
|
171
|
+
}
|
172
|
+
|
173
|
+
static void mock_on_data(intptr_t uuid, protocol_s *protocol) {
|
174
|
+
facil_quite(uuid);
|
175
|
+
(void)protocol;
|
176
|
+
}
|
177
|
+
|
178
|
+
static void mock_on_close(intptr_t uuid, protocol_s *protocol) {
|
179
|
+
(void)(protocol);
|
180
|
+
(void)(uuid);
|
181
|
+
}
|
182
|
+
|
183
|
+
static void mock_on_finish(intptr_t uuid, void *udata) {
|
184
|
+
(void)(udata);
|
185
|
+
(void)(uuid);
|
186
|
+
}
|
187
|
+
|
188
|
+
static void mock_ping(intptr_t uuid, protocol_s *protocol) {
|
189
|
+
(void)(protocol);
|
190
|
+
sock_force_close(uuid);
|
191
|
+
}
|
192
|
+
|
193
|
+
static void mock_idle(void) {}
|
194
|
+
|
195
|
+
/* Support for the default pub/sub cluster engine */
|
196
|
+
#pragma weak pubsub_cluster_init
|
197
|
+
void pubsub_cluster_init(void) {}
|
198
|
+
#pragma weak pubsub_cluster_on_fork_start
|
199
|
+
void pubsub_cluster_on_fork_start(void) {}
|
200
|
+
#pragma weak pubsub_cluster_on_fork_end
|
201
|
+
void pubsub_cluster_on_fork_end(void) {}
|
202
|
+
#pragma weak pubsub_cluster_cleanup
|
203
|
+
void pubsub_cluster_cleanup(void) {}
|
204
|
+
|
205
|
+
/* other cleanup concern */
|
206
|
+
#pragma weak fio_cli_end
|
207
|
+
void fio_cli_end(void) {}
|
208
|
+
|
209
|
+
/* perform initialization for external services. */
|
210
|
+
static void facil_external_init(void) {
|
211
|
+
sock_on_fork();
|
212
|
+
pubsub_cluster_on_fork_start();
|
213
|
+
fio_malloc_after_fork();
|
214
|
+
defer_on_fork();
|
215
|
+
}
|
216
|
+
|
217
|
+
/* perform stage 2 initialization for external services. */
|
218
|
+
static void facil_external_init2(void) { pubsub_cluster_on_fork_end(); }
|
219
|
+
|
220
|
+
/* perform cleanup for external services. */
|
221
|
+
static void facil_external_cleanup(void) {}
|
222
|
+
|
223
|
+
#pragma weak http_lib_init
|
224
|
+
void http_lib_init(void) {}
|
225
|
+
#pragma weak http_lib_cleanup
|
226
|
+
void http_lib_cleanup(void) {}
|
227
|
+
|
228
|
+
/* perform initialization for external services. */
|
229
|
+
static void facil_external_root_init(void) {
|
230
|
+
http_lib_init();
|
231
|
+
pubsub_cluster_init();
|
232
|
+
}
|
233
|
+
/* perform cleanup for external services. */
|
234
|
+
static void facil_external_root_cleanup(void) {
|
235
|
+
http_lib_cleanup();
|
236
|
+
pubsub_cluster_cleanup();
|
237
|
+
fio_cli_end();
|
238
|
+
}
|
239
|
+
|
87
240
|
/* *****************************************************************************
|
88
241
|
Deferred event handlers
|
89
242
|
***************************************************************************** */
|
@@ -97,54 +250,80 @@ postpone:
|
|
97
250
|
defer(deferred_on_close, uuid_, pr_);
|
98
251
|
}
|
99
252
|
|
100
|
-
static void
|
101
|
-
if (!uuid_data(arg).protocol)
|
253
|
+
static void deferred_on_shutdown(void *arg, void *arg2) {
|
254
|
+
if (!uuid_data(arg).protocol) {
|
102
255
|
return;
|
103
|
-
|
104
|
-
|
256
|
+
}
|
257
|
+
protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_TASK);
|
258
|
+
if (!pr) {
|
259
|
+
if (errno == EBADF)
|
260
|
+
return;
|
105
261
|
goto postpone;
|
106
|
-
|
107
|
-
|
262
|
+
}
|
263
|
+
uuid_data(arg).active = facil_data->last_cycle.tv_sec;
|
264
|
+
uuid_data(arg).timeout = 8;
|
265
|
+
/* TODO: 0.7.0 catch the return valuse to set timeout and maybe keep open */
|
266
|
+
pr->on_shutdown((intptr_t)arg, pr);
|
267
|
+
pr->ping = mock_ping;
|
268
|
+
protocol_unlock(pr, FIO_PR_LOCK_TASK);
|
269
|
+
sock_close((intptr_t)arg);
|
108
270
|
return;
|
109
271
|
postpone:
|
110
|
-
defer(
|
272
|
+
defer(deferred_on_shutdown, arg, NULL);
|
111
273
|
(void)arg2;
|
112
274
|
}
|
113
275
|
|
114
|
-
static void
|
115
|
-
if (!uuid_data(arg).protocol)
|
276
|
+
static void deferred_on_ready(void *arg, void *arg2) {
|
277
|
+
if (!uuid_data(arg).protocol) {
|
116
278
|
return;
|
279
|
+
}
|
117
280
|
protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_WRITE);
|
118
|
-
if (!pr)
|
281
|
+
if (!pr) {
|
282
|
+
if (errno == EBADF)
|
283
|
+
return;
|
119
284
|
goto postpone;
|
120
|
-
|
285
|
+
}
|
286
|
+
pr->on_ready((intptr_t)arg, pr);
|
121
287
|
protocol_unlock(pr, FIO_PR_LOCK_WRITE);
|
122
|
-
sock_close((intptr_t)arg);
|
123
288
|
return;
|
124
289
|
postpone:
|
125
|
-
defer(
|
290
|
+
defer(deferred_on_ready, arg, NULL);
|
126
291
|
(void)arg2;
|
127
292
|
}
|
128
293
|
|
129
|
-
static void deferred_on_data(void *
|
130
|
-
if (!uuid_data(
|
294
|
+
static void deferred_on_data(void *uuid, void *arg2) {
|
295
|
+
if (!uuid_data(uuid).protocol || sock_isclosed((intptr_t)uuid)) {
|
131
296
|
return;
|
132
|
-
|
133
|
-
|
297
|
+
}
|
298
|
+
protocol_s *pr = protocol_try_lock(sock_uuid2fd(uuid), FIO_PR_LOCK_TASK);
|
299
|
+
if (!pr) {
|
300
|
+
if (errno == EBADF)
|
301
|
+
return;
|
134
302
|
goto postpone;
|
135
|
-
|
303
|
+
}
|
304
|
+
spn_unlock(&uuid_data(uuid).scheduled);
|
305
|
+
pr->on_data((intptr_t)uuid, pr);
|
136
306
|
protocol_unlock(pr, FIO_PR_LOCK_TASK);
|
307
|
+
if (!spn_trylock(&uuid_data(uuid).scheduled)) {
|
308
|
+
evio_add_read(sock_uuid2fd((intptr_t)uuid), uuid);
|
309
|
+
}
|
137
310
|
return;
|
138
311
|
postpone:
|
139
|
-
|
140
|
-
|
312
|
+
if (arg2) {
|
313
|
+
/* the event is being forced, so force rescheduling */
|
314
|
+
defer(deferred_on_data, (void *)uuid, (void *)1);
|
315
|
+
} else {
|
316
|
+
/* the protocol was locked, so there might not be any need for the event */
|
317
|
+
evio_add_read(sock_uuid2fd((intptr_t)uuid), uuid);
|
318
|
+
}
|
319
|
+
return;
|
141
320
|
}
|
142
321
|
|
143
322
|
static void deferred_ping(void *arg, void *arg2) {
|
144
323
|
if (!uuid_data(arg).protocol ||
|
145
324
|
(uuid_data(arg).timeout &&
|
146
325
|
(uuid_data(arg).timeout >
|
147
|
-
(facil_data->last_cycle - uuid_data(arg).active)))) {
|
326
|
+
(facil_data->last_cycle.tv_sec - uuid_data(arg).active)))) {
|
148
327
|
return;
|
149
328
|
}
|
150
329
|
protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_WRITE);
|
@@ -158,22 +337,6 @@ postpone:
|
|
158
337
|
(void)arg2;
|
159
338
|
}
|
160
339
|
|
161
|
-
/* *****************************************************************************
|
162
|
-
Event Handlers (evio)
|
163
|
-
***************************************************************************** */
|
164
|
-
static void sock_flush_defer(void *arg, void *ignored) {
|
165
|
-
(void)ignored;
|
166
|
-
sock_flush((intptr_t)arg);
|
167
|
-
}
|
168
|
-
|
169
|
-
void evio_on_ready(void *arg) {
|
170
|
-
defer(sock_flush_defer, arg, NULL);
|
171
|
-
defer(deferred_on_ready, arg, NULL);
|
172
|
-
}
|
173
|
-
void evio_on_close(void *arg) { sock_force_close((intptr_t)arg); }
|
174
|
-
void evio_on_error(void *arg) { sock_force_close((intptr_t)arg); }
|
175
|
-
void evio_on_data(void *arg) { defer(deferred_on_data, arg, NULL); }
|
176
|
-
|
177
340
|
/* *****************************************************************************
|
178
341
|
Forcing IO events
|
179
342
|
***************************************************************************** */
|
@@ -181,7 +344,8 @@ Forcing IO events
|
|
181
344
|
void facil_force_event(intptr_t uuid, enum facil_io_event ev) {
|
182
345
|
switch (ev) {
|
183
346
|
case FIO_EVENT_ON_DATA:
|
184
|
-
|
347
|
+
spn_trylock(&uuid_data(uuid).scheduled);
|
348
|
+
defer(deferred_on_data, (void *)uuid, (void *)1);
|
185
349
|
break;
|
186
350
|
case FIO_EVENT_ON_TIMEOUT:
|
187
351
|
defer(deferred_ping, (void *)uuid, NULL);
|
@@ -192,21 +356,41 @@ void facil_force_event(intptr_t uuid, enum facil_io_event ev) {
|
|
192
356
|
}
|
193
357
|
}
|
194
358
|
|
359
|
+
/**
|
360
|
+
* Temporarily prevents `on_data` events from firing.
|
361
|
+
*
|
362
|
+
* The `on_data` event will be automatically rescheduled when (if) the socket's
|
363
|
+
* outgoing buffer fills up or when `facil_force_event` is called with
|
364
|
+
* `FIO_EVENT_ON_DATA`.
|
365
|
+
*/
|
366
|
+
void facil_quite(intptr_t uuid) {
|
367
|
+
if (sock_isvalid(uuid))
|
368
|
+
spn_trylock(&uuid_data(uuid).scheduled);
|
369
|
+
}
|
370
|
+
|
195
371
|
/* *****************************************************************************
|
196
372
|
Socket callbacks
|
197
373
|
***************************************************************************** */
|
198
374
|
|
199
375
|
void sock_on_close(intptr_t uuid) {
|
376
|
+
// fprintf(stderr, "INFO: facil.io, on-close called for %u (set to %p)\n",
|
377
|
+
// (unsigned int)sock_uuid2fd(uuid), (void
|
378
|
+
// *)uuid_data(uuid).protocol);
|
200
379
|
spn_lock(&uuid_data(uuid).lock);
|
201
380
|
protocol_s *old_protocol = uuid_data(uuid).protocol;
|
202
|
-
|
381
|
+
uuid_data(uuid) = (struct connection_data_s){.lock = uuid_data(uuid).lock};
|
203
382
|
spn_unlock(&uuid_data(uuid).lock);
|
204
|
-
if (old_protocol)
|
383
|
+
if (old_protocol) {
|
384
|
+
if (is_counted_protocol(old_protocol)) {
|
385
|
+
spn_sub(&facil_data->connection_count, 1);
|
386
|
+
}
|
205
387
|
defer(deferred_on_close, (void *)uuid, old_protocol);
|
388
|
+
}
|
206
389
|
}
|
207
390
|
|
208
391
|
void sock_touch(intptr_t uuid) {
|
209
|
-
|
392
|
+
if (facil_data && facil_data->active)
|
393
|
+
uuid_data(uuid).active = facil_data->last_cycle.tv_sec;
|
210
394
|
}
|
211
395
|
|
212
396
|
/* *****************************************************************************
|
@@ -214,13 +398,18 @@ Initialization and Cleanup
|
|
214
398
|
***************************************************************************** */
|
215
399
|
static spn_lock_i facil_libinit_lock = SPN_LOCK_INIT;
|
216
400
|
|
401
|
+
static void facil_cluster_cleanup(void); /* cluster data cleanup */
|
402
|
+
|
217
403
|
static void facil_libcleanup(void) {
|
218
404
|
/* free memory */
|
219
405
|
spn_lock(&facil_libinit_lock);
|
220
406
|
if (facil_data) {
|
407
|
+
facil_external_root_cleanup();
|
408
|
+
facil_cluster_cleanup();
|
409
|
+
defer_perform(); /* perform any lingering cleanup tasks */
|
221
410
|
munmap(facil_data,
|
222
|
-
sizeof(*facil_data) +
|
223
|
-
|
411
|
+
sizeof(*facil_data) + ((size_t)facil_data->capacity *
|
412
|
+
sizeof(struct connection_data_s)));
|
224
413
|
facil_data = NULL;
|
225
414
|
}
|
226
415
|
spn_unlock(&facil_libinit_lock);
|
@@ -228,19 +417,26 @@ static void facil_libcleanup(void) {
|
|
228
417
|
|
229
418
|
static void facil_lib_init(void) {
|
230
419
|
ssize_t capa = sock_max_capacity();
|
231
|
-
if (capa < 0)
|
232
|
-
perror("ERROR: socket capacity unknown / failure")
|
420
|
+
if (capa < 0) {
|
421
|
+
perror("ERROR: socket capacity unknown / failure");
|
422
|
+
exit(ENOMEM);
|
423
|
+
}
|
233
424
|
size_t mem_size =
|
234
|
-
sizeof(*facil_data) + (capa * sizeof(struct connection_data_s));
|
425
|
+
sizeof(*facil_data) + ((size_t)capa * sizeof(struct connection_data_s));
|
235
426
|
spn_lock(&facil_libinit_lock);
|
236
427
|
if (facil_data)
|
237
428
|
goto finish;
|
238
429
|
facil_data = mmap(NULL, mem_size, PROT_READ | PROT_WRITE | PROT_EXEC,
|
239
430
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
240
|
-
if (!facil_data)
|
241
|
-
perror("ERROR: Couldn't initialize the facil.io library")
|
431
|
+
if (!facil_data) {
|
432
|
+
perror("ERROR: Couldn't initialize the facil.io library");
|
433
|
+
exit(0);
|
434
|
+
}
|
242
435
|
memset(facil_data, 0, mem_size);
|
243
|
-
*facil_data = (struct facil_data_s){
|
436
|
+
*facil_data = (struct facil_data_s){
|
437
|
+
.capacity = capa, .parent = getpid(),
|
438
|
+
};
|
439
|
+
facil_external_root_init();
|
244
440
|
atexit(facil_libcleanup);
|
245
441
|
#ifdef DEBUG
|
246
442
|
if (FACIL_PRINT_STATE)
|
@@ -248,127 +444,65 @@ static void facil_lib_init(void) {
|
|
248
444
|
"Initialized the facil.io library.\n"
|
249
445
|
"facil.io's memory footprint per connection == %lu Bytes X %lu\n"
|
250
446
|
"=== facil.io's memory footprint: %lu ===\n\n",
|
251
|
-
sizeof(struct connection_data_s),
|
447
|
+
(unsigned long)sizeof(struct connection_data_s),
|
448
|
+
(unsigned long)facil_data->capacity, (unsigned long)mem_size);
|
252
449
|
#endif
|
253
450
|
finish:
|
254
451
|
spn_unlock(&facil_libinit_lock);
|
255
|
-
|
256
|
-
}
|
257
|
-
|
258
|
-
/* *****************************************************************************
|
259
|
-
Mock Protocol Callbacks and Service Funcions
|
260
|
-
***************************************************************************** */
|
261
|
-
static void mock_on_ev(intptr_t uuid, protocol_s *protocol) {
|
262
|
-
(void)uuid;
|
263
|
-
(void)protocol;
|
264
|
-
}
|
265
|
-
|
266
|
-
static void mock_on_close(intptr_t uuid, protocol_s *protocol) {
|
267
|
-
(void)(protocol);
|
268
|
-
(void)(uuid);
|
269
|
-
}
|
270
|
-
|
271
|
-
static void mock_on_finish(intptr_t uuid, void *udata) {
|
272
|
-
(void)(udata);
|
273
|
-
(void)(uuid);
|
274
|
-
}
|
275
|
-
|
276
|
-
static void mock_ping(intptr_t uuid, protocol_s *protocol) {
|
277
|
-
(void)(protocol);
|
278
|
-
sock_force_close(uuid);
|
452
|
+
clock_gettime(CLOCK_REALTIME, &facil_data->last_cycle);
|
279
453
|
}
|
280
|
-
static void mock_idle(void) {}
|
281
|
-
|
282
|
-
/* Support for the default pub/sub cluster engine */
|
283
|
-
#pragma weak pubsub_cluster_init
|
284
|
-
void pubsub_cluster_init(void) {}
|
285
454
|
|
286
|
-
|
287
|
-
*
|
455
|
+
/** Sets to shutdown flag for the current process.
|
456
|
+
*
|
457
|
+
* If a cluster is used and this function is called for a worker, a new worker
|
458
|
+
* will respawn.
|
288
459
|
*
|
289
|
-
*
|
460
|
+
* This MUST be signal safe (don't call any functions, just uswe flags).
|
290
461
|
*/
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
/* perform cleanup for external services. */
|
298
|
-
static void facil_external_cleanup(void) { fio_cfd_clear(); }
|
462
|
+
static void facil_stop(void) {
|
463
|
+
if (!facil_data)
|
464
|
+
return;
|
465
|
+
facil_data->active = 0;
|
466
|
+
}
|
299
467
|
|
300
468
|
/* *****************************************************************************
|
301
469
|
The listenning protocol
|
302
470
|
***************************************************************************** */
|
303
471
|
#undef facil_listen
|
304
472
|
|
305
|
-
static const char *listener_protocol_name =
|
306
|
-
"listening protocol __facil_internal__";
|
307
|
-
|
308
473
|
struct ListenerProtocol {
|
309
474
|
protocol_s protocol;
|
310
|
-
|
475
|
+
void (*on_open)(void *uuid, void *udata);
|
311
476
|
void *udata;
|
312
|
-
void *rw_udata;
|
313
|
-
sock_rw_hook_s *(*set_rw_hooks)(intptr_t uuid, void *udata);
|
314
|
-
void (*on_finish_rw)(intptr_t uuid, void *rw_udata);
|
315
477
|
void (*on_start)(intptr_t uuid, void *udata);
|
316
478
|
void (*on_finish)(intptr_t uuid, void *udata);
|
317
|
-
char port
|
479
|
+
char *port;
|
480
|
+
char *address;
|
481
|
+
uint8_t quite;
|
318
482
|
};
|
319
483
|
|
320
|
-
static sock_rw_hook_s *listener_set_rw_hooks(intptr_t uuid, void *udata) {
|
321
|
-
(void)udata;
|
322
|
-
(void)uuid;
|
323
|
-
return NULL; /* (sock_rw_hook_s *)&SOCK_DEFAULT_HOOKS; */
|
324
|
-
}
|
325
|
-
|
326
484
|
static void listener_ping(intptr_t uuid, protocol_s *plistener) {
|
327
485
|
// fprintf(stderr, "*** Listener Ping Called for %ld\n", sock_uuid2fd(uuid));
|
328
|
-
uuid_data(uuid).active = facil_data->last_cycle;
|
486
|
+
uuid_data(uuid).active = facil_data->last_cycle.tv_sec;
|
329
487
|
return;
|
330
488
|
(void)plistener;
|
331
489
|
}
|
332
490
|
|
333
|
-
/* called asynchronously after a new connection was opened */
|
334
|
-
static void listener_deferred_on_open(void *uuid_, void *srv_uuid_) {
|
335
|
-
intptr_t uuid = (intptr_t)uuid_;
|
336
|
-
intptr_t srv_uuid = (intptr_t)srv_uuid_;
|
337
|
-
struct ListenerProtocol *listener =
|
338
|
-
(struct ListenerProtocol *)protocol_try_lock(sock_uuid2fd(srv_uuid),
|
339
|
-
FIO_PR_LOCK_WRITE);
|
340
|
-
if (!listener) {
|
341
|
-
if (sock_isvalid(uuid) && sock_isvalid(srv_uuid))
|
342
|
-
defer(listener_deferred_on_open, uuid_, srv_uuid_);
|
343
|
-
else
|
344
|
-
sock_close(uuid);
|
345
|
-
return;
|
346
|
-
}
|
347
|
-
{
|
348
|
-
sock_rw_hook_s *hooks = listener->set_rw_hooks(uuid, listener->rw_udata);
|
349
|
-
if (hooks)
|
350
|
-
sock_rw_hook_set(uuid, hooks);
|
351
|
-
}
|
352
|
-
protocol_s *pr = listener->on_open(uuid, listener->udata);
|
353
|
-
protocol_unlock((protocol_s *)listener, FIO_PR_LOCK_WRITE);
|
354
|
-
facil_attach(uuid, pr);
|
355
|
-
if (!pr)
|
356
|
-
sock_close(uuid);
|
357
|
-
}
|
358
|
-
|
359
491
|
static void listener_on_data(intptr_t uuid, protocol_s *plistener) {
|
360
|
-
|
361
|
-
|
362
|
-
if (
|
363
|
-
|
364
|
-
|
492
|
+
for (int i = 0; i < 4; ++i) {
|
493
|
+
intptr_t new_client = sock_accept(uuid);
|
494
|
+
if (new_client == -1) {
|
495
|
+
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ECONNABORTED ||
|
496
|
+
errno == ECONNRESET)
|
497
|
+
return;
|
365
498
|
perror("ERROR: socket accept error");
|
366
|
-
|
499
|
+
return;
|
500
|
+
}
|
501
|
+
// to defer or not to defer...? TODO: answer the question
|
502
|
+
struct ListenerProtocol *listener = (struct ListenerProtocol *)plistener;
|
503
|
+
defer(listener->on_open, (void *)new_client, listener->udata);
|
367
504
|
}
|
368
|
-
|
369
|
-
defer(deferred_on_data, (void *)uuid, NULL);
|
370
|
-
return;
|
371
|
-
(void)plistener;
|
505
|
+
facil_force_event(uuid, FIO_EVENT_ON_DATA);
|
372
506
|
}
|
373
507
|
|
374
508
|
static void free_listenner(void *li) { free(li); }
|
@@ -376,10 +510,17 @@ static void free_listenner(void *li) { free(li); }
|
|
376
510
|
static void listener_on_close(intptr_t uuid, protocol_s *plistener) {
|
377
511
|
struct ListenerProtocol *listener = (void *)plistener;
|
378
512
|
listener->on_finish(uuid, listener->udata);
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
513
|
+
if (FACIL_PRINT_STATE && facil_data->parent == getpid()) {
|
514
|
+
if (listener->port) {
|
515
|
+
fprintf(stderr, "* Stopped listening on port %s\n", listener->port);
|
516
|
+
} else {
|
517
|
+
fprintf(stderr, "* Stopped listening on Unix Socket %s\n",
|
518
|
+
listener->address);
|
519
|
+
}
|
520
|
+
}
|
521
|
+
if (!listener->port) {
|
522
|
+
unlink(listener->address);
|
523
|
+
}
|
383
524
|
free_listenner(listener);
|
384
525
|
}
|
385
526
|
|
@@ -388,41 +529,56 @@ listener_alloc(struct facil_listen_args settings) {
|
|
388
529
|
if (!settings.on_start)
|
389
530
|
settings.on_start = mock_on_finish;
|
390
531
|
if (!settings.on_finish)
|
391
|
-
settings.on_finish =
|
392
|
-
|
393
|
-
|
394
|
-
if (
|
395
|
-
settings.
|
396
|
-
|
532
|
+
settings.on_finish = mock_on_finish;
|
533
|
+
size_t port_len = 0;
|
534
|
+
size_t addr_len = 0;
|
535
|
+
if (settings.port) {
|
536
|
+
port_len = strlen(settings.port) + 1;
|
537
|
+
}
|
538
|
+
if (settings.address) {
|
539
|
+
addr_len = strlen(settings.address) + 1;
|
540
|
+
}
|
541
|
+
struct ListenerProtocol *listener =
|
542
|
+
malloc(sizeof(*listener) + addr_len + port_len);
|
543
|
+
|
397
544
|
if (listener) {
|
398
545
|
*listener = (struct ListenerProtocol){
|
399
|
-
.protocol.service =
|
546
|
+
.protocol.service = LISTENER_PROTOCOL_NAME,
|
400
547
|
.protocol.on_data = listener_on_data,
|
401
548
|
.protocol.on_close = listener_on_close,
|
402
549
|
.protocol.ping = listener_ping,
|
403
|
-
.on_open = settings.on_open,
|
550
|
+
.on_open = (void (*)(void *, void *))settings.on_open,
|
404
551
|
.udata = settings.udata,
|
405
552
|
.on_start = settings.on_start,
|
406
553
|
.on_finish = settings.on_finish,
|
407
|
-
.on_finish_rw = settings.on_finish_rw,
|
408
|
-
.set_rw_hooks = settings.set_rw_hooks,
|
409
|
-
.rw_udata = settings.rw_udata,
|
410
554
|
};
|
411
|
-
|
412
|
-
|
555
|
+
if (settings.port) {
|
556
|
+
listener->port = (char *)(listener + 1);
|
557
|
+
memcpy(listener->port, settings.port, port_len);
|
558
|
+
}
|
559
|
+
if (settings.address) {
|
560
|
+
listener->address = (char *)(listener + 1);
|
561
|
+
listener->address += port_len;
|
562
|
+
memcpy(listener->address, settings.address, addr_len);
|
563
|
+
}
|
413
564
|
return listener;
|
414
565
|
}
|
415
566
|
return NULL;
|
416
567
|
}
|
417
568
|
|
418
|
-
inline static void listener_on_start(
|
419
|
-
intptr_t uuid = sock_fd2uuid(fd);
|
420
|
-
if (uuid < 0)
|
421
|
-
fprintf(stderr, "ERROR: listening socket dropped?\n")
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
569
|
+
inline static void listener_on_start(int fd) {
|
570
|
+
intptr_t uuid = sock_fd2uuid((int)fd);
|
571
|
+
if (uuid < 0) {
|
572
|
+
fprintf(stderr, "ERROR: listening socket dropped?\n");
|
573
|
+
kill(0, SIGINT);
|
574
|
+
exit(4);
|
575
|
+
}
|
576
|
+
if (evio_add(fd, (void *)uuid) < 0) {
|
577
|
+
perror("Couldn't register listening socket");
|
578
|
+
kill(0, SIGINT);
|
579
|
+
exit(4);
|
580
|
+
}
|
581
|
+
fd_data(fd).active = facil_data->last_cycle.tv_sec;
|
426
582
|
// call the on_init callback
|
427
583
|
struct ListenerProtocol *listener =
|
428
584
|
(struct ListenerProtocol *)uuid_data(uuid).protocol;
|
@@ -439,8 +595,14 @@ though a `srv_stop` function or when a SIGINT/SIGTERM is received).
|
|
439
595
|
int facil_listen(struct facil_listen_args settings) {
|
440
596
|
if (!facil_data)
|
441
597
|
facil_lib_init();
|
442
|
-
if (settings.on_open == NULL
|
598
|
+
if (settings.on_open == NULL) {
|
599
|
+
errno = EINVAL;
|
443
600
|
return -1;
|
601
|
+
}
|
602
|
+
if (!settings.port || settings.port[0] == 0 ||
|
603
|
+
(settings.port[0] == '0' && settings.port[1] == 0)) {
|
604
|
+
settings.port = NULL;
|
605
|
+
}
|
444
606
|
intptr_t uuid = sock_listen(settings.address, settings.port);
|
445
607
|
if (uuid == -1) {
|
446
608
|
return -1;
|
@@ -451,8 +613,12 @@ int facil_listen(struct facil_listen_args settings) {
|
|
451
613
|
sock_close(uuid);
|
452
614
|
return -1;
|
453
615
|
}
|
454
|
-
if (FACIL_PRINT_STATE)
|
455
|
-
|
616
|
+
if (FACIL_PRINT_STATE && facil_data->parent == getpid()) {
|
617
|
+
if (settings.port)
|
618
|
+
fprintf(stderr, "* Listening on port %s\n", settings.port);
|
619
|
+
else
|
620
|
+
fprintf(stderr, "* Listening on Unix Socket at %s\n", settings.address);
|
621
|
+
}
|
456
622
|
return 0;
|
457
623
|
}
|
458
624
|
|
@@ -460,42 +626,32 @@ int facil_listen(struct facil_listen_args settings) {
|
|
460
626
|
Connect (as client)
|
461
627
|
***************************************************************************** */
|
462
628
|
|
463
|
-
static const char *connector_protocol_name = "connect protocol __internal__";
|
464
|
-
|
465
629
|
struct ConnectProtocol {
|
466
630
|
protocol_s protocol;
|
467
|
-
|
631
|
+
void (*on_connect)(void *uuid, void *udata);
|
468
632
|
void (*on_fail)(intptr_t uuid, void *udata);
|
469
|
-
sock_rw_hook_s *(*set_rw_hooks)(intptr_t uuid, void *udata);
|
470
633
|
void *udata;
|
471
|
-
void *rw_udata;
|
472
634
|
intptr_t uuid;
|
473
|
-
|
635
|
+
uint8_t opened;
|
474
636
|
};
|
475
637
|
|
476
638
|
/* The first `ready` signal is fired when a connection was established */
|
477
639
|
static void connector_on_ready(intptr_t uuid, protocol_s *_connector) {
|
478
640
|
struct ConnectProtocol *connector = (void *)_connector;
|
479
|
-
// fprintf(stderr, "connector_on_ready called\n");
|
480
|
-
if (!connector->opened) {
|
481
|
-
sock_rw_hook_s *hooks = connector->set_rw_hooks(uuid, connector->rw_udata);
|
482
|
-
if (hooks)
|
483
|
-
sock_rw_hook_set(uuid, hooks);
|
484
|
-
}
|
485
|
-
connector->opened = 1;
|
486
641
|
sock_touch(uuid);
|
487
|
-
if (
|
488
|
-
|
489
|
-
|
642
|
+
if (connector->opened == 0) {
|
643
|
+
connector->opened = 1;
|
644
|
+
facil_set_timeout(uuid, 0); /* remove connection timeout settings */
|
645
|
+
connector->on_connect((void *)uuid, connector->udata);
|
646
|
+
evio_add_write(sock_uuid2fd(uuid), (void *)uuid);
|
647
|
+
}
|
490
648
|
return;
|
491
|
-
error:
|
492
|
-
sock_close(uuid);
|
493
649
|
}
|
494
650
|
|
495
651
|
/* If data events reach this protocol, delay their execution. */
|
496
652
|
static void connector_on_data(intptr_t uuid, protocol_s *connector) {
|
497
653
|
(void)connector;
|
498
|
-
|
654
|
+
(void)uuid;
|
499
655
|
}
|
500
656
|
|
501
657
|
/* Failed to connect */
|
@@ -510,39 +666,43 @@ static void connector_on_close(intptr_t uuid, protocol_s *pconnector) {
|
|
510
666
|
#undef facil_connect
|
511
667
|
intptr_t facil_connect(struct facil_connect_args opt) {
|
512
668
|
intptr_t uuid = -1;
|
513
|
-
if (!opt.
|
669
|
+
if (!opt.on_connect || (!opt.address && !opt.port))
|
514
670
|
goto error;
|
515
|
-
if (!opt.
|
516
|
-
opt.
|
671
|
+
if (!opt.timeout)
|
672
|
+
opt.timeout = 30;
|
517
673
|
struct ConnectProtocol *connector = malloc(sizeof(*connector));
|
518
674
|
if (!connector)
|
519
675
|
goto error;
|
520
676
|
*connector = (struct ConnectProtocol){
|
521
|
-
.on_connect = opt.on_connect,
|
677
|
+
.on_connect = (void (*)(void *, void *))opt.on_connect,
|
522
678
|
.on_fail = opt.on_fail,
|
523
679
|
.udata = opt.udata,
|
524
|
-
.protocol.service =
|
680
|
+
.protocol.service = CONNECTOR_PROTOCOL_NAME,
|
525
681
|
.protocol.on_data = connector_on_data,
|
526
682
|
.protocol.on_ready = connector_on_ready,
|
527
683
|
.protocol.on_close = connector_on_close,
|
528
|
-
.set_rw_hooks = opt.set_rw_hooks,
|
529
|
-
.rw_udata = opt.rw_udata,
|
530
684
|
.opened = 0,
|
531
685
|
};
|
532
686
|
uuid = connector->uuid = sock_connect(opt.address, opt.port);
|
533
687
|
/* check for errors, always invoke the on_fail if required */
|
534
|
-
if (uuid == -1)
|
688
|
+
if (uuid == -1) {
|
689
|
+
free(connector);
|
535
690
|
goto error;
|
691
|
+
}
|
536
692
|
if (facil_attach(uuid, &connector->protocol) == -1) {
|
693
|
+
/* facil_attach calls the failure / `on_close` callback */
|
537
694
|
sock_close(uuid);
|
538
|
-
|
695
|
+
return -1;
|
539
696
|
}
|
697
|
+
facil_set_timeout(uuid, opt.timeout);
|
540
698
|
return uuid;
|
541
699
|
error:
|
542
700
|
if (opt.on_fail)
|
543
701
|
opt.on_fail(uuid, opt.udata);
|
544
702
|
return -1;
|
545
703
|
}
|
704
|
+
#define facil_connect(...) \
|
705
|
+
facil_connect((struct facil_connect_args){__VA_ARGS__})
|
546
706
|
|
547
707
|
/* *****************************************************************************
|
548
708
|
Timers
|
@@ -562,18 +722,19 @@ typedef struct {
|
|
562
722
|
|
563
723
|
#define prot2timer(protocol) (*((timer_protocol_s *)(protocol)))
|
564
724
|
|
565
|
-
static const char *timer_protocol_name = "timer protocol __facil_internal__";
|
566
|
-
|
567
725
|
static void timer_on_data(intptr_t uuid, protocol_s *protocol) {
|
568
726
|
prot2timer(protocol).task(prot2timer(protocol).arg);
|
569
|
-
evio_reset_timer(sock_uuid2fd(uuid));
|
570
727
|
if (prot2timer(protocol).repetitions == 0)
|
571
|
-
|
728
|
+
goto reschedule;
|
572
729
|
prot2timer(protocol).repetitions -= 1;
|
573
730
|
if (prot2timer(protocol).repetitions)
|
574
|
-
|
575
|
-
evio_remove(sock_uuid2fd(uuid));
|
731
|
+
goto reschedule;
|
576
732
|
sock_force_close(uuid);
|
733
|
+
return;
|
734
|
+
reschedule:
|
735
|
+
spn_trylock(&uuid_data(uuid).scheduled);
|
736
|
+
evio_set_timer(sock_uuid2fd(uuid), (void *)uuid,
|
737
|
+
prot2timer(protocol).milliseconds);
|
577
738
|
}
|
578
739
|
|
579
740
|
static void timer_on_close(intptr_t uuid, protocol_s *protocol) {
|
@@ -596,7 +757,7 @@ static inline timer_protocol_s *timer_alloc(void (*task)(void *), void *arg,
|
|
596
757
|
timer_protocol_s *t = malloc(sizeof(*t));
|
597
758
|
if (t)
|
598
759
|
*t = (timer_protocol_s){
|
599
|
-
.protocol.service =
|
760
|
+
.protocol.service = TIMER_PROTOCOL_NAME,
|
600
761
|
.protocol.on_data = timer_on_data,
|
601
762
|
.protocol.on_close = timer_on_close,
|
602
763
|
.protocol.ping = timer_ping,
|
@@ -610,10 +771,12 @@ static inline timer_protocol_s *timer_alloc(void (*task)(void *), void *arg,
|
|
610
771
|
}
|
611
772
|
|
612
773
|
inline static void timer_on_server_start(int fd) {
|
613
|
-
if (
|
614
|
-
prot2timer(fd_data(fd).protocol).milliseconds))
|
615
|
-
perror("Couldn't register a required timed event.")
|
616
|
-
|
774
|
+
if (evio_set_timer(fd, (void *)sock_fd2uuid(fd),
|
775
|
+
prot2timer(fd_data(fd).protocol).milliseconds)) {
|
776
|
+
perror("Couldn't register a required timed event.");
|
777
|
+
kill(0, SIGINT);
|
778
|
+
exit(4);
|
779
|
+
}
|
617
780
|
}
|
618
781
|
|
619
782
|
/**
|
@@ -645,8 +808,13 @@ int facil_run_every(size_t milliseconds, size_t repetitions,
|
|
645
808
|
if (protocol == NULL)
|
646
809
|
goto error;
|
647
810
|
facil_attach(uuid, (protocol_s *)protocol);
|
648
|
-
if (evio_isactive() &&
|
649
|
-
goto error
|
811
|
+
if (evio_isactive() && evio_set_timer(fd, (void *)uuid, milliseconds) == -1) {
|
812
|
+
/* don't goto error because the protocol is attached. */
|
813
|
+
const int old = errno;
|
814
|
+
sock_force_close(uuid);
|
815
|
+
errno = old;
|
816
|
+
return -1;
|
817
|
+
}
|
650
818
|
return 0;
|
651
819
|
error:
|
652
820
|
if (uuid != -1) {
|
@@ -668,292 +836,582 @@ error_fin:
|
|
668
836
|
}
|
669
837
|
|
670
838
|
/* *****************************************************************************
|
671
|
-
Cluster Messaging
|
839
|
+
Cluster Messaging - using Unix Sockets
|
672
840
|
***************************************************************************** */
|
673
|
-
/* cluster state management */
|
674
|
-
static struct {
|
675
|
-
uintptr_t count;
|
676
|
-
fio_list_s handlers;
|
677
|
-
spn_lock_i lock;
|
678
|
-
struct facil_cluster_data_pipe {
|
679
|
-
intptr_t in;
|
680
|
-
intptr_t out;
|
681
|
-
} * pipes;
|
682
|
-
} facil_cluster_data = {.handlers.next = &facil_cluster_data.handlers,
|
683
|
-
.handlers.prev = &facil_cluster_data.handlers,
|
684
|
-
.lock = SPN_LOCK_INIT};
|
685
|
-
|
686
|
-
/* message handler */
|
687
|
-
typedef struct {
|
688
|
-
fio_list_s list;
|
689
|
-
void (*on_message)(void *data, uint32_t len);
|
690
|
-
int32_t msg_type;
|
691
|
-
} fio_cluster_handler;
|
692
|
-
|
693
|
-
/* message sending and protocol. */
|
694
|
-
struct facil_cluster_msg_packet {
|
695
|
-
uint16_t count;
|
696
|
-
spn_lock_i lock;
|
697
|
-
struct facil_cluster_msg {
|
698
|
-
uint32_t len;
|
699
|
-
int32_t msg_type;
|
700
|
-
} payload;
|
701
|
-
uint8_t data[];
|
702
|
-
};
|
703
841
|
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
842
|
+
#ifdef __BIG_ENDIAN__
|
843
|
+
inline static uint32_t cluster_str2uint32(uint8_t *str) {
|
844
|
+
return ((str[0] & 0xFF) | ((((uint32_t)str[1]) << 8) & 0xFF00) |
|
845
|
+
((((uint32_t)str[2]) << 16) & 0xFF0000) |
|
846
|
+
((((uint32_t)str[3]) << 24) & 0xFF000000));
|
847
|
+
}
|
848
|
+
inline static void cluster_uint2str(uint8_t *dest, uint32_t i) {
|
849
|
+
dest[0] = i & 0xFF;
|
850
|
+
dest[1] = (i >> 8) & 0xFF;
|
851
|
+
dest[2] = (i >> 16) & 0xFF;
|
852
|
+
dest[3] = (i >> 24) & 0xFF;
|
853
|
+
}
|
854
|
+
#else
|
855
|
+
inline static uint32_t cluster_str2uint32(uint8_t *str) {
|
856
|
+
return (((((uint32_t)str[0]) << 24) & 0xFF000000) |
|
857
|
+
((((uint32_t)str[1]) << 16) & 0xFF0000) |
|
858
|
+
((((uint32_t)str[2]) << 8) & 0xFF00) | (str[3] & 0xFF));
|
859
|
+
}
|
860
|
+
inline static void cluster_uint2str(uint8_t *dest, uint32_t i) {
|
861
|
+
dest[0] = (i >> 24) & 0xFF;
|
862
|
+
dest[1] = (i >> 16) & 0xFF;
|
863
|
+
dest[2] = (i >> 8) & 0xFF;
|
864
|
+
dest[3] = i & 0xFF;
|
865
|
+
}
|
866
|
+
#endif
|
867
|
+
|
868
|
+
#define CLUSTER_READ_BUFFER 16384
|
869
|
+
typedef struct {
|
870
|
+
protocol_s pr;
|
871
|
+
FIOBJ channel;
|
872
|
+
FIOBJ msg;
|
873
|
+
uint32_t exp_channel;
|
874
|
+
uint32_t exp_msg;
|
875
|
+
uint32_t type;
|
876
|
+
int32_t filter;
|
877
|
+
uint32_t length;
|
878
|
+
uint8_t buffer[];
|
879
|
+
} cluster_pr_s;
|
880
|
+
|
881
|
+
typedef struct {
|
882
|
+
void (*on_message)(int32_t filter, FIOBJ, FIOBJ);
|
883
|
+
FIOBJ channel;
|
884
|
+
FIOBJ msg;
|
885
|
+
int32_t filter;
|
886
|
+
} cluster_msg_data_s;
|
887
|
+
|
888
|
+
static void cluster_on_new_peer(intptr_t srv, protocol_s *pr);
|
889
|
+
static void cluster_on_listening_ping(intptr_t srv, protocol_s *pr);
|
890
|
+
static void cluster_on_listening_close(intptr_t srv, protocol_s *pr);
|
891
|
+
|
892
|
+
static struct {
|
893
|
+
protocol_s listening;
|
894
|
+
intptr_t root;
|
895
|
+
fio_hash_s clients;
|
896
|
+
fio_hash_s handlers;
|
897
|
+
spn_lock_i lock;
|
898
|
+
uint8_t client_mode;
|
899
|
+
char cluster_name[128];
|
900
|
+
} facil_cluster_data = {
|
901
|
+
.lock = SPN_LOCK_INIT,
|
902
|
+
.root = -1,
|
903
|
+
.listening =
|
904
|
+
{
|
905
|
+
.on_close = cluster_on_listening_close,
|
906
|
+
.ping = cluster_on_listening_ping,
|
907
|
+
.on_data = cluster_on_new_peer,
|
908
|
+
},
|
909
|
+
};
|
910
|
+
|
911
|
+
enum cluster_message_type_e {
|
912
|
+
CLUSTER_MESSAGE_FORWARD,
|
913
|
+
CLUSTER_MESSAGE_JSON,
|
914
|
+
CLUSTER_MESSAGE_SHUTDOWN,
|
915
|
+
CLUSTER_MESSAGE_ERROR,
|
916
|
+
CLUSTER_MESSAGE_PING,
|
710
917
|
};
|
711
918
|
|
712
|
-
|
713
|
-
|
714
|
-
|
919
|
+
static void cluster_deferred_handler(void *msg_data_, void *ignr) {
|
920
|
+
cluster_msg_data_s *data = msg_data_;
|
921
|
+
data->on_message(data->filter, data->channel, data->msg);
|
922
|
+
fiobj_free(data->channel);
|
923
|
+
fiobj_free(data->msg);
|
924
|
+
fio_free(data);
|
925
|
+
(void)ignr;
|
926
|
+
}
|
927
|
+
|
928
|
+
static void cluster_forward_msg2handlers(cluster_pr_s *c) {
|
715
929
|
spn_lock(&facil_cluster_data.lock);
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
930
|
+
void *target_ =
|
931
|
+
fio_hash_find(&facil_cluster_data.handlers, (FIO_HASH_KEY_TYPE)c->filter);
|
932
|
+
spn_unlock(&facil_cluster_data.lock);
|
933
|
+
// fprintf(stderr, "handler for %d: %p\n", c->filter, target_);
|
934
|
+
if (target_) {
|
935
|
+
cluster_msg_data_s *data = fio_malloc(sizeof(*data));
|
936
|
+
if (!data) {
|
937
|
+
perror("FATAL ERROR: (facil.io cluster) couldn't allocate memory");
|
938
|
+
exit(errno);
|
720
939
|
}
|
940
|
+
*data = (cluster_msg_data_s){
|
941
|
+
.on_message = ((cluster_msg_data_s *)(&target_))->on_message,
|
942
|
+
.channel = fiobj_dup(c->channel),
|
943
|
+
.msg = fiobj_dup(c->msg),
|
944
|
+
.filter = c->filter,
|
945
|
+
};
|
946
|
+
defer(cluster_deferred_handler, data, NULL);
|
721
947
|
}
|
722
|
-
spn_unlock(&facil_cluster_data.lock);
|
723
948
|
}
|
724
949
|
|
725
|
-
|
726
|
-
|
950
|
+
static inline FIOBJ cluster_wrap_message(uint32_t ch_len, uint32_t msg_len,
|
951
|
+
uint32_t type, int32_t id,
|
952
|
+
uint8_t *ch_data, uint8_t *msg_data) {
|
953
|
+
FIOBJ buf = fiobj_str_buf(ch_len + msg_len + 16);
|
954
|
+
fio_cstr_s f = fiobj_obj2cstr(buf);
|
955
|
+
cluster_uint2str(f.bytes, ch_len);
|
956
|
+
cluster_uint2str(f.bytes + 4, msg_len);
|
957
|
+
cluster_uint2str(f.bytes + 8, type);
|
958
|
+
cluster_uint2str(f.bytes + 12, (uint32_t)id);
|
959
|
+
if (ch_len && ch_data) {
|
960
|
+
memcpy(f.bytes + 16, ch_data, ch_len);
|
961
|
+
}
|
962
|
+
if (msg_len && msg_data) {
|
963
|
+
memcpy(f.bytes + 16 + ch_len, msg_data, msg_len);
|
964
|
+
}
|
965
|
+
fiobj_str_resize(buf, ch_len + msg_len + 16);
|
966
|
+
return buf;
|
967
|
+
}
|
727
968
|
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
969
|
+
static inline void cluster_send2clients(uint32_t ch_len, uint32_t msg_len,
|
970
|
+
uint32_t type, int32_t id,
|
971
|
+
uint8_t *ch_data, uint8_t *msg_data,
|
972
|
+
intptr_t uuid) {
|
973
|
+
if (facil_cluster_data.clients.count == 0)
|
974
|
+
return;
|
975
|
+
FIOBJ forward =
|
976
|
+
cluster_wrap_message(ch_len, msg_len, type, id, ch_data, msg_data);
|
977
|
+
spn_lock(&facil_cluster_data.lock);
|
978
|
+
FIO_HASH_FOR_LOOP(&facil_cluster_data.clients, i) {
|
979
|
+
if (i->obj) {
|
980
|
+
if ((intptr_t)i->key != uuid)
|
981
|
+
fiobj_send_free((intptr_t)i->key, fiobj_dup(forward));
|
982
|
+
}
|
983
|
+
}
|
984
|
+
spn_unlock(&facil_cluster_data.lock);
|
985
|
+
fiobj_free(forward);
|
986
|
+
}
|
987
|
+
|
988
|
+
static inline void cluster_send2traget(uint32_t ch_len, uint32_t msg_len,
|
989
|
+
uint32_t type, int32_t id,
|
990
|
+
uint8_t *ch_data, uint8_t *msg_data) {
|
991
|
+
if (facil_cluster_data.client_mode) {
|
992
|
+
FIOBJ forward =
|
993
|
+
cluster_wrap_message(ch_len, msg_len, type, id, ch_data, msg_data);
|
994
|
+
fiobj_send_free(facil_cluster_data.root, forward);
|
995
|
+
} else {
|
996
|
+
cluster_send2clients(ch_len, msg_len, type, id, ch_data, msg_data, 0);
|
997
|
+
}
|
740
998
|
}
|
741
999
|
|
742
|
-
/*
|
743
|
-
static void
|
744
|
-
if (
|
1000
|
+
/* NOT signal safe. */
|
1001
|
+
static inline void facil_cluster_signal_children(void) {
|
1002
|
+
if (facil_parent_pid() != getpid()) {
|
1003
|
+
facil_stop();
|
745
1004
|
return;
|
746
|
-
if (defer_fork_is_active()) {
|
747
|
-
kill(getpid(), SIGINT);
|
748
|
-
#if DEBUG
|
749
|
-
fprintf(stderr,
|
750
|
-
"* Sibling death detected, signaling self exit process %d\n",
|
751
|
-
getpid());
|
752
|
-
#endif
|
753
1005
|
}
|
754
|
-
(
|
755
|
-
|
1006
|
+
cluster_send2traget(0, 0, CLUSTER_MESSAGE_SHUTDOWN, 0, NULL, NULL);
|
1007
|
+
/* TODO: add `on_restart` callback handler. */
|
1008
|
+
}
|
1009
|
+
|
1010
|
+
static void cluster_on_client_message(cluster_pr_s *c, intptr_t uuid) {
|
1011
|
+
switch ((enum cluster_message_type_e)c->type) {
|
1012
|
+
case CLUSTER_MESSAGE_JSON: {
|
1013
|
+
fio_cstr_s s = fiobj_obj2cstr(c->channel);
|
1014
|
+
FIOBJ tmp = FIOBJ_INVALID;
|
1015
|
+
if (fiobj_json2obj(&tmp, s.bytes, s.len)) {
|
1016
|
+
fiobj_free(c->channel);
|
1017
|
+
c->channel = tmp;
|
1018
|
+
tmp = FIOBJ_INVALID;
|
1019
|
+
} else {
|
1020
|
+
fprintf(stderr,
|
1021
|
+
"WARNING: (facil.io cluster) JSON channel isn't valid JSON.\n");
|
1022
|
+
}
|
1023
|
+
s = fiobj_obj2cstr(c->msg);
|
1024
|
+
if (fiobj_json2obj(&tmp, s.bytes, s.len)) {
|
1025
|
+
fiobj_free(c->msg);
|
1026
|
+
c->msg = tmp;
|
1027
|
+
} else {
|
1028
|
+
fprintf(stderr,
|
1029
|
+
"WARNING: (facil.io cluster) JSON message isn't valid JSON.\n");
|
1030
|
+
}
|
1031
|
+
}
|
1032
|
+
/* fallthrough */
|
1033
|
+
case CLUSTER_MESSAGE_FORWARD:
|
1034
|
+
cluster_forward_msg2handlers(c);
|
1035
|
+
break;
|
1036
|
+
|
1037
|
+
case CLUSTER_MESSAGE_ERROR:
|
1038
|
+
case CLUSTER_MESSAGE_SHUTDOWN:
|
1039
|
+
facil_stop();
|
1040
|
+
facil_cluster_data.root = -1;
|
1041
|
+
sock_close(uuid);
|
1042
|
+
break;
|
1043
|
+
|
1044
|
+
case CLUSTER_MESSAGE_PING:
|
1045
|
+
/* do nothing, really. */
|
1046
|
+
break;
|
1047
|
+
}
|
756
1048
|
}
|
757
1049
|
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
if (read < 0)
|
768
|
-
perror("ERROR: cluster pipe read error");
|
769
|
-
return;
|
770
|
-
}
|
771
|
-
pr->read += read;
|
772
|
-
if (pr->read < 8)
|
773
|
-
continue;
|
774
|
-
if (pr->mem_size < pr->msg->len) {
|
775
|
-
void *tmp = realloc(pr->msg, pr->msg->len);
|
776
|
-
if (!tmp)
|
777
|
-
perror("ERROR: (critical) cannot allocate memory for a clustered "
|
778
|
-
"message."),
|
779
|
-
kill(0, SIGINT), exit(errno);
|
780
|
-
pr->msg = tmp;
|
781
|
-
#if defined(FACIL_PRINT_STATE) && FACIL_PRINT_STATE == 1 && defined(DEBUG)
|
782
|
-
fprintf(stderr, "* Cluster (%d) Reallocated msg size to %u\n",
|
783
|
-
defer_fork_pid(), pr->msg->len);
|
784
|
-
#endif
|
785
|
-
pr->mem_size = pr->msg->len;
|
786
|
-
}
|
1050
|
+
static void cluster_on_server_message(cluster_pr_s *c, intptr_t uuid) {
|
1051
|
+
switch ((enum cluster_message_type_e)c->type) {
|
1052
|
+
case CLUSTER_MESSAGE_JSON:
|
1053
|
+
case CLUSTER_MESSAGE_FORWARD: {
|
1054
|
+
if (fio_hash_count(&facil_cluster_data.clients)) {
|
1055
|
+
fio_cstr_s cs = fiobj_obj2cstr(c->channel);
|
1056
|
+
fio_cstr_s ms = fiobj_obj2cstr(c->msg);
|
1057
|
+
cluster_send2clients((uint32_t)cs.len, (uint32_t)ms.len, c->type,
|
1058
|
+
c->filter, cs.bytes, ms.bytes, uuid);
|
787
1059
|
}
|
788
|
-
if (
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
1060
|
+
if (c->type == CLUSTER_MESSAGE_JSON) {
|
1061
|
+
fio_cstr_s s = fiobj_obj2cstr(c->channel);
|
1062
|
+
FIOBJ tmp = FIOBJ_INVALID;
|
1063
|
+
if (fiobj_json2obj(&tmp, s.bytes, s.len)) {
|
1064
|
+
fiobj_free(c->channel);
|
1065
|
+
c->channel = tmp;
|
1066
|
+
tmp = FIOBJ_INVALID;
|
1067
|
+
} else {
|
1068
|
+
fprintf(stderr,
|
1069
|
+
"WARNING: (facil.io cluster) JSON message isn't valid JSON.\n");
|
1070
|
+
}
|
1071
|
+
s = fiobj_obj2cstr(c->msg);
|
1072
|
+
if (fiobj_json2obj(&tmp, s.bytes, s.len)) {
|
1073
|
+
fiobj_free(c->msg);
|
1074
|
+
c->msg = tmp;
|
1075
|
+
} else {
|
1076
|
+
fprintf(stderr,
|
1077
|
+
"WARNING: (facil.io cluster) JSON message isn't valid JSON.\n");
|
795
1078
|
}
|
796
1079
|
}
|
1080
|
+
cluster_forward_msg2handlers(c);
|
1081
|
+
break;
|
1082
|
+
}
|
1083
|
+
case CLUSTER_MESSAGE_SHUTDOWN:
|
1084
|
+
case CLUSTER_MESSAGE_ERROR:
|
1085
|
+
case CLUSTER_MESSAGE_PING:
|
1086
|
+
/* do nothing, really. */
|
1087
|
+
break;
|
797
1088
|
}
|
798
1089
|
}
|
799
1090
|
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
(
|
804
|
-
|
1091
|
+
static void cluster_on_client_close(intptr_t uuid, protocol_s *pr_) {
|
1092
|
+
cluster_pr_s *c = (cluster_pr_s *)pr_;
|
1093
|
+
/* no shutdown message received - parent crashed. */
|
1094
|
+
if (facil_cluster_data.root == uuid && c->type != CLUSTER_MESSAGE_SHUTDOWN &&
|
1095
|
+
facil_data->active) {
|
1096
|
+
if (FACIL_PRINT_STATE)
|
1097
|
+
fprintf(stderr, "* (%d) Parent Process crash detected!\n", getpid());
|
1098
|
+
unlink(facil_cluster_data.cluster_name);
|
1099
|
+
}
|
1100
|
+
fiobj_free(c->msg);
|
1101
|
+
fiobj_free(c->channel);
|
1102
|
+
free(c);
|
1103
|
+
facil_stop();
|
1104
|
+
if (facil_cluster_data.root == uuid)
|
1105
|
+
facil_cluster_data.root = -1;
|
1106
|
+
}
|
1107
|
+
|
1108
|
+
static void cluster_on_server_close(intptr_t uuid, protocol_s *pr_) {
|
1109
|
+
cluster_pr_s *c = (cluster_pr_s *)pr_;
|
1110
|
+
if (facil_cluster_data.client_mode || facil_parent_pid() != getpid()) {
|
1111
|
+
/* we respawned - clean up resources, but don't stop server */
|
1112
|
+
fiobj_free(c->msg);
|
1113
|
+
fiobj_free(c->channel);
|
1114
|
+
free(c);
|
1115
|
+
if (facil_cluster_data.root == uuid)
|
1116
|
+
facil_cluster_data.root = -1;
|
805
1117
|
return;
|
806
|
-
|
807
|
-
/* used to watch for sibling crashes and prevent other events from firing */
|
808
|
-
static protocol_s sentinel_protocol = {
|
809
|
-
.service = NULL,
|
810
|
-
.ping = listener_ping,
|
811
|
-
.on_close = facil_cluster_on_sibling_close,
|
812
|
-
};
|
813
|
-
/* close what we don't need and watch the connections we do need */
|
814
|
-
for (size_t i = 0; i < facil_cluster_data.count; i++) {
|
815
|
-
if (i == (size_t)defer_fork_pid())
|
816
|
-
continue;
|
817
|
-
sock_close(facil_cluster_data.pipes[i].in);
|
818
|
-
facil_attach(facil_cluster_data.pipes[i].out, &sentinel_protocol);
|
819
1118
|
}
|
820
|
-
|
1119
|
+
spn_lock(&facil_cluster_data.lock);
|
1120
|
+
fio_hash_insert(&facil_cluster_data.clients, (FIO_HASH_KEY_TYPE)uuid, NULL);
|
1121
|
+
// fio_hash_compact(&facil_cluster_data.clients);
|
1122
|
+
spn_unlock(&facil_cluster_data.lock);
|
1123
|
+
fiobj_free(c->msg);
|
1124
|
+
fiobj_free(c->channel);
|
1125
|
+
free(c);
|
1126
|
+
}
|
821
1127
|
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
.protocol.service = facil_cluster_protocol_id,
|
828
|
-
.protocol.on_data = facil_cluster_on_data,
|
829
|
-
.protocol.on_close = facil_cluster_on_close,
|
830
|
-
.protocol.ping = listener_ping,
|
831
|
-
};
|
832
|
-
pr->msg = calloc(1, 1024);
|
833
|
-
pr->mem_size = 1024;
|
834
|
-
if (!pr->msg)
|
835
|
-
perror("ERROR: (critical) cannot allocate memory for cluster."),
|
836
|
-
kill(0, SIGINT), exit(errno);
|
837
|
-
facil_attach(facil_cluster_data.pipes[defer_fork_pid()].in, &pr->protocol);
|
1128
|
+
static void cluster_on_shutdown(intptr_t uuid, protocol_s *pr_) {
|
1129
|
+
cluster_send2traget(0, 0, CLUSTER_MESSAGE_SHUTDOWN, 0, NULL, NULL);
|
1130
|
+
facil_force_event(uuid, FIO_EVENT_ON_READY);
|
1131
|
+
(void)pr_;
|
1132
|
+
(void)uuid;
|
838
1133
|
}
|
839
1134
|
|
840
|
-
static void
|
841
|
-
|
1135
|
+
static void cluster_on_data(intptr_t uuid, protocol_s *pr_) {
|
1136
|
+
cluster_pr_s *c = (cluster_pr_s *)pr_;
|
1137
|
+
ssize_t i =
|
1138
|
+
sock_read(uuid, c->buffer + c->length, CLUSTER_READ_BUFFER - c->length);
|
1139
|
+
if (i <= 0)
|
842
1140
|
return;
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
1141
|
+
c->length += i;
|
1142
|
+
i = 0;
|
1143
|
+
do {
|
1144
|
+
if (!c->exp_channel && !c->exp_msg) {
|
1145
|
+
if (c->length - i < 16)
|
1146
|
+
break;
|
1147
|
+
c->exp_channel = cluster_str2uint32(c->buffer + i);
|
1148
|
+
c->exp_msg = cluster_str2uint32(c->buffer + i + 4);
|
1149
|
+
c->type = cluster_str2uint32(c->buffer + i + 8);
|
1150
|
+
c->filter = (int32_t)cluster_str2uint32(c->buffer + i + 12);
|
1151
|
+
if (c->exp_channel) {
|
1152
|
+
if (c->exp_channel >= (1024 * 1024 * 16)) {
|
1153
|
+
fprintf(
|
1154
|
+
stderr,
|
1155
|
+
"ERROR: (%d) cluster message name too long (16Mb limit): %u\n",
|
1156
|
+
getpid(), (unsigned int)c->exp_channel);
|
1157
|
+
facil_stop();
|
1158
|
+
return;
|
1159
|
+
}
|
1160
|
+
c->channel = fiobj_str_buf(c->exp_channel);
|
1161
|
+
}
|
1162
|
+
if (c->exp_msg) {
|
1163
|
+
if (c->exp_msg >= (1024 * 1024 * 64)) {
|
1164
|
+
fprintf(
|
1165
|
+
stderr,
|
1166
|
+
"ERROR: (%d) cluster message data too long (64Mb limit): %u\n",
|
1167
|
+
getpid(), (unsigned int)c->exp_msg);
|
1168
|
+
facil_stop();
|
1169
|
+
return;
|
1170
|
+
}
|
1171
|
+
c->msg = fiobj_str_buf(c->exp_msg);
|
1172
|
+
}
|
1173
|
+
i += 16;
|
1174
|
+
}
|
1175
|
+
if (c->exp_channel) {
|
1176
|
+
if (c->exp_channel + i > c->length) {
|
1177
|
+
fiobj_str_write(c->channel, (char *)c->buffer + i,
|
1178
|
+
(size_t)(c->length - i));
|
1179
|
+
c->exp_channel -= (c->length - i);
|
1180
|
+
i = c->length;
|
1181
|
+
break;
|
1182
|
+
} else {
|
1183
|
+
fiobj_str_write(c->channel, (char *)c->buffer + i, c->exp_channel);
|
1184
|
+
i += c->exp_channel;
|
1185
|
+
c->exp_channel = 0;
|
1186
|
+
}
|
1187
|
+
}
|
1188
|
+
if (c->exp_msg) {
|
1189
|
+
if (c->exp_msg + i > c->length) {
|
1190
|
+
fiobj_str_write(c->msg, (char *)c->buffer + i, (size_t)(c->length - i));
|
1191
|
+
c->exp_msg -= (c->length - i);
|
1192
|
+
i = c->length;
|
1193
|
+
break;
|
1194
|
+
} else {
|
1195
|
+
fiobj_str_write(c->msg, (char *)c->buffer + i, c->exp_msg);
|
1196
|
+
i += c->exp_msg;
|
1197
|
+
c->exp_msg = 0;
|
1198
|
+
}
|
1199
|
+
}
|
1200
|
+
if (facil_cluster_data.client_mode) {
|
1201
|
+
cluster_on_client_message(c, uuid);
|
1202
|
+
} else {
|
1203
|
+
cluster_on_server_message(c, uuid);
|
1204
|
+
}
|
1205
|
+
fiobj_free(c->msg);
|
1206
|
+
fiobj_free(c->channel);
|
1207
|
+
c->msg = FIOBJ_INVALID;
|
1208
|
+
c->channel = FIOBJ_INVALID;
|
1209
|
+
} while (c->length > i);
|
1210
|
+
c->length -= i;
|
1211
|
+
if (c->length) {
|
1212
|
+
memmove(c->buffer, c->buffer + i, c->length);
|
1213
|
+
}
|
1214
|
+
(void)pr_;
|
1215
|
+
}
|
1216
|
+
static void cluster_ping(intptr_t uuid, protocol_s *pr_) {
|
1217
|
+
FIOBJ ping = cluster_wrap_message(0, 0, CLUSTER_MESSAGE_PING, 0, NULL, NULL);
|
1218
|
+
fiobj_send_free(uuid, ping);
|
1219
|
+
(void)pr_;
|
1220
|
+
}
|
847
1221
|
|
848
|
-
|
849
|
-
|
850
|
-
|
1222
|
+
static void cluster_on_open(intptr_t fd, void *udata) {
|
1223
|
+
cluster_pr_s *pr = malloc(sizeof(*pr) + CLUSTER_READ_BUFFER);
|
1224
|
+
*pr = (cluster_pr_s){
|
1225
|
+
.pr =
|
1226
|
+
{
|
1227
|
+
.service = CLUSTER_CONNECTION_PROTOCOL_NAME,
|
1228
|
+
.on_data = cluster_on_data,
|
1229
|
+
.on_shutdown = cluster_on_shutdown,
|
1230
|
+
.on_close =
|
1231
|
+
(facil_cluster_data.client_mode ? cluster_on_client_close
|
1232
|
+
: cluster_on_server_close),
|
1233
|
+
.ping = cluster_ping,
|
1234
|
+
},
|
851
1235
|
};
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
facil_cluster_data.
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
}
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
1236
|
+
if (facil_cluster_data.root >= 0 && facil_cluster_data.root != fd) {
|
1237
|
+
// if (facil_parent_pid() != getpid()) {
|
1238
|
+
// fprintf(stderr,
|
1239
|
+
// "WARNING: non-root process adding client...\n"
|
1240
|
+
// " adding to clients %p (root == %p)\n",
|
1241
|
+
// (void *)fd, (void *)facil_cluster_data.root);
|
1242
|
+
// } else {
|
1243
|
+
// fprintf(stderr, "INFO: root process adding child connection %p\n",
|
1244
|
+
// (void *)fd);
|
1245
|
+
// }
|
1246
|
+
spn_lock(&facil_cluster_data.lock);
|
1247
|
+
fio_hash_insert(&facil_cluster_data.clients, (FIO_HASH_KEY_TYPE)fd,
|
1248
|
+
(void *)fd);
|
1249
|
+
spn_unlock(&facil_cluster_data.lock);
|
1250
|
+
} else if (facil_parent_pid() != getpid()) {
|
1251
|
+
// fprintf(stderr, "INFO: child process registering...%p \n", (void *)fd);
|
1252
|
+
}
|
1253
|
+
if (facil_attach(fd, &pr->pr) == -1) {
|
1254
|
+
fprintf(stderr, "(%d) ", getpid());
|
1255
|
+
perror("ERROR: (facil.io cluster) couldn't attach connection");
|
1256
|
+
facil_stop();
|
1257
|
+
}
|
1258
|
+
(void)udata;
|
872
1259
|
}
|
873
1260
|
|
874
|
-
static void
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
1261
|
+
static void cluster_on_new_peer(intptr_t srv, protocol_s *pr) {
|
1262
|
+
intptr_t client = sock_accept(srv);
|
1263
|
+
if (client == -1) {
|
1264
|
+
// fprintf(stderr,
|
1265
|
+
// "ERROR: (facil.io cluster) couldn't accept connection\n");
|
1266
|
+
} else {
|
1267
|
+
cluster_on_open(client, NULL);
|
880
1268
|
}
|
881
|
-
|
882
|
-
fio_cluster_handler *hnd;
|
883
|
-
while ((hnd = fio_list_pop(fio_cluster_handler, list,
|
884
|
-
facil_cluster_data.handlers)))
|
885
|
-
free(hnd);
|
886
|
-
free(facil_cluster_data.pipes);
|
887
|
-
facil_cluster_data.pipes = NULL;
|
1269
|
+
(void)pr;
|
888
1270
|
}
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
fio_cluster_handler *hnd;
|
893
|
-
spn_lock(&facil_cluster_data.lock);
|
894
|
-
fio_list_for_each(fio_cluster_handler, list, hnd,
|
895
|
-
facil_cluster_data.handlers) {
|
896
|
-
if (hnd->msg_type == msg_type) {
|
897
|
-
hnd->on_message = on_message;
|
898
|
-
spn_unlock(&facil_cluster_data.lock);
|
899
|
-
return;
|
900
|
-
}
|
1271
|
+
static void cluster_on_listening_close(intptr_t srv, protocol_s *pr) {
|
1272
|
+
if (facil_parent_pid() == getpid()) {
|
1273
|
+
unlink(facil_cluster_data.cluster_name);
|
901
1274
|
}
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
1275
|
+
if (facil_cluster_data.root == srv)
|
1276
|
+
facil_cluster_data.root = -1;
|
1277
|
+
(void)srv;
|
1278
|
+
(void)pr;
|
1279
|
+
}
|
1280
|
+
static void cluster_on_listening_ping(intptr_t srv, protocol_s *pr) {
|
1281
|
+
sock_touch(srv);
|
1282
|
+
(void)pr;
|
906
1283
|
}
|
907
1284
|
|
908
|
-
static
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
1285
|
+
static int cluster_on_start(void) {
|
1286
|
+
if (facil_data->active <= 1)
|
1287
|
+
return 0;
|
1288
|
+
if (facil_parent_pid() == getpid()) {
|
1289
|
+
/* Root process, listen to incoming cluster connections */
|
1290
|
+
facil_cluster_data.client_mode = 0;
|
1291
|
+
facil_cluster_data.listening.service = CLUSTER_LISTEN_PROTOCOL_NAME;
|
1292
|
+
if (facil_attach(facil_cluster_data.root, &facil_cluster_data.listening)) {
|
1293
|
+
perror("FATAL ERROR: (facil.io) couldn't attach cluster socket");
|
1294
|
+
}
|
1295
|
+
} else {
|
1296
|
+
/* Worker process, connect to Root process */
|
1297
|
+
facil_cluster_data.client_mode = 1;
|
1298
|
+
fio_hash_free(&facil_cluster_data.clients);
|
1299
|
+
facil_cluster_data.clients = (fio_hash_s)FIO_HASH_INIT;
|
1300
|
+
if (facil_cluster_data.root != -1) {
|
1301
|
+
sock_force_close(facil_cluster_data.root);
|
1302
|
+
}
|
1303
|
+
facil_cluster_data.root =
|
1304
|
+
facil_connect(.address = facil_cluster_data.cluster_name,
|
1305
|
+
.on_connect = cluster_on_open);
|
1306
|
+
if (facil_cluster_data.root == -1) {
|
1307
|
+
perror(
|
1308
|
+
"FATAL ERROR: (facil.io cluster) couldn't connect to cluster socket");
|
1309
|
+
fprintf(stderr, " socket: %s\n", facil_cluster_data.cluster_name);
|
1310
|
+
facil_stop();
|
1311
|
+
return -1;
|
1312
|
+
}
|
915
1313
|
}
|
916
|
-
|
1314
|
+
return 0;
|
917
1315
|
}
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
1316
|
+
|
1317
|
+
static int facil_cluster_init(void) {
|
1318
|
+
if (facil_data->active <= 1)
|
1319
|
+
return 0;
|
1320
|
+
/* create a unique socket name */
|
1321
|
+
char *tmp_folder = getenv("TMPDIR");
|
1322
|
+
uint32_t tmp_folder_len = 0;
|
1323
|
+
if (!tmp_folder || ((tmp_folder_len = (uint32_t)strlen(tmp_folder)) > 100)) {
|
1324
|
+
#ifdef P_tmpdir
|
1325
|
+
tmp_folder = P_tmpdir;
|
1326
|
+
if (tmp_folder)
|
1327
|
+
tmp_folder_len = (uint32_t)strlen(tmp_folder);
|
1328
|
+
#else
|
1329
|
+
tmp_folder = "/tmp/";
|
1330
|
+
tmp_folder_len = 5;
|
924
1331
|
#endif
|
925
|
-
return -1;
|
926
1332
|
}
|
927
|
-
if (
|
928
|
-
|
929
|
-
if (
|
1333
|
+
if (tmp_folder_len >= 100)
|
1334
|
+
tmp_folder_len = 0;
|
1335
|
+
if (tmp_folder_len) {
|
1336
|
+
memcpy(facil_cluster_data.cluster_name, tmp_folder, tmp_folder_len);
|
1337
|
+
if (facil_cluster_data.cluster_name[tmp_folder_len - 1] != '/')
|
1338
|
+
facil_cluster_data.cluster_name[tmp_folder_len++] = '/';
|
1339
|
+
}
|
1340
|
+
memcpy(facil_cluster_data.cluster_name + tmp_folder_len, "facil-io-sock-",
|
1341
|
+
14);
|
1342
|
+
tmp_folder_len += 14;
|
1343
|
+
tmp_folder_len +=
|
1344
|
+
fio_ltoa(facil_cluster_data.cluster_name + tmp_folder_len, getpid(), 8);
|
1345
|
+
facil_cluster_data.cluster_name[tmp_folder_len] = 0;
|
1346
|
+
|
1347
|
+
/* remove if existing */
|
1348
|
+
unlink(facil_cluster_data.cluster_name);
|
1349
|
+
/* create, bind, listen */
|
1350
|
+
facil_cluster_data.root = sock_listen(facil_cluster_data.cluster_name, NULL);
|
1351
|
+
|
1352
|
+
if (facil_cluster_data.root == -1) {
|
1353
|
+
perror("FATAL ERROR: (facil.io cluster) failed to open cluster socket.\n"
|
1354
|
+
" check file permissions");
|
930
1355
|
return -1;
|
931
|
-
|
932
|
-
|
1356
|
+
}
|
1357
|
+
return 0;
|
1358
|
+
}
|
1359
|
+
|
1360
|
+
void facil_cluster_set_handler(int32_t filter,
|
1361
|
+
void (*on_message)(int32_t id, FIOBJ ch,
|
1362
|
+
FIOBJ msg)) {
|
1363
|
+
spn_lock(&facil_cluster_data.lock);
|
1364
|
+
fio_hash_insert(&facil_cluster_data.handlers, (uint64_t)filter,
|
1365
|
+
(void *)(uintptr_t)on_message);
|
1366
|
+
spn_unlock(&facil_cluster_data.lock);
|
1367
|
+
}
|
1368
|
+
|
1369
|
+
int facil_cluster_send(int32_t filter, FIOBJ ch, FIOBJ msg) {
|
1370
|
+
if (!facil_data) {
|
1371
|
+
fprintf(stderr, "ERROR: cluster inactive, can't send message.\n");
|
933
1372
|
return -1;
|
934
|
-
msg->count = facil_cluster_data.count - 1;
|
935
|
-
msg->lock = SPN_LOCK_INIT;
|
936
|
-
msg->payload.len = len + sizeof(msg->payload);
|
937
|
-
msg->payload.msg_type = msg_type;
|
938
|
-
memcpy(msg->data, data, len);
|
939
|
-
for (int i = 0; i < (int)facil_cluster_data.count; i++) {
|
940
|
-
if (defer_fork_pid() == i)
|
941
|
-
continue;
|
942
|
-
if (sock_write2(.uuid = facil_cluster_data.pipes[i].out, .buffer = msg,
|
943
|
-
.offset =
|
944
|
-
((uintptr_t) &
|
945
|
-
(((struct facil_cluster_msg_packet *)(0))->payload)),
|
946
|
-
.length = msg->payload.len, .move = 1,
|
947
|
-
.dealloc = facil_msg_free))
|
948
|
-
perror("ERROR: Cluster `write` failed");
|
949
1373
|
}
|
1374
|
+
uint32_t type = CLUSTER_MESSAGE_FORWARD;
|
1375
|
+
|
1376
|
+
if ((!ch || FIOBJ_TYPE_IS(ch, FIOBJ_T_STRING)) &&
|
1377
|
+
(!msg || FIOBJ_TYPE_IS(msg, FIOBJ_T_STRING))) {
|
1378
|
+
fiobj_dup(ch);
|
1379
|
+
fiobj_dup(msg);
|
1380
|
+
} else {
|
1381
|
+
type = CLUSTER_MESSAGE_JSON;
|
1382
|
+
ch = fiobj_obj2json(ch, 0);
|
1383
|
+
msg = fiobj_obj2json(msg, 0);
|
1384
|
+
}
|
1385
|
+
fio_cstr_s cs = fiobj_obj2cstr(ch);
|
1386
|
+
fio_cstr_s ms = fiobj_obj2cstr(msg);
|
1387
|
+
cluster_send2traget((uint32_t)cs.len, (uint32_t)ms.len, type, filter,
|
1388
|
+
cs.bytes, ms.bytes);
|
1389
|
+
fiobj_free(ch);
|
1390
|
+
fiobj_free(msg);
|
950
1391
|
return 0;
|
951
1392
|
}
|
952
1393
|
|
1394
|
+
static void facil_cluster_cleanup(void) {
|
1395
|
+
fio_hash_free(&facil_cluster_data.handlers);
|
1396
|
+
fio_hash_free(&facil_cluster_data.clients);
|
1397
|
+
}
|
953
1398
|
/* *****************************************************************************
|
954
1399
|
Running the server
|
955
1400
|
***************************************************************************** */
|
956
1401
|
|
1402
|
+
volatile uint8_t facil_signal_children_flag = 0;
|
1403
|
+
|
1404
|
+
static inline void facil_internal_poll(void) {
|
1405
|
+
if (facil_signal_children_flag) {
|
1406
|
+
facil_signal_children_flag = 0;
|
1407
|
+
facil_cluster_signal_children();
|
1408
|
+
}
|
1409
|
+
}
|
1410
|
+
|
1411
|
+
static inline void facil_internal_poll_reset(void) {
|
1412
|
+
facil_signal_children_flag = 0;
|
1413
|
+
}
|
1414
|
+
|
957
1415
|
static void print_pid(void *arg, void *ignr) {
|
958
1416
|
(void)arg;
|
959
1417
|
(void)ignr;
|
@@ -963,7 +1421,7 @@ static void print_pid(void *arg, void *ignr) {
|
|
963
1421
|
static void facil_review_timeout(void *arg, void *ignr) {
|
964
1422
|
(void)ignr;
|
965
1423
|
protocol_s *tmp;
|
966
|
-
time_t review = facil_data->last_cycle;
|
1424
|
+
time_t review = facil_data->last_cycle.tv_sec;
|
967
1425
|
intptr_t fd = (intptr_t)arg;
|
968
1426
|
|
969
1427
|
uint16_t timeout = fd_data(fd).timeout;
|
@@ -978,7 +1436,7 @@ static void facil_review_timeout(void *arg, void *ignr) {
|
|
978
1436
|
if (prt_meta(tmp).locks[FIO_PR_LOCK_TASK] ||
|
979
1437
|
prt_meta(tmp).locks[FIO_PR_LOCK_WRITE])
|
980
1438
|
goto unlock;
|
981
|
-
defer(deferred_ping, (void *)sock_fd2uuid(fd), NULL);
|
1439
|
+
defer(deferred_ping, (void *)sock_fd2uuid((int)fd), NULL);
|
982
1440
|
unlock:
|
983
1441
|
protocol_unlock(tmp, FIO_PR_LOCK_STATE);
|
984
1442
|
finish:
|
@@ -994,75 +1452,432 @@ reschedule:
|
|
994
1452
|
defer(facil_review_timeout, (void *)fd, NULL);
|
995
1453
|
}
|
996
1454
|
|
997
|
-
static void
|
1455
|
+
static void perform_idle(void *arg, void *ignr) {
|
1456
|
+
facil_data->on_idle();
|
1457
|
+
(void)arg;
|
998
1458
|
(void)ignr;
|
1459
|
+
}
|
1460
|
+
|
1461
|
+
/* reactor pattern cycling - common */
|
1462
|
+
static void facil_cycle_schedule_events(void) {
|
999
1463
|
static int idle = 0;
|
1000
|
-
|
1464
|
+
clock_gettime(CLOCK_REALTIME, &facil_data->last_cycle);
|
1465
|
+
facil_internal_poll();
|
1001
1466
|
int events;
|
1002
1467
|
if (defer_has_queue()) {
|
1003
1468
|
events = evio_review(0);
|
1004
|
-
if (events < 0)
|
1005
|
-
|
1006
|
-
|
1469
|
+
if (events < 0) {
|
1470
|
+
return;
|
1471
|
+
}
|
1472
|
+
if (events > 0) {
|
1007
1473
|
idle = 1;
|
1474
|
+
}
|
1008
1475
|
} else {
|
1009
|
-
events = evio_review(
|
1476
|
+
events = evio_review(EVIO_TICK);
|
1010
1477
|
if (events < 0)
|
1011
|
-
|
1478
|
+
return;
|
1012
1479
|
if (events > 0) {
|
1013
1480
|
idle = 1;
|
1014
1481
|
} else if (idle) {
|
1015
|
-
(
|
1482
|
+
defer(perform_idle, NULL, NULL);
|
1016
1483
|
idle = 0;
|
1017
1484
|
}
|
1018
1485
|
}
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1486
|
+
static time_t last_to_review = 0;
|
1487
|
+
if (facil_data->need_review &&
|
1488
|
+
facil_data->last_cycle.tv_sec != last_to_review) {
|
1489
|
+
last_to_review = facil_data->last_cycle.tv_sec;
|
1022
1490
|
facil_data->need_review = 0;
|
1023
1491
|
defer(facil_review_timeout, (void *)0, NULL);
|
1024
1492
|
}
|
1025
|
-
defer(facil_cycle, arg, NULL);
|
1026
|
-
error:
|
1027
|
-
(void)1;
|
1028
1493
|
}
|
1029
1494
|
|
1030
|
-
|
1031
|
-
|
1032
|
-
(
|
1495
|
+
/* reactor pattern cycling during cleanup */
|
1496
|
+
static void facil_cycle_unwind(void *ignr, void *ignr2) {
|
1497
|
+
if (facil_data->connection_count) {
|
1498
|
+
facil_cycle_schedule_events();
|
1499
|
+
defer(facil_cycle_unwind, ignr, ignr2);
|
1500
|
+
return;
|
1501
|
+
}
|
1502
|
+
pool_pt pool = facil_data->thread_pool;
|
1503
|
+
facil_data->thread_pool = NULL;
|
1504
|
+
defer_pool_stop(pool);
|
1505
|
+
return;
|
1506
|
+
(void)ignr;
|
1507
|
+
(void)ignr2;
|
1508
|
+
}
|
1509
|
+
|
1510
|
+
/* reactor pattern cycling */
|
1511
|
+
static void facil_cycle(void *ignr, void *ignr2) {
|
1512
|
+
facil_cycle_schedule_events();
|
1513
|
+
if (facil_data->active) {
|
1514
|
+
defer(facil_cycle, ignr, ignr2);
|
1515
|
+
return;
|
1516
|
+
}
|
1517
|
+
/* switch to winding down */
|
1518
|
+
if (FACIL_PRINT_STATE && !facil_data->spindown) {
|
1519
|
+
pid_t pid = getpid();
|
1520
|
+
if (pid != facil_data->parent)
|
1521
|
+
fprintf(stderr, "* (%d) Detected exit signal.\n", getpid());
|
1522
|
+
else
|
1523
|
+
fprintf(stderr, "* Server Detected exit signal.\n");
|
1524
|
+
}
|
1525
|
+
pool_pt pool = facil_data->thread_pool;
|
1526
|
+
facil_data->thread_pool = NULL;
|
1527
|
+
defer_pool_stop(pool);
|
1528
|
+
return;
|
1529
|
+
(void)ignr;
|
1530
|
+
(void)ignr2;
|
1531
|
+
}
|
1532
|
+
|
1533
|
+
/**
|
1534
|
+
OVERRIDE THIS to replace the default `fork` implementation or to inject hooks
|
1535
|
+
into the forking function.
|
1536
|
+
|
1537
|
+
Behaves like the system's `fork`.
|
1538
|
+
*/
|
1539
|
+
#pragma weak facil_fork
|
1540
|
+
int facil_fork(void) { return (int)fork(); }
|
1541
|
+
|
1542
|
+
/** This will be called by child processes, make sure to unlock any existing
|
1543
|
+
* locks.
|
1544
|
+
*
|
1545
|
+
* Known locks:
|
1546
|
+
* * `defer` tasks lock.
|
1547
|
+
* * `sock` packet pool lock.
|
1548
|
+
* * `sock` connection lock (per connection).
|
1549
|
+
* * `facil` global lock.
|
1550
|
+
* * `facil` pub/sub lock.
|
1551
|
+
* * `facil` connection data lock (per connection data).
|
1552
|
+
* * `facil` protocol lock (per protocol object, placed in `rsv`).
|
1553
|
+
* * `pubsub` pubsub global lock (should be initialized in facil_external_init.
|
1554
|
+
* * `pubsub` pubsub client lock (should be initialized in facil_external_init.
|
1555
|
+
*/
|
1556
|
+
static void facil_worker_startup(uint8_t sentinel) {
|
1557
|
+
facil_cluster_data.lock = facil_data->global_lock = SPN_LOCK_INIT;
|
1558
|
+
facil_internal_poll_reset();
|
1033
1559
|
evio_create();
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1560
|
+
clock_gettime(CLOCK_REALTIME, &facil_data->last_cycle);
|
1561
|
+
facil_external_init();
|
1562
|
+
if (facil_data->active == 1) {
|
1563
|
+
/* single process */
|
1564
|
+
for (int i = 0; i < facil_data->capacity; i++) {
|
1565
|
+
errno = 0;
|
1566
|
+
fd_data(i).lock = SPN_LOCK_INIT;
|
1567
|
+
if (fd_data(i).protocol) {
|
1568
|
+
fd_data(i).protocol->rsv = 0;
|
1569
|
+
if (fd_data(i).protocol->service == LISTENER_PROTOCOL_NAME)
|
1570
|
+
listener_on_start(i);
|
1571
|
+
else if (fd_data(i).protocol->service == TIMER_PROTOCOL_NAME)
|
1572
|
+
timer_on_server_start(i);
|
1573
|
+
else {
|
1574
|
+
evio_add(i, (void *)sock_fd2uuid(i));
|
1575
|
+
}
|
1576
|
+
}
|
1043
1577
|
}
|
1578
|
+
} else if (sentinel == 0) {
|
1579
|
+
/* child process */
|
1580
|
+
for (int i = 0; i < facil_data->capacity; i++) {
|
1581
|
+
errno = 0;
|
1582
|
+
fd_data(i).lock = SPN_LOCK_INIT;
|
1583
|
+
if (fd_data(i).protocol) {
|
1584
|
+
fd_data(i).protocol->rsv = 0;
|
1585
|
+
if (fd_data(i).protocol->service == LISTENER_PROTOCOL_NAME)
|
1586
|
+
listener_on_start(i);
|
1587
|
+
else if (fd_data(i).protocol->service == TIMER_PROTOCOL_NAME)
|
1588
|
+
timer_on_server_start(i);
|
1589
|
+
else {
|
1590
|
+
/* prevent normal connections from being shared across workers */
|
1591
|
+
intptr_t uuid = sock_fd2uuid(i);
|
1592
|
+
if (uuid != -1) {
|
1593
|
+
sock_force_close(uuid);
|
1594
|
+
} else {
|
1595
|
+
sock_on_close(uuid);
|
1596
|
+
}
|
1597
|
+
}
|
1598
|
+
}
|
1599
|
+
}
|
1600
|
+
} else {
|
1601
|
+
/* sentinel process - ignore listening sockets, but keep them open */
|
1602
|
+
for (int i = 0; i < facil_data->capacity; i++) {
|
1603
|
+
fd_data(i).lock = SPN_LOCK_INIT;
|
1604
|
+
if (fd_data(i).protocol) {
|
1605
|
+
fd_data(i).protocol->rsv = 0;
|
1606
|
+
if (fd_data(i).protocol->service == TIMER_PROTOCOL_NAME)
|
1607
|
+
timer_on_server_start(i);
|
1608
|
+
else if (fd_data(i).protocol->service != LISTENER_PROTOCOL_NAME) {
|
1609
|
+
evio_add(i, (void *)sock_fd2uuid(i));
|
1610
|
+
}
|
1611
|
+
}
|
1612
|
+
}
|
1613
|
+
}
|
1614
|
+
/* Clear all existing events & flush `facil_cycle` out. */
|
1615
|
+
{
|
1616
|
+
facil_data->spindown = 1;
|
1617
|
+
uint16_t old_active = facil_data->active;
|
1618
|
+
facil_data->active = 0;
|
1619
|
+
defer_perform();
|
1620
|
+
facil_data->active = old_active;
|
1621
|
+
facil_data->spindown = 0;
|
1622
|
+
}
|
1623
|
+
/* called after connection cleanup, as it should open connections. */
|
1624
|
+
if (cluster_on_start()) {
|
1625
|
+
facil_data->thread_pool = NULL;
|
1626
|
+
return;
|
1044
1627
|
}
|
1628
|
+
/* call any external startup callbacks. */
|
1629
|
+
facil_external_init2();
|
1630
|
+
/* add cycling to the defer queue to setup the reactor pattern. */
|
1045
1631
|
facil_data->need_review = 1;
|
1046
|
-
|
1047
|
-
defer(facil_cycle, arg, NULL);
|
1048
|
-
}
|
1632
|
+
defer(facil_cycle, NULL, NULL);
|
1049
1633
|
|
1050
|
-
|
1634
|
+
if (FACIL_PRINT_STATE) {
|
1635
|
+
if (sentinel || facil_data->parent == getpid()) {
|
1636
|
+
fprintf(stderr,
|
1637
|
+
"Server is running %u %s X %u %s, press ^C to stop\n"
|
1638
|
+
"* Detected capacity: %zd open file limit\n"
|
1639
|
+
"* Root pid: %d\n",
|
1640
|
+
facil_data->active, facil_data->active > 1 ? "workers" : "worker",
|
1641
|
+
facil_data->threads,
|
1642
|
+
facil_data->threads > 1 ? "threads" : "thread",
|
1643
|
+
facil_data->capacity, facil_data->parent);
|
1644
|
+
} else {
|
1645
|
+
defer(print_pid, NULL, NULL);
|
1646
|
+
}
|
1647
|
+
}
|
1648
|
+
facil_data->thread_pool =
|
1649
|
+
defer_pool_start((sentinel ? 1 : facil_data->threads));
|
1650
|
+
if (facil_data->thread_pool)
|
1651
|
+
defer_pool_wait(facil_data->thread_pool);
|
1652
|
+
}
|
1653
|
+
|
1654
|
+
static void facil_worker_cleanup(void) {
|
1655
|
+
facil_data->active = 0;
|
1656
|
+
facil_cluster_signal_children();
|
1657
|
+
for (int i = 0; i <= facil_data->capacity; ++i) {
|
1658
|
+
intptr_t uuid;
|
1659
|
+
if (fd_data(i).protocol && is_counted_protocol(fd_data(i).protocol) &&
|
1660
|
+
(uuid = sock_fd2uuid(i)) >= 0) {
|
1661
|
+
defer(deferred_on_shutdown, (void *)uuid, NULL);
|
1662
|
+
}
|
1663
|
+
}
|
1664
|
+
facil_data->thread_pool = defer_pool_start(facil_data->threads);
|
1665
|
+
if (facil_data->thread_pool) {
|
1666
|
+
defer(facil_cycle_unwind, NULL, NULL);
|
1667
|
+
defer_pool_wait(facil_data->thread_pool);
|
1668
|
+
facil_data->thread_pool = NULL;
|
1669
|
+
}
|
1051
1670
|
fprintf(stderr, "* %d cleanning up.\n", getpid());
|
1052
|
-
|
1053
|
-
for (
|
1671
|
+
/* close leftovers */
|
1672
|
+
for (int i = 0; i <= facil_data->capacity; ++i) {
|
1673
|
+
intptr_t uuid;
|
1054
1674
|
if (fd_data(i).protocol && (uuid = sock_fd2uuid(i)) >= 0) {
|
1055
|
-
|
1675
|
+
sock_force_close(uuid);
|
1056
1676
|
}
|
1057
1677
|
}
|
1058
|
-
facil_cycle(arg, NULL);
|
1059
1678
|
defer_perform();
|
1060
|
-
|
1061
|
-
((struct facil_run_args *)arg)->on_finish();
|
1679
|
+
facil_data->on_finish();
|
1062
1680
|
defer_perform();
|
1063
1681
|
evio_close();
|
1064
|
-
facil_cluster_destroy();
|
1065
1682
|
facil_external_cleanup();
|
1683
|
+
|
1684
|
+
if (facil_data->parent == getpid()) {
|
1685
|
+
kill(0, SIGINT);
|
1686
|
+
while (wait(NULL) != -1)
|
1687
|
+
;
|
1688
|
+
if (FACIL_PRINT_STATE) {
|
1689
|
+
fprintf(stderr, "\n --- Shutdown Complete ---\n");
|
1690
|
+
}
|
1691
|
+
}
|
1692
|
+
}
|
1693
|
+
|
1694
|
+
static void facil_sentinel_task(void *arg1, void *arg2);
|
1695
|
+
static void *facil_sentinel_worker_thread(void *arg) {
|
1696
|
+
errno = 0;
|
1697
|
+
pid_t child = facil_fork();
|
1698
|
+
if (arg && child) {
|
1699
|
+
spn_unlock((spn_lock_i *)arg);
|
1700
|
+
}
|
1701
|
+
if (child == -1) {
|
1702
|
+
perror("FATAL ERROR: couldn't spawn worker.");
|
1703
|
+
kill(facil_parent_pid(), SIGINT);
|
1704
|
+
facil_stop();
|
1705
|
+
return NULL;
|
1706
|
+
} else if (child) {
|
1707
|
+
int status;
|
1708
|
+
waitpid(child, &status, 0);
|
1709
|
+
#if DEBUG
|
1710
|
+
if (facil_data->active) { /* !WIFEXITED(status) || WEXITSTATUS(status) */
|
1711
|
+
if (!WIFEXITED(status) || WEXITSTATUS(status)) {
|
1712
|
+
fprintf(stderr,
|
1713
|
+
"FATAL ERROR: Child worker (%d) crashed. Stopping services in "
|
1714
|
+
"DEBUG mode.\n",
|
1715
|
+
child);
|
1716
|
+
} else {
|
1717
|
+
fprintf(
|
1718
|
+
stderr,
|
1719
|
+
"INFO (FATAL): Child worker (%d) shutdown. Stopping services in "
|
1720
|
+
"DEBUG mode.\n",
|
1721
|
+
child);
|
1722
|
+
}
|
1723
|
+
kill(0, SIGINT);
|
1724
|
+
}
|
1725
|
+
#else
|
1726
|
+
if (facil_data->active) {
|
1727
|
+
if (!WIFEXITED(status) || WEXITSTATUS(status)) {
|
1728
|
+
fprintf(stderr,
|
1729
|
+
"ERROR: Child worker (%d) crashed. Respawning worker.\n",
|
1730
|
+
child);
|
1731
|
+
} else {
|
1732
|
+
fprintf(stderr,
|
1733
|
+
"INFO: Child worker (%d) shutdown. Respawning worker.\n",
|
1734
|
+
child);
|
1735
|
+
}
|
1736
|
+
defer(facil_sentinel_task, NULL, NULL);
|
1737
|
+
}
|
1738
|
+
#endif
|
1739
|
+
} else {
|
1740
|
+
facil_worker_startup(0);
|
1741
|
+
facil_worker_cleanup();
|
1742
|
+
exit(0);
|
1743
|
+
}
|
1744
|
+
return NULL;
|
1745
|
+
}
|
1746
|
+
|
1747
|
+
#if FIO_SENTINEL_USE_PTHREAD
|
1748
|
+
static void facil_sentinel_task(void *arg1, void *arg2) {
|
1749
|
+
if (!facil_data->active)
|
1750
|
+
return;
|
1751
|
+
spn_lock_i pre_fork_lock = SPN_LOCK_INIT;
|
1752
|
+
spn_lock(&pre_fork_lock);
|
1753
|
+
pthread_t sentinel;
|
1754
|
+
if (pthread_create(&sentinel, NULL, facil_sentinel_worker_thread,
|
1755
|
+
(void *)&pre_fork_lock)) {
|
1756
|
+
perror("FATAL ERROR: couldn't start sentinel thread");
|
1757
|
+
exit(errno);
|
1758
|
+
}
|
1759
|
+
pthread_detach(sentinel);
|
1760
|
+
spn_lock(&pre_fork_lock); /* will wait for worker thread to release lock. */
|
1761
|
+
facil_cluster_data.listening.on_data(facil_cluster_data.root,
|
1762
|
+
&facil_cluster_data.listening);
|
1763
|
+
(void)arg1;
|
1764
|
+
(void)arg2;
|
1765
|
+
}
|
1766
|
+
#else
|
1767
|
+
static void facil_sentinel_task(void *arg1, void *arg2) {
|
1768
|
+
if (!facil_data->active)
|
1769
|
+
return;
|
1770
|
+
spn_lock_i pre_fork_lock = SPN_LOCK_INIT;
|
1771
|
+
spn_lock(&pre_fork_lock);
|
1772
|
+
void *thrd =
|
1773
|
+
defer_new_thread(facil_sentinel_worker_thread, (void *)&pre_fork_lock);
|
1774
|
+
defer_free_thread(thrd);
|
1775
|
+
spn_lock(&pre_fork_lock); /* will wait for worker thread to release lock. */
|
1776
|
+
facil_cluster_data.listening.on_data(facil_cluster_data.root,
|
1777
|
+
&facil_cluster_data.listening);
|
1778
|
+
(void)arg1;
|
1779
|
+
(void)arg2;
|
1780
|
+
}
|
1781
|
+
#endif
|
1782
|
+
|
1783
|
+
/* handles the SIGUSR1, SIGINT and SIGTERM signals. */
|
1784
|
+
static void sig_int_handler(int sig) {
|
1785
|
+
switch (sig) {
|
1786
|
+
#if !FACIL_DISABLE_HOT_RESTART
|
1787
|
+
case SIGUSR1:
|
1788
|
+
facil_signal_children_flag = 1;
|
1789
|
+
break;
|
1790
|
+
#endif
|
1791
|
+
case SIGINT: /* fallthrough */
|
1792
|
+
case SIGTERM: /* fallthrough */
|
1793
|
+
facil_stop();
|
1794
|
+
break;
|
1795
|
+
default:
|
1796
|
+
break;
|
1797
|
+
}
|
1798
|
+
}
|
1799
|
+
|
1800
|
+
/* setup handling for the SIGUSR1, SIGPIPE, SIGINT and SIGTERM signals. */
|
1801
|
+
static void facil_setup_signal_handler(void) {
|
1802
|
+
/* setup signal handling */
|
1803
|
+
struct sigaction act, old;
|
1804
|
+
|
1805
|
+
act.sa_handler = sig_int_handler;
|
1806
|
+
sigemptyset(&act.sa_mask);
|
1807
|
+
act.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
1808
|
+
|
1809
|
+
if (sigaction(SIGINT, &act, &old)) {
|
1810
|
+
perror("couldn't set signal handler");
|
1811
|
+
return;
|
1812
|
+
};
|
1813
|
+
|
1814
|
+
if (sigaction(SIGTERM, &act, &old)) {
|
1815
|
+
perror("couldn't set signal handler");
|
1816
|
+
return;
|
1817
|
+
};
|
1818
|
+
#if !FACIL_DISABLE_HOT_RESTART
|
1819
|
+
if (sigaction(SIGUSR1, &act, &old)) {
|
1820
|
+
perror("couldn't set signal handler");
|
1821
|
+
return;
|
1822
|
+
};
|
1823
|
+
#endif
|
1824
|
+
|
1825
|
+
act.sa_handler = SIG_IGN;
|
1826
|
+
if (sigaction(SIGPIPE, &act, &old)) {
|
1827
|
+
perror("couldn't set signal handler");
|
1828
|
+
return;
|
1829
|
+
};
|
1830
|
+
}
|
1831
|
+
|
1832
|
+
/*
|
1833
|
+
* Zombie Reaping
|
1834
|
+
* With thanks to Dr Graham D Shaw.
|
1835
|
+
* http://www.microhowto.info/howto/reap_zombie_processes_using_a_sigchld_handler.html
|
1836
|
+
*/
|
1837
|
+
static void reap_child_handler(int sig) {
|
1838
|
+
(void)(sig);
|
1839
|
+
int old_errno = errno;
|
1840
|
+
while (waitpid(-1, NULL, WNOHANG) > 0)
|
1841
|
+
;
|
1842
|
+
errno = old_errno;
|
1843
|
+
}
|
1844
|
+
|
1845
|
+
/* initializes zombie reaping for the process */
|
1846
|
+
void facil_reap_children(void) {
|
1847
|
+
struct sigaction sa;
|
1848
|
+
sa.sa_handler = reap_child_handler;
|
1849
|
+
sigemptyset(&sa.sa_mask);
|
1850
|
+
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
1851
|
+
if (sigaction(SIGCHLD, &sa, 0) == -1) {
|
1852
|
+
perror("Child reaping initialization failed");
|
1853
|
+
kill(0, SIGINT);
|
1854
|
+
exit(errno);
|
1855
|
+
}
|
1856
|
+
}
|
1857
|
+
|
1858
|
+
/** returns facil.io's parent (root) process pid. */
|
1859
|
+
pid_t facil_parent_pid(void) {
|
1860
|
+
if (!facil_data)
|
1861
|
+
facil_lib_init();
|
1862
|
+
return facil_data->parent;
|
1863
|
+
}
|
1864
|
+
|
1865
|
+
static inline size_t facil_detect_cpu_cores(void) {
|
1866
|
+
ssize_t cpu_count = 0;
|
1867
|
+
#ifdef _SC_NPROCESSORS_ONLN
|
1868
|
+
cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
|
1869
|
+
if (cpu_count < 0) {
|
1870
|
+
if (FACIL_PRINT_STATE) {
|
1871
|
+
fprintf(stderr, "WARNING: CPU core count auto-detection failed.\n");
|
1872
|
+
}
|
1873
|
+
return 0;
|
1874
|
+
}
|
1875
|
+
#else
|
1876
|
+
if (1 || FACIL_PRINT_STATE) {
|
1877
|
+
fprintf(stderr, "WARNING: CPU core count auto-detection failed.\n");
|
1878
|
+
}
|
1879
|
+
#endif
|
1880
|
+
return cpu_count;
|
1066
1881
|
}
|
1067
1882
|
|
1068
1883
|
#undef facil_run
|
@@ -1074,60 +1889,106 @@ void facil_run(struct facil_run_args args) {
|
|
1074
1889
|
args.on_idle = mock_idle;
|
1075
1890
|
if (!args.on_finish)
|
1076
1891
|
args.on_finish = mock_idle;
|
1077
|
-
#ifdef _SC_NPROCESSORS_ONLN
|
1078
1892
|
if (!args.threads && !args.processes) {
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1893
|
+
/* both options set to 0 - default to cores*cores matrix */
|
1894
|
+
ssize_t cpu_count = facil_detect_cpu_cores();
|
1895
|
+
#if FACIL_CPU_CORES_LIMIT
|
1896
|
+
if (cpu_count > FACIL_CPU_CORES_LIMIT) {
|
1897
|
+
fprintf(
|
1898
|
+
stderr,
|
1899
|
+
"INFO: Detected %zu cores. Capping auto-detection of cores "
|
1900
|
+
"to %zu.\n"
|
1901
|
+
" Avoid this message by setting threads / workers manually.\n"
|
1902
|
+
" To increase auto-detection limit, recompile with:\n"
|
1903
|
+
" -DFACIL_CPU_CORES_LIMIT=%zu \n",
|
1904
|
+
(size_t)cpu_count, (size_t)FACIL_CPU_CORES_LIMIT, (size_t)cpu_count);
|
1905
|
+
cpu_count = FACIL_CPU_CORES_LIMIT;
|
1906
|
+
}
|
1083
1907
|
#endif
|
1084
|
-
|
1908
|
+
args.threads = args.processes = (int16_t)cpu_count;
|
1909
|
+
} else if (args.threads < 0 || args.processes < 0) {
|
1910
|
+
/* Set any option that is less than 0 be equal to cores/value */
|
1911
|
+
/* Set any option equal to 0 be equal to the other option in value */
|
1912
|
+
ssize_t cpu_count = facil_detect_cpu_cores();
|
1913
|
+
|
1914
|
+
if (cpu_count > 0) {
|
1915
|
+
int16_t threads = 0;
|
1916
|
+
if (args.threads < 0)
|
1917
|
+
threads = (int16_t)(cpu_count / (args.threads * -1));
|
1918
|
+
else if (args.threads == 0)
|
1919
|
+
threads = -1 * args.processes;
|
1920
|
+
if (args.processes < 0)
|
1921
|
+
args.processes = (int16_t)(cpu_count / (args.processes * -1));
|
1922
|
+
else if (args.processes == 0)
|
1923
|
+
args.processes = -1 * args.threads;
|
1924
|
+
args.threads = threads;
|
1925
|
+
}
|
1926
|
+
}
|
1927
|
+
|
1928
|
+
/* make sure we have at least one process and at least one thread */
|
1929
|
+
if (args.processes <= 0)
|
1085
1930
|
args.processes = 1;
|
1086
|
-
if (
|
1931
|
+
if (args.threads <= 0)
|
1087
1932
|
args.threads = 1;
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
if (
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1933
|
+
|
1934
|
+
/* listen to SIGINT / SIGTERM */
|
1935
|
+
facil_setup_signal_handler();
|
1936
|
+
|
1937
|
+
/* activate facil, fork if needed */
|
1938
|
+
facil_data->active = (uint16_t)args.processes;
|
1939
|
+
facil_data->threads = (uint16_t)args.threads;
|
1940
|
+
facil_data->on_finish = args.on_finish;
|
1941
|
+
facil_data->on_idle = args.on_idle;
|
1942
|
+
/* initialize cluster */
|
1943
|
+
if (args.processes > 1) {
|
1944
|
+
if (facil_cluster_init()) {
|
1945
|
+
kill(0, SIGINT);
|
1946
|
+
if (FACIL_PRINT_STATE) {
|
1947
|
+
fprintf(stderr, "\n !!! Crashed trying to "
|
1948
|
+
"start the service !!!\n");
|
1949
|
+
}
|
1950
|
+
exit(-1);
|
1951
|
+
}
|
1952
|
+
for (int i = 0; i < args.processes && facil_data->active; ++i) {
|
1953
|
+
facil_sentinel_task(NULL, NULL);
|
1954
|
+
}
|
1955
|
+
facil_worker_startup(1);
|
1956
|
+
} else {
|
1957
|
+
facil_worker_startup(0);
|
1102
1958
|
}
|
1103
|
-
|
1104
|
-
fprintf(stderr, "\n --- Completed Shutdown ---\n");
|
1959
|
+
facil_worker_cleanup();
|
1105
1960
|
}
|
1106
1961
|
|
1107
1962
|
/**
|
1108
1963
|
* returns true (1) if the facil.io engine is already running.
|
1109
1964
|
*/
|
1110
|
-
int facil_is_running(void) { return (facil_data &&
|
1965
|
+
int facil_is_running(void) { return (facil_data && facil_data->active > 0); }
|
1111
1966
|
|
1112
1967
|
/* *****************************************************************************
|
1113
1968
|
Setting the protocol
|
1114
1969
|
***************************************************************************** */
|
1115
1970
|
|
1971
|
+
/* managing the protocol pointer array and the `on_close` callback */
|
1116
1972
|
static int facil_attach_state(intptr_t uuid, protocol_s *protocol,
|
1117
1973
|
protocol_metadata_s state) {
|
1118
1974
|
if (!facil_data)
|
1119
1975
|
facil_lib_init();
|
1120
1976
|
if (protocol) {
|
1121
|
-
if (!protocol->on_close)
|
1977
|
+
if (!protocol->on_close) {
|
1122
1978
|
protocol->on_close = mock_on_close;
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1979
|
+
}
|
1980
|
+
if (!protocol->on_data) {
|
1981
|
+
protocol->on_data = mock_on_data;
|
1982
|
+
}
|
1983
|
+
if (!protocol->on_ready) {
|
1126
1984
|
protocol->on_ready = mock_on_ev;
|
1127
|
-
|
1985
|
+
}
|
1986
|
+
if (!protocol->ping) {
|
1128
1987
|
protocol->ping = mock_ping;
|
1129
|
-
|
1988
|
+
}
|
1989
|
+
if (!protocol->on_shutdown) {
|
1130
1990
|
protocol->on_shutdown = mock_on_ev;
|
1991
|
+
}
|
1131
1992
|
prt_meta(protocol) = state;
|
1132
1993
|
}
|
1133
1994
|
spn_lock(&uuid_data(uuid).lock);
|
@@ -1135,16 +1996,28 @@ static int facil_attach_state(intptr_t uuid, protocol_s *protocol,
|
|
1135
1996
|
spn_unlock(&uuid_data(uuid).lock);
|
1136
1997
|
if (protocol)
|
1137
1998
|
defer(deferred_on_close, (void *)uuid, protocol);
|
1999
|
+
if (uuid == -1)
|
2000
|
+
errno = EBADF;
|
2001
|
+
else
|
2002
|
+
errno = ENOTCONN;
|
1138
2003
|
return -1;
|
2004
|
+
} else {
|
2005
|
+
if (is_counted_protocol(protocol)) {
|
2006
|
+
spn_add(&facil_data->connection_count, 1);
|
2007
|
+
}
|
1139
2008
|
}
|
1140
2009
|
protocol_s *old_protocol = uuid_data(uuid).protocol;
|
1141
2010
|
uuid_data(uuid).protocol = protocol;
|
1142
|
-
uuid_data(uuid).active = facil_data->last_cycle;
|
2011
|
+
uuid_data(uuid).active = facil_data->last_cycle.tv_sec;
|
1143
2012
|
spn_unlock(&uuid_data(uuid).lock);
|
1144
|
-
if (old_protocol)
|
2013
|
+
if (old_protocol) {
|
2014
|
+
if (is_counted_protocol(old_protocol)) {
|
2015
|
+
spn_sub(&facil_data->connection_count, 1);
|
2016
|
+
}
|
1145
2017
|
defer(deferred_on_close, (void *)uuid, old_protocol);
|
1146
|
-
if (evio_isactive())
|
1147
|
-
evio_add(sock_uuid2fd(uuid), (void *)uuid);
|
2018
|
+
} else if (evio_isactive()) {
|
2019
|
+
return evio_add(sock_uuid2fd(uuid), (void *)uuid);
|
2020
|
+
}
|
1148
2021
|
return 0;
|
1149
2022
|
}
|
1150
2023
|
|
@@ -1169,7 +2042,7 @@ int facil_attach_locked(intptr_t uuid, protocol_s *protocol) {
|
|
1169
2042
|
/** Sets a timeout for a specific connection (if active). */
|
1170
2043
|
void facil_set_timeout(intptr_t uuid, uint8_t timeout) {
|
1171
2044
|
if (sock_isvalid(uuid)) {
|
1172
|
-
uuid_data(uuid).active = facil_data->last_cycle;
|
2045
|
+
uuid_data(uuid).active = facil_data->last_cycle.tv_sec;
|
1173
2046
|
uuid_data(uuid).timeout = timeout;
|
1174
2047
|
}
|
1175
2048
|
}
|
@@ -1184,8 +2057,12 @@ Misc helpers
|
|
1184
2057
|
/**
|
1185
2058
|
Returns the last time the server reviewed any pending IO events.
|
1186
2059
|
*/
|
1187
|
-
|
1188
|
-
|
2060
|
+
struct timespec facil_last_tick(void) {
|
2061
|
+
if (!facil_data) {
|
2062
|
+
facil_lib_init();
|
2063
|
+
clock_gettime(CLOCK_REALTIME, &facil_data->last_cycle);
|
2064
|
+
}
|
2065
|
+
return facil_data->last_cycle;
|
1189
2066
|
}
|
1190
2067
|
|
1191
2068
|
/**
|
@@ -1208,15 +2085,14 @@ void facil_protocol_unlock(protocol_s *pr, enum facil_protocol_lock_e type) {
|
|
1208
2085
|
}
|
1209
2086
|
/** Counts all the connections of a specific type. */
|
1210
2087
|
size_t facil_count(void *service) {
|
1211
|
-
|
1212
|
-
void *tmp;
|
2088
|
+
size_t count = 0;
|
1213
2089
|
for (intptr_t i = 0; i < facil_data->capacity; i++) {
|
1214
|
-
tmp = NULL;
|
2090
|
+
void *tmp = NULL;
|
1215
2091
|
spn_lock(&fd_data(i).lock);
|
1216
2092
|
if (fd_data(i).protocol && fd_data(i).protocol->service)
|
1217
2093
|
tmp = (void *)fd_data(i).protocol->service;
|
1218
2094
|
spn_unlock(&fd_data(i).lock);
|
1219
|
-
if (tmp !=
|
2095
|
+
if (tmp != LISTENER_PROTOCOL_NAME && tmp != TIMER_PROTOCOL_NAME &&
|
1220
2096
|
(!service || (tmp == service)))
|
1221
2097
|
count++;
|
1222
2098
|
}
|
@@ -1239,10 +2115,10 @@ struct task {
|
|
1239
2115
|
};
|
1240
2116
|
|
1241
2117
|
static inline struct task *alloc_facil_task(void) {
|
1242
|
-
return
|
2118
|
+
return fio_malloc(sizeof(struct task));
|
1243
2119
|
}
|
1244
2120
|
|
1245
|
-
static inline void free_facil_task(struct task *task) {
|
2121
|
+
static inline void free_facil_task(struct task *task) { fio_free(task); }
|
1246
2122
|
|
1247
2123
|
static void mock_on_task_done(intptr_t uuid, void *arg) {
|
1248
2124
|
(void)uuid;
|
@@ -1256,7 +2132,7 @@ static void perform_single_task(void *v_uuid, void *v_task) {
|
|
1256
2132
|
protocol_s *pr = protocol_try_lock(sock_uuid2fd(v_uuid), task->task_type);
|
1257
2133
|
if (!pr)
|
1258
2134
|
goto defer;
|
1259
|
-
if (pr->service ==
|
2135
|
+
if (pr->service == CONNECTOR_PROTOCOL_NAME) {
|
1260
2136
|
protocol_unlock(pr, task->task_type);
|
1261
2137
|
goto defer;
|
1262
2138
|
}
|
@@ -1298,8 +2174,10 @@ static void perform_multi_task(void *v_fd, void *v_task) {
|
|
1298
2174
|
protocol_s *pr = protocol_try_lock((intptr_t)v_fd, task->task_type);
|
1299
2175
|
if (!pr)
|
1300
2176
|
goto reschedule;
|
1301
|
-
if (pr->service == task->service)
|
1302
|
-
|
2177
|
+
if (pr->service == task->service) {
|
2178
|
+
const intptr_t uuid = sock_fd2uuid((int)(intptr_t)v_fd);
|
2179
|
+
task->func(uuid, pr, task->arg);
|
2180
|
+
}
|
1303
2181
|
protocol_unlock(pr, task->task_type);
|
1304
2182
|
defer(finish_multi_task, v_fd, v_task);
|
1305
2183
|
return;
|
@@ -1350,8 +2228,8 @@ complete:
|
|
1350
2228
|
void facil_defer(struct facil_defer_args_s args) {
|
1351
2229
|
if (!args.fallback)
|
1352
2230
|
args.fallback = mock_on_task_done;
|
1353
|
-
if (!args.
|
1354
|
-
args.
|
2231
|
+
if (!args.type)
|
2232
|
+
args.type = FIO_PR_LOCK_TASK;
|
1355
2233
|
if (!args.task || !uuid_data(args.uuid).protocol || args.uuid < 0 ||
|
1356
2234
|
!sock_isvalid(args.uuid))
|
1357
2235
|
goto error;
|