iodine 0.2.17 → 0.3.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/CHANGELOG.md +6 -0
- data/README.md +36 -3
- data/bin/config.ru +23 -2
- data/bin/http-hello +1 -1
- data/bin/ws-shootout +5 -0
- data/ext/iodine/defer.c +468 -0
- data/ext/iodine/defer.h +105 -0
- data/ext/iodine/evio.c +263 -0
- data/ext/iodine/evio.h +133 -0
- data/ext/iodine/extconf.rb +2 -1
- data/ext/iodine/facil.c +958 -0
- data/ext/iodine/facil.h +423 -0
- data/ext/iodine/http.c +90 -0
- data/ext/iodine/http.h +50 -12
- data/ext/iodine/http1.c +200 -267
- data/ext/iodine/http1.h +17 -26
- data/ext/iodine/http1_request.c +81 -0
- data/ext/iodine/http1_request.h +58 -0
- data/ext/iodine/http1_response.c +403 -0
- data/ext/iodine/http1_response.h +90 -0
- data/ext/iodine/http1_simple_parser.c +124 -108
- data/ext/iodine/http1_simple_parser.h +8 -3
- data/ext/iodine/http_request.c +104 -0
- data/ext/iodine/http_request.h +58 -102
- data/ext/iodine/http_response.c +212 -208
- data/ext/iodine/http_response.h +89 -252
- data/ext/iodine/iodine_core.c +57 -46
- data/ext/iodine/iodine_core.h +3 -1
- data/ext/iodine/iodine_http.c +105 -81
- data/ext/iodine/iodine_websocket.c +17 -13
- data/ext/iodine/iodine_websocket.h +1 -0
- data/ext/iodine/rb-call.c +9 -7
- data/ext/iodine/{rb-libasync.h → rb-defer.c} +57 -49
- data/ext/iodine/rb-rack-io.c +12 -6
- data/ext/iodine/rb-rack-io.h +1 -1
- data/ext/iodine/rb-registry.c +5 -2
- data/ext/iodine/sock.c +1159 -0
- data/ext/iodine/{libsock.h → sock.h} +138 -142
- data/ext/iodine/spnlock.inc +77 -0
- data/ext/iodine/websockets.c +101 -112
- data/ext/iodine/websockets.h +38 -19
- data/iodine.gemspec +3 -3
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +6 -6
- metadata +23 -19
- data/ext/iodine/http_response_http1.h +0 -382
- data/ext/iodine/libasync.c +0 -570
- data/ext/iodine/libasync.h +0 -122
- data/ext/iodine/libreact.c +0 -350
- data/ext/iodine/libreact.h +0 -244
- data/ext/iodine/libserver.c +0 -957
- data/ext/iodine/libserver.h +0 -481
- data/ext/iodine/libsock.c +0 -1025
- data/ext/iodine/spnlock.h +0 -243
data/ext/iodine/extconf.rb
CHANGED
@@ -29,7 +29,8 @@ elsif find_executable('gcc-4.9')
|
|
29
29
|
$CPP = ENV['CPP'] = find_executable('g++-4.9') ? 'g++-4.9' : 'gcc-4.9'
|
30
30
|
puts 'using gcc-4.9 compiler.'
|
31
31
|
else
|
32
|
-
check_for_stdatomics
|
32
|
+
# check_for_stdatomics
|
33
|
+
puts 'using an unknown (old?) compiler... who knows if this will work out... we hope.'
|
33
34
|
end
|
34
35
|
|
35
36
|
$CFLAGS = '-std=c11 -O3 -Wall -DSERVER_DELAY_IO=1'
|
data/ext/iodine/facil.c
ADDED
@@ -0,0 +1,958 @@
|
|
1
|
+
/*
|
2
|
+
Copyright: Boaz Segev, 2016-2017
|
3
|
+
License: MIT
|
4
|
+
|
5
|
+
Feel free to copy, use and enjoy according to the license provided.
|
6
|
+
*/
|
7
|
+
#include "spnlock.inc"
|
8
|
+
|
9
|
+
#include "evio.h"
|
10
|
+
#include "facil.h"
|
11
|
+
|
12
|
+
#include <errno.h>
|
13
|
+
#include <signal.h>
|
14
|
+
#include <stdio.h>
|
15
|
+
#include <stdlib.h>
|
16
|
+
#include <string.h>
|
17
|
+
#include <sys/mman.h>
|
18
|
+
|
19
|
+
/* *****************************************************************************
|
20
|
+
Data Structures
|
21
|
+
***************************************************************************** */
|
22
|
+
typedef struct ProtocolMetadata {
|
23
|
+
spn_lock_i locks[3];
|
24
|
+
unsigned rsv : 8;
|
25
|
+
} protocol_metadata_s;
|
26
|
+
|
27
|
+
#define prt_meta(prt) (*((protocol_metadata_s *)(&(prt)->rsv)))
|
28
|
+
|
29
|
+
struct connection_data_s {
|
30
|
+
protocol_s *protocol;
|
31
|
+
time_t active;
|
32
|
+
uint8_t timeout;
|
33
|
+
spn_lock_i lock;
|
34
|
+
};
|
35
|
+
|
36
|
+
static struct facil_data_s {
|
37
|
+
spn_lock_i global_lock;
|
38
|
+
uint8_t need_review;
|
39
|
+
ssize_t capacity;
|
40
|
+
time_t last_cycle;
|
41
|
+
pid_t parent;
|
42
|
+
struct connection_data_s conn[];
|
43
|
+
} * facil_data;
|
44
|
+
|
45
|
+
#define fd_data(fd) (facil_data->conn[(fd)])
|
46
|
+
#define uuid_data(uuid) fd_data(sock_uuid2fd((uuid)))
|
47
|
+
#define uuid_prt_meta(uuid) prt_meta(uuid_data((uuid)).protocol)
|
48
|
+
|
49
|
+
static inline void clear_connection_data_unsafe(intptr_t uuid,
|
50
|
+
protocol_s *protocol) {
|
51
|
+
uuid_data(uuid) = (struct connection_data_s){.active = facil_data->last_cycle,
|
52
|
+
.protocol = protocol,
|
53
|
+
.lock = uuid_data(uuid).lock};
|
54
|
+
}
|
55
|
+
/** locks a connection's protocol returns a pointer that need to be unlocked. */
|
56
|
+
inline static protocol_s *protocol_try_lock(intptr_t fd,
|
57
|
+
enum facil_protocol_lock_e type) {
|
58
|
+
if (spn_trylock(&fd_data(fd).lock))
|
59
|
+
return NULL;
|
60
|
+
protocol_s *pr = fd_data(fd).protocol;
|
61
|
+
if (!pr) {
|
62
|
+
spn_unlock(&fd_data(fd).lock);
|
63
|
+
return NULL;
|
64
|
+
}
|
65
|
+
if (spn_trylock(&prt_meta(pr).locks[type]))
|
66
|
+
pr = NULL;
|
67
|
+
spn_unlock(&fd_data(fd).lock);
|
68
|
+
return pr;
|
69
|
+
}
|
70
|
+
/** See `facil_protocol_try_lock` for details. */
|
71
|
+
inline static void protocol_unlock(protocol_s *pr,
|
72
|
+
enum facil_protocol_lock_e type) {
|
73
|
+
spn_unlock(&prt_meta(pr).locks[type]);
|
74
|
+
}
|
75
|
+
|
76
|
+
/* *****************************************************************************
|
77
|
+
Deferred event handlers
|
78
|
+
***************************************************************************** */
|
79
|
+
static void deferred_on_close(void *arg, void *arg2) {
|
80
|
+
protocol_s *pr = arg;
|
81
|
+
if (pr->rsv)
|
82
|
+
goto postpone;
|
83
|
+
pr->on_close(pr);
|
84
|
+
return;
|
85
|
+
postpone:
|
86
|
+
defer(deferred_on_close, arg, NULL);
|
87
|
+
(void)arg2;
|
88
|
+
}
|
89
|
+
|
90
|
+
static void deferred_on_ready(void *arg, void *arg2) {
|
91
|
+
if (!uuid_data(arg).protocol)
|
92
|
+
return;
|
93
|
+
protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_WRITE);
|
94
|
+
if (!pr)
|
95
|
+
goto postpone;
|
96
|
+
pr->on_ready((intptr_t)arg, pr);
|
97
|
+
protocol_unlock(pr, FIO_PR_LOCK_WRITE);
|
98
|
+
return;
|
99
|
+
postpone:
|
100
|
+
defer(deferred_on_ready, arg, NULL);
|
101
|
+
(void)arg2;
|
102
|
+
}
|
103
|
+
|
104
|
+
static void deferred_on_shutdown(void *arg, void *arg2) {
|
105
|
+
if (!uuid_data(arg).protocol)
|
106
|
+
return;
|
107
|
+
protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_WRITE);
|
108
|
+
if (!pr)
|
109
|
+
goto postpone;
|
110
|
+
pr->on_shutdown((intptr_t)arg, pr);
|
111
|
+
protocol_unlock(pr, FIO_PR_LOCK_WRITE);
|
112
|
+
sock_close((intptr_t)arg);
|
113
|
+
return;
|
114
|
+
postpone:
|
115
|
+
defer(deferred_on_shutdown, arg, NULL);
|
116
|
+
(void)arg2;
|
117
|
+
}
|
118
|
+
|
119
|
+
static void deferred_on_data(void *arg, void *arg2) {
|
120
|
+
if (!uuid_data(arg).protocol)
|
121
|
+
return;
|
122
|
+
protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_TASK);
|
123
|
+
if (!pr)
|
124
|
+
goto postpone;
|
125
|
+
pr->on_data((intptr_t)arg, pr);
|
126
|
+
protocol_unlock(pr, FIO_PR_LOCK_TASK);
|
127
|
+
return;
|
128
|
+
postpone:
|
129
|
+
defer(deferred_on_data, arg, NULL);
|
130
|
+
(void)arg2;
|
131
|
+
}
|
132
|
+
|
133
|
+
static void deferred_ping(void *arg, void *arg2) {
|
134
|
+
if (!uuid_data(arg).protocol ||
|
135
|
+
(uuid_data(arg).timeout &&
|
136
|
+
(uuid_data(arg).timeout >
|
137
|
+
(facil_data->last_cycle - uuid_data(arg).active)))) {
|
138
|
+
return;
|
139
|
+
}
|
140
|
+
protocol_s *pr = protocol_try_lock(sock_uuid2fd(arg), FIO_PR_LOCK_WRITE);
|
141
|
+
if (!pr)
|
142
|
+
goto postpone;
|
143
|
+
pr->ping((intptr_t)arg, pr);
|
144
|
+
protocol_unlock(pr, FIO_PR_LOCK_WRITE);
|
145
|
+
return;
|
146
|
+
postpone:
|
147
|
+
defer(deferred_ping, arg, NULL);
|
148
|
+
(void)arg2;
|
149
|
+
}
|
150
|
+
|
151
|
+
/* *****************************************************************************
|
152
|
+
Event Handlers (evio)
|
153
|
+
***************************************************************************** */
|
154
|
+
static void sock_flush_defer(void *arg, void *ignored) {
|
155
|
+
(void)ignored;
|
156
|
+
sock_flush((intptr_t)arg);
|
157
|
+
}
|
158
|
+
|
159
|
+
void evio_on_ready(void *arg) {
|
160
|
+
defer(sock_flush_defer, arg, NULL);
|
161
|
+
defer(deferred_on_ready, arg, NULL);
|
162
|
+
}
|
163
|
+
void evio_on_close(void *arg) { sock_force_close((intptr_t)arg); }
|
164
|
+
void evio_on_error(void *arg) { sock_force_close((intptr_t)arg); }
|
165
|
+
void evio_on_data(void *arg) { defer(deferred_on_data, arg, NULL); }
|
166
|
+
|
167
|
+
/* *****************************************************************************
|
168
|
+
Socket callbacks
|
169
|
+
***************************************************************************** */
|
170
|
+
|
171
|
+
void sock_on_close(intptr_t uuid) {
|
172
|
+
spn_lock(&uuid_data(uuid).lock);
|
173
|
+
protocol_s *old_protocol = uuid_data(uuid).protocol;
|
174
|
+
clear_connection_data_unsafe(uuid, NULL);
|
175
|
+
spn_unlock(&uuid_data(uuid).lock);
|
176
|
+
if (old_protocol)
|
177
|
+
defer(deferred_on_close, old_protocol, NULL);
|
178
|
+
}
|
179
|
+
|
180
|
+
void sock_touch(intptr_t uuid) {
|
181
|
+
uuid_data(uuid).active = facil_data->last_cycle;
|
182
|
+
}
|
183
|
+
|
184
|
+
/* *****************************************************************************
|
185
|
+
Initialization and Cleanup
|
186
|
+
***************************************************************************** */
|
187
|
+
static spn_lock_i facil_libinit_lock = SPN_LOCK_INIT;
|
188
|
+
|
189
|
+
static void facil_libcleanup(void) {
|
190
|
+
/* free memory */
|
191
|
+
spn_lock(&facil_libinit_lock);
|
192
|
+
if (facil_data) {
|
193
|
+
munmap(facil_data,
|
194
|
+
sizeof(*facil_data) +
|
195
|
+
(facil_data->capacity * sizeof(struct connection_data_s)));
|
196
|
+
facil_data = NULL;
|
197
|
+
}
|
198
|
+
spn_unlock(&facil_libinit_lock);
|
199
|
+
}
|
200
|
+
|
201
|
+
static void facil_lib_init(void) {
|
202
|
+
ssize_t capa = sock_max_capacity();
|
203
|
+
if (capa < 0)
|
204
|
+
perror("ERROR: socket capacity unknown / failure"), exit(ENOMEM);
|
205
|
+
size_t mem_size =
|
206
|
+
sizeof(*facil_data) + (capa * sizeof(struct connection_data_s));
|
207
|
+
spn_lock(&facil_libinit_lock);
|
208
|
+
if (facil_data)
|
209
|
+
goto finish;
|
210
|
+
facil_data = mmap(NULL, mem_size, PROT_READ | PROT_WRITE | PROT_EXEC,
|
211
|
+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
212
|
+
if (!facil_data)
|
213
|
+
perror("ERROR: Couldn't initialize the facil.io library"), exit(0);
|
214
|
+
memset(facil_data, 0, mem_size);
|
215
|
+
*facil_data = (struct facil_data_s){.capacity = capa, .parent = getpid()};
|
216
|
+
atexit(facil_libcleanup);
|
217
|
+
#ifdef DEBUG
|
218
|
+
if (FACIL_PRINT_STATE)
|
219
|
+
fprintf(stderr,
|
220
|
+
"Initialized the facil.io library.\n"
|
221
|
+
"facil.io's memory footprint per connection == %lu Bytes X %lu\n"
|
222
|
+
"=== facil.io's memory footprint: %lu ===\n\n",
|
223
|
+
sizeof(struct connection_data_s), facil_data->capacity, mem_size);
|
224
|
+
#endif
|
225
|
+
finish:
|
226
|
+
spn_unlock(&facil_libinit_lock);
|
227
|
+
time(&facil_data->last_cycle);
|
228
|
+
}
|
229
|
+
|
230
|
+
/* *****************************************************************************
|
231
|
+
Mock Protocol and service Callbacks
|
232
|
+
***************************************************************************** */
|
233
|
+
static void mock_on_ev(intptr_t uuid, protocol_s *protocol) {
|
234
|
+
(void)uuid;
|
235
|
+
(void)protocol;
|
236
|
+
}
|
237
|
+
|
238
|
+
static void mock_on_close(protocol_s *protocol) { (void)(protocol); }
|
239
|
+
|
240
|
+
static void mock_ping(intptr_t uuid, protocol_s *protocol) {
|
241
|
+
(void)(protocol);
|
242
|
+
sock_force_close(uuid);
|
243
|
+
}
|
244
|
+
static void mock_idle(void) {}
|
245
|
+
|
246
|
+
/* *****************************************************************************
|
247
|
+
The listenning protocol
|
248
|
+
***************************************************************************** */
|
249
|
+
#undef facil_listen
|
250
|
+
|
251
|
+
static const char *listener_protocol_name =
|
252
|
+
"listening protocol __facil_internal__";
|
253
|
+
|
254
|
+
struct ListenerProtocol {
|
255
|
+
protocol_s protocol;
|
256
|
+
protocol_s *(*on_open)(intptr_t uuid, void *udata);
|
257
|
+
void *udata;
|
258
|
+
void (*on_start)(void *udata);
|
259
|
+
void (*on_finish)(void *udata);
|
260
|
+
const char *port;
|
261
|
+
};
|
262
|
+
|
263
|
+
static void listener_ping(intptr_t uuid, protocol_s *plistener) {
|
264
|
+
// fprintf(stderr, "*** Listener Ping Called for %ld\n", sock_uuid2fd(uuid));
|
265
|
+
uuid_data(uuid).active = facil_data->last_cycle;
|
266
|
+
return;
|
267
|
+
(void)plistener;
|
268
|
+
}
|
269
|
+
|
270
|
+
static void listener_deferred_on_open(void *uuid_, void *srv_uuid_) {
|
271
|
+
intptr_t uuid = (intptr_t)uuid_;
|
272
|
+
intptr_t srv_uuid = (intptr_t)srv_uuid_;
|
273
|
+
struct ListenerProtocol *listener =
|
274
|
+
(struct ListenerProtocol *)protocol_try_lock(sock_uuid2fd(srv_uuid),
|
275
|
+
FIO_PR_LOCK_WRITE);
|
276
|
+
if (!listener) {
|
277
|
+
if (errno != EBADF)
|
278
|
+
defer(listener_deferred_on_open, uuid_, srv_uuid_);
|
279
|
+
return;
|
280
|
+
}
|
281
|
+
protocol_s *pr = listener->on_open(uuid, listener->udata);
|
282
|
+
facil_attach(uuid, pr);
|
283
|
+
if (!pr)
|
284
|
+
sock_close(uuid);
|
285
|
+
protocol_unlock((protocol_s *)listener, FIO_PR_LOCK_WRITE);
|
286
|
+
}
|
287
|
+
|
288
|
+
static void listener_on_data(intptr_t uuid, protocol_s *plistener) {
|
289
|
+
intptr_t new_client;
|
290
|
+
if ((new_client = sock_accept(uuid)) == -1) {
|
291
|
+
if (errno == ECONNABORTED || errno == ECONNRESET)
|
292
|
+
defer(deferred_on_data, (void *)uuid, NULL);
|
293
|
+
else if (errno != EWOULDBLOCK)
|
294
|
+
perror("ERROR: socket accept error");
|
295
|
+
return;
|
296
|
+
}
|
297
|
+
defer(listener_deferred_on_open, (void *)new_client, (void *)uuid);
|
298
|
+
defer(deferred_on_data, (void *)uuid, NULL);
|
299
|
+
// // Was, without `deferred_on_data`
|
300
|
+
// struct ListenerProtocol *listener = (void *)plistener;
|
301
|
+
// protocol_s *pr = listener->on_open(new_client, listener->udata);
|
302
|
+
// facil_attach(new_client, pr);
|
303
|
+
// if (!pr)
|
304
|
+
// sock_close(new_client);
|
305
|
+
return;
|
306
|
+
(void)plistener;
|
307
|
+
}
|
308
|
+
|
309
|
+
static void free_listenner(void *li) { free(li); }
|
310
|
+
|
311
|
+
static void listener_on_close(protocol_s *plistener) {
|
312
|
+
struct ListenerProtocol *listener = (void *)plistener;
|
313
|
+
listener->on_finish(listener->udata);
|
314
|
+
if (FACIL_PRINT_STATE)
|
315
|
+
fprintf(stderr, "* (%d) Stopped listening on port %s\n", getpid(),
|
316
|
+
listener->port);
|
317
|
+
free_listenner(listener);
|
318
|
+
}
|
319
|
+
|
320
|
+
static inline struct ListenerProtocol *
|
321
|
+
listener_alloc(struct facil_listen_args settings) {
|
322
|
+
if (!settings.on_start)
|
323
|
+
settings.on_start = (void (*)(void *))mock_on_close;
|
324
|
+
if (!settings.on_finish)
|
325
|
+
settings.on_finish = (void (*)(void *))mock_on_close;
|
326
|
+
struct ListenerProtocol *listener = malloc(sizeof(*listener));
|
327
|
+
if (listener) {
|
328
|
+
*listener = (struct ListenerProtocol){
|
329
|
+
.protocol.service = listener_protocol_name,
|
330
|
+
.protocol.on_data = listener_on_data,
|
331
|
+
.protocol.on_close = listener_on_close,
|
332
|
+
.protocol.ping = listener_ping,
|
333
|
+
.on_open = settings.on_open,
|
334
|
+
.udata = settings.udata,
|
335
|
+
.on_start = settings.on_start,
|
336
|
+
.on_finish = settings.on_finish,
|
337
|
+
.port = settings.port,
|
338
|
+
};
|
339
|
+
return listener;
|
340
|
+
}
|
341
|
+
return NULL;
|
342
|
+
}
|
343
|
+
|
344
|
+
inline static void listener_on_start(size_t fd) {
|
345
|
+
intptr_t uuid = sock_fd2uuid(fd);
|
346
|
+
if (uuid < 0)
|
347
|
+
fprintf(stderr, "ERROR: listening socket dropped?\n"), exit(4);
|
348
|
+
if (evio_add(fd, (void *)uuid) < 0)
|
349
|
+
perror("Couldn't register listening socket"), exit(4);
|
350
|
+
fd_data(fd).active = facil_data->last_cycle;
|
351
|
+
// call the on_init callback
|
352
|
+
struct ListenerProtocol *listener =
|
353
|
+
(struct ListenerProtocol *)uuid_data(uuid).protocol;
|
354
|
+
listener->on_start(listener->udata);
|
355
|
+
}
|
356
|
+
|
357
|
+
/**
|
358
|
+
Listens to a server with the following server settings (which MUST include
|
359
|
+
a default protocol).
|
360
|
+
|
361
|
+
This method blocks the current thread until the server is stopped (either
|
362
|
+
though a `srv_stop` function or when a SIGINT/SIGTERM is received).
|
363
|
+
*/
|
364
|
+
int facil_listen(struct facil_listen_args settings) {
|
365
|
+
if (!facil_data)
|
366
|
+
facil_lib_init();
|
367
|
+
if (settings.on_open == NULL || settings.port == NULL)
|
368
|
+
return -1;
|
369
|
+
intptr_t uuid = sock_listen(settings.address, settings.port);
|
370
|
+
if (uuid == -1) {
|
371
|
+
return -1;
|
372
|
+
}
|
373
|
+
protocol_s *protocol = (void *)listener_alloc(settings);
|
374
|
+
facil_attach(uuid, protocol);
|
375
|
+
if (!protocol) {
|
376
|
+
sock_close(uuid);
|
377
|
+
return -1;
|
378
|
+
}
|
379
|
+
if (FACIL_PRINT_STATE)
|
380
|
+
fprintf(stderr, "* Listening on port %s\n", settings.port);
|
381
|
+
return 0;
|
382
|
+
}
|
383
|
+
|
384
|
+
/* *****************************************************************************
|
385
|
+
Connect (as client)
|
386
|
+
***************************************************************************** */
|
387
|
+
|
388
|
+
static const char *connector_protocol_name = "connect protocol __internal__";
|
389
|
+
|
390
|
+
struct ConnectProtocol {
|
391
|
+
protocol_s protocol;
|
392
|
+
protocol_s *(*on_connect)(intptr_t uuid, void *udata);
|
393
|
+
void (*on_fail)(void *udata);
|
394
|
+
void *udata;
|
395
|
+
int opened;
|
396
|
+
};
|
397
|
+
|
398
|
+
static void connector_on_ready(intptr_t uuid, protocol_s *_connector) {
|
399
|
+
struct ConnectProtocol *connector = (void *)_connector;
|
400
|
+
connector->opened = 1;
|
401
|
+
// fprintf(stderr, "connector_on_ready called\n");
|
402
|
+
if (connector->on_connect) {
|
403
|
+
sock_touch(uuid);
|
404
|
+
if (facil_attach(uuid, connector->on_connect(uuid, connector->udata)) == -1)
|
405
|
+
goto error;
|
406
|
+
uuid_data(uuid).protocol->on_ready(uuid, uuid_data(uuid).protocol);
|
407
|
+
return;
|
408
|
+
}
|
409
|
+
error:
|
410
|
+
sock_close(uuid);
|
411
|
+
}
|
412
|
+
|
413
|
+
static void connector_on_data(intptr_t uuid, protocol_s *connector) {
|
414
|
+
(void)connector;
|
415
|
+
defer(deferred_on_data, (void *)uuid, NULL);
|
416
|
+
}
|
417
|
+
|
418
|
+
static void connector_on_close(protocol_s *pconnector) {
|
419
|
+
struct ConnectProtocol *connector = (void *)pconnector;
|
420
|
+
if (connector->opened == 0 && connector->on_fail)
|
421
|
+
connector->on_fail(connector->udata);
|
422
|
+
free(connector);
|
423
|
+
}
|
424
|
+
|
425
|
+
#undef facil_connect
|
426
|
+
intptr_t facil_connect(struct facil_connect_args opt) {
|
427
|
+
if (!opt.address || !opt.port || !opt.on_connect)
|
428
|
+
return -1;
|
429
|
+
if (!facil_data->last_cycle)
|
430
|
+
time(&facil_data->last_cycle);
|
431
|
+
struct ConnectProtocol *connector = malloc(sizeof(*connector));
|
432
|
+
*connector = (struct ConnectProtocol){
|
433
|
+
.on_connect = opt.on_connect,
|
434
|
+
.on_fail = opt.on_fail,
|
435
|
+
.udata = opt.udata,
|
436
|
+
.protocol.service = connector_protocol_name,
|
437
|
+
.protocol.on_data = connector_on_data,
|
438
|
+
.protocol.on_ready = connector_on_ready,
|
439
|
+
.protocol.on_close = connector_on_close,
|
440
|
+
.opened = 0,
|
441
|
+
};
|
442
|
+
if (!connector)
|
443
|
+
return -1;
|
444
|
+
intptr_t uuid = sock_connect(opt.address, opt.port);
|
445
|
+
if (uuid == -1)
|
446
|
+
return -1;
|
447
|
+
if (facil_attach(uuid, &connector->protocol) == -1) {
|
448
|
+
sock_close(uuid);
|
449
|
+
return -1;
|
450
|
+
}
|
451
|
+
return uuid;
|
452
|
+
}
|
453
|
+
|
454
|
+
/* *****************************************************************************
|
455
|
+
Timers
|
456
|
+
***************************************************************************** */
|
457
|
+
|
458
|
+
/* *******
|
459
|
+
Timer Protocol
|
460
|
+
******* */
|
461
|
+
typedef struct {
|
462
|
+
protocol_s protocol;
|
463
|
+
size_t milliseconds;
|
464
|
+
size_t repetitions;
|
465
|
+
void (*task)(void *);
|
466
|
+
void (*on_finish)(void *);
|
467
|
+
void *arg;
|
468
|
+
} timer_protocol_s;
|
469
|
+
|
470
|
+
#define prot2timer(protocol) (*((timer_protocol_s *)(protocol)))
|
471
|
+
|
472
|
+
const char *timer_protocol_name = "timer protocol __facil_internal__";
|
473
|
+
|
474
|
+
static void timer_on_data(intptr_t uuid, protocol_s *protocol) {
|
475
|
+
prot2timer(protocol).task(prot2timer(protocol).arg);
|
476
|
+
evio_reset_timer(sock_uuid2fd(uuid));
|
477
|
+
if (prot2timer(protocol).repetitions == 0)
|
478
|
+
return;
|
479
|
+
prot2timer(protocol).repetitions -= 1;
|
480
|
+
if (prot2timer(protocol).repetitions)
|
481
|
+
return;
|
482
|
+
evio_remove(sock_uuid2fd(uuid));
|
483
|
+
sock_force_close(uuid);
|
484
|
+
}
|
485
|
+
|
486
|
+
static void timer_on_close(protocol_s *protocol) {
|
487
|
+
prot2timer(protocol).on_finish(prot2timer(protocol).arg);
|
488
|
+
free(protocol);
|
489
|
+
}
|
490
|
+
|
491
|
+
static inline timer_protocol_s *timer_alloc(void (*task)(void *), void *arg,
|
492
|
+
size_t milliseconds,
|
493
|
+
size_t repetitions,
|
494
|
+
void (*on_finish)(void *)) {
|
495
|
+
if (!on_finish)
|
496
|
+
on_finish = (void (*)(void *))mock_on_close;
|
497
|
+
timer_protocol_s *t = malloc(sizeof(*t));
|
498
|
+
if (t)
|
499
|
+
*t = (timer_protocol_s){
|
500
|
+
.protocol.service = timer_protocol_name,
|
501
|
+
.protocol.on_data = timer_on_data,
|
502
|
+
.protocol.on_close = timer_on_close,
|
503
|
+
.arg = arg,
|
504
|
+
.task = task,
|
505
|
+
.on_finish = on_finish,
|
506
|
+
.milliseconds = milliseconds,
|
507
|
+
.repetitions = repetitions,
|
508
|
+
};
|
509
|
+
return t;
|
510
|
+
}
|
511
|
+
|
512
|
+
inline static void timer_on_server_start(int fd) {
|
513
|
+
if (evio_add_timer(fd, (void *)sock_fd2uuid(fd),
|
514
|
+
prot2timer(fd_data(fd).protocol).milliseconds))
|
515
|
+
perror("Couldn't register a required timed event."), exit(4);
|
516
|
+
}
|
517
|
+
|
518
|
+
/**
|
519
|
+
* Creates a system timer (at the cost of 1 file descriptor).
|
520
|
+
*
|
521
|
+
* The task will repeat `repetitions` times. If `repetitions` is set to 0, task
|
522
|
+
* will repeat forever.
|
523
|
+
*
|
524
|
+
* Returns -1 on error or the new file descriptor on succeess.
|
525
|
+
*/
|
526
|
+
int facil_run_every(size_t milliseconds, size_t repetitions,
|
527
|
+
void (*task)(void *), void *arg,
|
528
|
+
void (*on_finish)(void *)) {
|
529
|
+
if (task == NULL)
|
530
|
+
return -1;
|
531
|
+
timer_protocol_s *protocol = NULL;
|
532
|
+
intptr_t uuid = -1;
|
533
|
+
int fd = evio_open_timer();
|
534
|
+
if (fd == -1) {
|
535
|
+
perror("ERROR: couldn't create a timer fd");
|
536
|
+
goto error;
|
537
|
+
}
|
538
|
+
uuid = sock_open(fd);
|
539
|
+
if (uuid == -1)
|
540
|
+
goto error;
|
541
|
+
protocol = timer_alloc(task, arg, milliseconds, repetitions, on_finish);
|
542
|
+
if (protocol == NULL)
|
543
|
+
goto error;
|
544
|
+
facil_attach(uuid, (protocol_s *)protocol);
|
545
|
+
if (evio_isactive() && evio_add_timer(fd, (void *)uuid, milliseconds) < 0)
|
546
|
+
goto error;
|
547
|
+
return 0;
|
548
|
+
error:
|
549
|
+
if (uuid != -1)
|
550
|
+
sock_close(uuid);
|
551
|
+
else if (fd != -1)
|
552
|
+
close(fd);
|
553
|
+
return -1;
|
554
|
+
}
|
555
|
+
|
556
|
+
/* *****************************************************************************
|
557
|
+
Running the server
|
558
|
+
***************************************************************************** */
|
559
|
+
|
560
|
+
static void print_pid(void *arg, void *ignr) {
|
561
|
+
(void)arg;
|
562
|
+
(void)ignr;
|
563
|
+
fprintf(stderr, "* %d is running.\n", getpid());
|
564
|
+
}
|
565
|
+
|
566
|
+
static void facil_review_timeout(void *arg, void *ignr) {
|
567
|
+
(void)ignr;
|
568
|
+
protocol_s *tmp;
|
569
|
+
time_t review = facil_data->last_cycle;
|
570
|
+
intptr_t fd = (intptr_t)arg;
|
571
|
+
|
572
|
+
uint16_t timeout = fd_data(fd).timeout;
|
573
|
+
if (!timeout)
|
574
|
+
timeout = 300; /* enforced timout settings */
|
575
|
+
|
576
|
+
if (!fd_data(fd).protocol || (fd_data(fd).active + timeout >= review))
|
577
|
+
goto finish;
|
578
|
+
tmp = protocol_try_lock(fd, FIO_PR_LOCK_STATE);
|
579
|
+
if (!tmp)
|
580
|
+
goto reschedule;
|
581
|
+
if (prt_meta(tmp).locks[FIO_PR_LOCK_TASK] ||
|
582
|
+
prt_meta(tmp).locks[FIO_PR_LOCK_WRITE])
|
583
|
+
goto unlock;
|
584
|
+
defer(deferred_ping, (void *)sock_fd2uuid(fd), NULL);
|
585
|
+
unlock:
|
586
|
+
protocol_unlock(tmp, FIO_PR_LOCK_STATE);
|
587
|
+
finish:
|
588
|
+
do {
|
589
|
+
fd++;
|
590
|
+
} while (!fd_data(fd).protocol && (fd < facil_data->capacity));
|
591
|
+
|
592
|
+
if (facil_data->capacity <= fd) {
|
593
|
+
facil_data->need_review = 1;
|
594
|
+
return;
|
595
|
+
}
|
596
|
+
reschedule:
|
597
|
+
defer(facil_review_timeout, (void *)fd, NULL);
|
598
|
+
}
|
599
|
+
|
600
|
+
static void facil_cycle(void *arg, void *ignr) {
|
601
|
+
(void)ignr;
|
602
|
+
static int idle = 0;
|
603
|
+
time(&facil_data->last_cycle);
|
604
|
+
int events = evio_review(defer_has_queue() ? 0 : 512);
|
605
|
+
if (events < 0)
|
606
|
+
goto error;
|
607
|
+
if (events > 0) {
|
608
|
+
idle = 1;
|
609
|
+
goto finish;
|
610
|
+
}
|
611
|
+
if (idle) {
|
612
|
+
((struct facil_run_args *)arg)->on_idle();
|
613
|
+
idle = 0;
|
614
|
+
}
|
615
|
+
finish:
|
616
|
+
if (!defer_fork_is_active())
|
617
|
+
return;
|
618
|
+
if (facil_data->need_review) {
|
619
|
+
facil_data->need_review = 0;
|
620
|
+
defer(facil_review_timeout, (void *)0, NULL);
|
621
|
+
}
|
622
|
+
defer(facil_cycle, arg, NULL);
|
623
|
+
error:
|
624
|
+
(void)1;
|
625
|
+
}
|
626
|
+
|
627
|
+
static void facil_init_run(void *arg, void *arg2) {
|
628
|
+
(void)arg;
|
629
|
+
(void)arg2;
|
630
|
+
evio_create();
|
631
|
+
time(&facil_data->last_cycle);
|
632
|
+
for (intptr_t i = 0; i < facil_data->capacity; i++) {
|
633
|
+
if (fd_data(i).protocol) {
|
634
|
+
if (fd_data(i).protocol->service == listener_protocol_name)
|
635
|
+
listener_on_start(i);
|
636
|
+
else if (fd_data(i).protocol->service == timer_protocol_name)
|
637
|
+
timer_on_server_start(i);
|
638
|
+
else
|
639
|
+
evio_add(i, (void *)sock_fd2uuid(i));
|
640
|
+
}
|
641
|
+
}
|
642
|
+
facil_data->need_review = 1;
|
643
|
+
defer(facil_cycle, arg, NULL);
|
644
|
+
}
|
645
|
+
|
646
|
+
static void facil_cleanup(void *arg) {
|
647
|
+
fprintf(stderr, "* %d cleanning up.\n", getpid());
|
648
|
+
intptr_t uuid;
|
649
|
+
for (intptr_t i = 0; i < facil_data->capacity; i++) {
|
650
|
+
if (fd_data(i).protocol && (uuid = sock_fd2uuid(i)) >= 0) {
|
651
|
+
defer(deferred_on_shutdown, (void *)uuid, NULL);
|
652
|
+
}
|
653
|
+
}
|
654
|
+
facil_cycle(arg, NULL);
|
655
|
+
defer_perform();
|
656
|
+
facil_cycle(arg, NULL);
|
657
|
+
((struct facil_run_args *)arg)->on_finish();
|
658
|
+
defer_perform();
|
659
|
+
}
|
660
|
+
|
661
|
+
#undef facil_run
|
662
|
+
void facil_run(struct facil_run_args args) {
|
663
|
+
signal(SIGPIPE, SIG_IGN);
|
664
|
+
if (!facil_data)
|
665
|
+
facil_lib_init();
|
666
|
+
if (!args.on_idle)
|
667
|
+
args.on_idle = mock_idle;
|
668
|
+
if (!args.on_finish)
|
669
|
+
args.on_finish = mock_idle;
|
670
|
+
#ifdef _SC_NPROCESSORS_ONLN
|
671
|
+
if (!args.threads && !args.processes) {
|
672
|
+
ssize_t cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
|
673
|
+
if (cpu_count > 0)
|
674
|
+
args.threads = args.processes = cpu_count;
|
675
|
+
}
|
676
|
+
#endif
|
677
|
+
if (!args.processes)
|
678
|
+
args.processes = 1;
|
679
|
+
if (!args.threads)
|
680
|
+
args.threads = 1;
|
681
|
+
if (FACIL_PRINT_STATE) {
|
682
|
+
fprintf(stderr, "Server is running %u %s X %u %s, press ^C to stop\n",
|
683
|
+
args.processes, args.processes > 1 ? "workers" : "worker",
|
684
|
+
args.threads, args.threads > 1 ? "threads" : "thread");
|
685
|
+
defer(print_pid, NULL, NULL);
|
686
|
+
}
|
687
|
+
defer(facil_init_run, &args, NULL);
|
688
|
+
int frk = defer_perform_in_fork(args.processes, args.threads);
|
689
|
+
facil_cleanup(&args);
|
690
|
+
if (frk < 0) {
|
691
|
+
perror("ERROR: couldn't spawn workers");
|
692
|
+
} else if (frk > 0) {
|
693
|
+
exit(0);
|
694
|
+
}
|
695
|
+
if (FACIL_PRINT_STATE)
|
696
|
+
fprintf(stderr, "\n --- Completed Shutdown ---\n");
|
697
|
+
}
|
698
|
+
/* *****************************************************************************
|
699
|
+
Setting the protocol
|
700
|
+
***************************************************************************** */
|
701
|
+
|
702
|
+
/** Attaches (or updates) a protocol object to a socket UUID.
|
703
|
+
* Returns -1 on error and 0 on success.
|
704
|
+
*/
|
705
|
+
int facil_attach(intptr_t uuid, protocol_s *protocol) {
|
706
|
+
if (!facil_data)
|
707
|
+
facil_lib_init();
|
708
|
+
if (protocol) {
|
709
|
+
if (!protocol->on_close)
|
710
|
+
protocol->on_close = mock_on_close;
|
711
|
+
if (!protocol->on_data)
|
712
|
+
protocol->on_data = mock_on_ev;
|
713
|
+
if (!protocol->on_ready)
|
714
|
+
protocol->on_ready = mock_on_ev;
|
715
|
+
if (!protocol->ping)
|
716
|
+
protocol->ping = mock_ping;
|
717
|
+
if (!protocol->on_shutdown)
|
718
|
+
protocol->on_shutdown = mock_on_ev;
|
719
|
+
protocol->rsv = 0;
|
720
|
+
}
|
721
|
+
if (!sock_isvalid(uuid))
|
722
|
+
return -1;
|
723
|
+
spn_lock(&uuid_data(uuid).lock);
|
724
|
+
protocol_s *old_protocol = uuid_data(uuid).protocol;
|
725
|
+
uuid_data(uuid).protocol = protocol;
|
726
|
+
uuid_data(uuid).active = facil_data->last_cycle;
|
727
|
+
spn_unlock(&uuid_data(uuid).lock);
|
728
|
+
if (old_protocol)
|
729
|
+
defer(deferred_on_close, old_protocol, NULL);
|
730
|
+
if (evio_isactive())
|
731
|
+
evio_add(sock_uuid2fd(uuid), (void *)uuid);
|
732
|
+
return 0;
|
733
|
+
}
|
734
|
+
|
735
|
+
/** Sets a timeout for a specific connection (if active). */
|
736
|
+
void facil_set_timeout(intptr_t uuid, uint8_t timeout) {
|
737
|
+
if (sock_isvalid(uuid)) {
|
738
|
+
uuid_data(uuid).active = facil_data->last_cycle;
|
739
|
+
uuid_data(uuid).timeout = timeout;
|
740
|
+
}
|
741
|
+
}
|
742
|
+
/** Gets a timeout for a specific connection. Returns 0 if there's no set
|
743
|
+
* timeout or the connection is inactive. */
|
744
|
+
uint8_t facil_get_timeout(intptr_t uuid) { return uuid_data(uuid).timeout; }
|
745
|
+
|
746
|
+
/* *****************************************************************************
|
747
|
+
Misc helpers
|
748
|
+
***************************************************************************** */
|
749
|
+
|
750
|
+
/**
|
751
|
+
Returns the last time the server reviewed any pending IO events.
|
752
|
+
*/
|
753
|
+
time_t facil_last_tick(void) { return facil_data->last_cycle; }
|
754
|
+
|
755
|
+
/**
|
756
|
+
* This function allows out-of-task access to a connection's `protocol_s` object
|
757
|
+
* by attempting to lock it.
|
758
|
+
*/
|
759
|
+
protocol_s *facil_protocol_try_lock(intptr_t uuid,
|
760
|
+
enum facil_protocol_lock_e type) {
|
761
|
+
if (sock_isvalid(uuid) || !uuid_data(uuid).protocol) {
|
762
|
+
errno = EBADF;
|
763
|
+
return NULL;
|
764
|
+
}
|
765
|
+
return protocol_try_lock(sock_uuid2fd(uuid), type);
|
766
|
+
}
|
767
|
+
/** See `facil_protocol_try_lock` for details. */
|
768
|
+
void facil_protocol_unlock(protocol_s *pr, enum facil_protocol_lock_e type) {
|
769
|
+
if (!pr)
|
770
|
+
return;
|
771
|
+
protocol_unlock(pr, type);
|
772
|
+
}
|
773
|
+
/** Counts all the connections of a specific type. */
|
774
|
+
size_t facil_count(void *service) {
|
775
|
+
long count = 0;
|
776
|
+
void *tmp;
|
777
|
+
for (intptr_t i = 0; i < facil_data->capacity; i++) {
|
778
|
+
tmp = NULL;
|
779
|
+
spn_lock(&fd_data(i).lock);
|
780
|
+
if (fd_data(i).protocol && fd_data(i).protocol->service)
|
781
|
+
tmp = (void *)fd_data(i).protocol->service;
|
782
|
+
spn_unlock(&fd_data(i).lock);
|
783
|
+
if (tmp != listener_protocol_name && tmp != timer_protocol_name &&
|
784
|
+
(!service || (tmp == service)))
|
785
|
+
count++;
|
786
|
+
}
|
787
|
+
return count;
|
788
|
+
}
|
789
|
+
|
790
|
+
/* *****************************************************************************
|
791
|
+
Task Management - `facil_defer`, `facil_each`
|
792
|
+
***************************************************************************** */
|
793
|
+
|
794
|
+
struct task {
|
795
|
+
intptr_t origin;
|
796
|
+
void (*func)(intptr_t uuid, protocol_s *, void *arg);
|
797
|
+
void *arg;
|
798
|
+
void (*on_done)(intptr_t uuid, void *arg);
|
799
|
+
const void *service;
|
800
|
+
uint32_t count;
|
801
|
+
enum facil_protocol_lock_e task_type;
|
802
|
+
spn_lock_i lock;
|
803
|
+
};
|
804
|
+
|
805
|
+
static inline struct task *alloc_facil_task(void) {
|
806
|
+
return malloc(sizeof(struct task));
|
807
|
+
}
|
808
|
+
|
809
|
+
static inline void free_facil_task(struct task *task) { free(task); }
|
810
|
+
|
811
|
+
static void mock_on_task_done(intptr_t uuid, void *arg) {
|
812
|
+
(void)uuid;
|
813
|
+
(void)arg;
|
814
|
+
}
|
815
|
+
|
816
|
+
static void perform_single_task(void *v_uuid, void *v_task) {
|
817
|
+
struct task *task = v_task;
|
818
|
+
if (!uuid_data(v_uuid).protocol)
|
819
|
+
goto fallback;
|
820
|
+
protocol_s *pr = protocol_try_lock(sock_uuid2fd(v_uuid), task->task_type);
|
821
|
+
if (!pr)
|
822
|
+
goto defer;
|
823
|
+
task->func((intptr_t)v_uuid, pr, task->arg);
|
824
|
+
protocol_unlock(pr, task->task_type);
|
825
|
+
free_facil_task(task);
|
826
|
+
return;
|
827
|
+
fallback:
|
828
|
+
task->on_done((intptr_t)v_uuid, task->arg);
|
829
|
+
return;
|
830
|
+
defer:
|
831
|
+
defer(perform_single_task, v_uuid, v_task);
|
832
|
+
return;
|
833
|
+
}
|
834
|
+
|
835
|
+
static void finish_multi_task(void *v_fd, void *v_task) {
|
836
|
+
struct task *task = v_task;
|
837
|
+
if (spn_trylock(&task->lock))
|
838
|
+
goto reschedule;
|
839
|
+
task->count--;
|
840
|
+
if (task->count) {
|
841
|
+
spn_unlock(&task->lock);
|
842
|
+
return;
|
843
|
+
}
|
844
|
+
task->on_done(task->origin, task->arg);
|
845
|
+
free_facil_task(task);
|
846
|
+
return;
|
847
|
+
reschedule:
|
848
|
+
defer(finish_multi_task, v_fd, v_task);
|
849
|
+
}
|
850
|
+
|
851
|
+
static void perform_multi_task(void *v_fd, void *v_task) {
|
852
|
+
if (!fd_data((intptr_t)v_fd).protocol) {
|
853
|
+
finish_multi_task(v_fd, v_task);
|
854
|
+
return;
|
855
|
+
}
|
856
|
+
struct task *task = v_task;
|
857
|
+
protocol_s *pr = protocol_try_lock((intptr_t)v_fd, task->task_type);
|
858
|
+
if (!pr)
|
859
|
+
goto reschedule;
|
860
|
+
if (pr->service == task->service)
|
861
|
+
task->func(sock_fd2uuid((intptr_t)v_fd), pr, task->arg);
|
862
|
+
protocol_unlock(pr, task->task_type);
|
863
|
+
defer(finish_multi_task, v_fd, v_task);
|
864
|
+
return;
|
865
|
+
reschedule:
|
866
|
+
// fprintf(stderr, "rescheduling multi for %p\n", v_fd);
|
867
|
+
defer(perform_multi_task, v_fd, v_task);
|
868
|
+
}
|
869
|
+
|
870
|
+
static void schedule_multi_task(void *v_fd, void *v_task) {
|
871
|
+
struct task *task = v_task;
|
872
|
+
intptr_t fd = (intptr_t)v_fd;
|
873
|
+
for (size_t i = 0; i < 64; i++) {
|
874
|
+
if (!fd_data(fd).protocol)
|
875
|
+
goto finish;
|
876
|
+
if (spn_trylock(&fd_data(fd).lock))
|
877
|
+
goto reschedule;
|
878
|
+
if (!fd_data(fd).protocol ||
|
879
|
+
fd_data(fd).protocol->service != task->service || fd == task->origin) {
|
880
|
+
spn_unlock(&fd_data(fd).lock);
|
881
|
+
goto finish;
|
882
|
+
}
|
883
|
+
spn_unlock(&fd_data(fd).lock);
|
884
|
+
spn_lock(&task->lock);
|
885
|
+
task->count++;
|
886
|
+
spn_unlock(&task->lock);
|
887
|
+
defer(perform_multi_task, (void *)fd, task);
|
888
|
+
finish:
|
889
|
+
do {
|
890
|
+
fd++;
|
891
|
+
} while (!fd_data(fd).protocol && (fd < facil_data->capacity));
|
892
|
+
if (fd >= (intptr_t)facil_data->capacity)
|
893
|
+
goto complete;
|
894
|
+
}
|
895
|
+
reschedule:
|
896
|
+
schedule_multi_task((void *)fd, v_task);
|
897
|
+
return;
|
898
|
+
complete:
|
899
|
+
defer(finish_multi_task, NULL, v_task);
|
900
|
+
}
|
901
|
+
/**
|
902
|
+
* Schedules a protected connection task. The task will run within the
|
903
|
+
* connection's lock.
|
904
|
+
*
|
905
|
+
* If the connection is closed before the task can run, the
|
906
|
+
* `fallback` task wil be called instead, allowing for resource cleanup.
|
907
|
+
*/
|
908
|
+
#undef facil_defer
|
909
|
+
void facil_defer(struct facil_defer_args_s args) {
|
910
|
+
if (!args.fallback)
|
911
|
+
args.fallback = mock_on_task_done;
|
912
|
+
if (!args.task_type)
|
913
|
+
args.task_type = FIO_PR_LOCK_TASK;
|
914
|
+
if (!args.task || !uuid_data(args.uuid).protocol || args.uuid < 0 ||
|
915
|
+
!sock_isvalid(args.uuid))
|
916
|
+
goto error;
|
917
|
+
struct task *task = alloc_facil_task();
|
918
|
+
if (!task)
|
919
|
+
goto error;
|
920
|
+
*task = (struct task){
|
921
|
+
.func = args.task, .arg = args.arg, .on_done = args.fallback};
|
922
|
+
defer(perform_single_task, (void *)args.uuid, task);
|
923
|
+
return;
|
924
|
+
error:
|
925
|
+
defer((void (*)(void *, void *))args.fallback, (void *)args.uuid, args.arg);
|
926
|
+
}
|
927
|
+
|
928
|
+
/**
|
929
|
+
* Schedules a protected connection task for each `service` connection.
|
930
|
+
* The tasks will run within each of the connection's locks.
|
931
|
+
*
|
932
|
+
* Once all the tasks were performed, the `on_complete` callback will be called.
|
933
|
+
*/
|
934
|
+
#undef facil_each
|
935
|
+
int facil_each(struct facil_each_args_s args) {
|
936
|
+
if (!args.on_complete)
|
937
|
+
args.on_complete = mock_on_task_done;
|
938
|
+
if (!args.task_type)
|
939
|
+
args.task_type = FIO_PR_LOCK_TASK;
|
940
|
+
if (!args.task)
|
941
|
+
goto error;
|
942
|
+
struct task *task = alloc_facil_task();
|
943
|
+
if (!task)
|
944
|
+
goto error;
|
945
|
+
*task = (struct task){.origin = args.origin,
|
946
|
+
.func = args.task,
|
947
|
+
.arg = args.arg,
|
948
|
+
.on_done = args.on_complete,
|
949
|
+
.service = args.service,
|
950
|
+
.task_type = args.task_type,
|
951
|
+
.count = 1};
|
952
|
+
defer(schedule_multi_task, (void *)0, task);
|
953
|
+
return 0;
|
954
|
+
error:
|
955
|
+
defer((void (*)(void *, void *))args.on_complete, (void *)args.origin,
|
956
|
+
args.arg);
|
957
|
+
return -1;
|
958
|
+
}
|