iodine 0.3.5 → 0.3.6
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/ext/iodine/defer.c +18 -5
- data/ext/iodine/defer.h +10 -0
- data/ext/iodine/facil.c +277 -8
- data/ext/iodine/facil.h +33 -0
- data/ext/iodine/fio_list.h +84 -0
- data/ext/iodine/http.c +28 -4
- data/ext/iodine/http_request.c +2 -1
- data/ext/iodine/iodine_core.c +3 -2
- data/ext/iodine/sock.c +34 -20
- data/ext/iodine/spnlock.inc +2 -2
- data/ext/iodine/websockets.c +0 -2
- data/lib/iodine/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: efa1a3d3d054e8668d5e7f79bf04e61d5f0b169b
|
4
|
+
data.tar.gz: fe096a9cc9f6e6a1196c3c3091228c6d3a72a539
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a6d75ec543937ff9f660cf643b442bd8adc8f4c31deac8d9233570385a6ae38a8743c4f1360b7bf3215401019d254e1fbb31c9dc57a1a197ee3c56aad97a1a6
|
7
|
+
data.tar.gz: 980fa44bbba6a0ad6c43c5f8383482261540a6c602b52a98ad716d7bb51ff7635f8fd58aaeb0bfb60bdcbed74218fa98a9ea8edc3cb2122cb5ed1d88c104098e
|
data/CHANGELOG.md
CHANGED
@@ -8,6 +8,12 @@ Please notice that this change log contains changes for upcoming releases as wel
|
|
8
8
|
|
9
9
|
***
|
10
10
|
|
11
|
+
#### Change log v.0.3.6
|
12
|
+
|
13
|
+
**Update**: Now using `facil.io` v.0.4.3. This fixes some delays in websocket packet flushing (latency), as well as other internal polishes. It also promises some possible future feature extensions that could add a major performance boost.
|
14
|
+
|
15
|
+
***
|
16
|
+
|
11
17
|
#### Change log v.0.3.5
|
12
18
|
|
13
19
|
**Fix**: (`sock`) Fixed an issue with the `sendfile` implementation on macOS and BSD, where medium to large files wouldn't be sent correctly.
|
data/ext/iodine/defer.c
CHANGED
@@ -23,7 +23,7 @@ Compile time settings
|
|
23
23
|
#define DEFER_QUEUE_BUFFER 4096
|
24
24
|
#endif
|
25
25
|
#ifndef DEFER_THROTTLE
|
26
|
-
#define DEFER_THROTTLE
|
26
|
+
#define DEFER_THROTTLE 524287UL
|
27
27
|
#endif
|
28
28
|
|
29
29
|
/* *****************************************************************************
|
@@ -86,7 +86,7 @@ schedule:
|
|
86
86
|
error:
|
87
87
|
spn_unlock(&deferred.lock);
|
88
88
|
perror("ERROR CRITICAL: defer can't allocate task");
|
89
|
-
exit(
|
89
|
+
kill(0, SIGINT), exit(errno);
|
90
90
|
call_error:
|
91
91
|
return -1;
|
92
92
|
initialize:
|
@@ -156,6 +156,11 @@ int defer_join_thread(void *p_thr) {
|
|
156
156
|
return 0;
|
157
157
|
}
|
158
158
|
|
159
|
+
#pragma weak defer_thread_throttle
|
160
|
+
void defer_thread_throttle(unsigned long microsec) {
|
161
|
+
throttle_thread(microsec);
|
162
|
+
}
|
163
|
+
|
159
164
|
#else /* No pthreads... BYO thread implementation. */
|
160
165
|
|
161
166
|
#pragma weak defer_new_thread
|
@@ -170,6 +175,9 @@ int defer_join_thread(void *p_thr) {
|
|
170
175
|
return -1;
|
171
176
|
}
|
172
177
|
|
178
|
+
#pragma weak defer_thread_throttle
|
179
|
+
void defer_thread_throttle(unsigned long microsec) { return; }
|
180
|
+
|
173
181
|
#endif /* DEBUG || pthread default */
|
174
182
|
|
175
183
|
struct defer_pool {
|
@@ -181,8 +189,8 @@ struct defer_pool {
|
|
181
189
|
static void *defer_worker_thread(void *pool) {
|
182
190
|
signal(SIGPIPE, SIG_IGN);
|
183
191
|
size_t throttle = (((pool_pt)pool)->count) * DEFER_THROTTLE;
|
184
|
-
if (!throttle || throttle >
|
185
|
-
throttle =
|
192
|
+
if (!throttle || throttle > 1572864UL)
|
193
|
+
throttle = 1572864UL;
|
186
194
|
do {
|
187
195
|
throttle_thread(throttle);
|
188
196
|
defer_perform();
|
@@ -260,10 +268,11 @@ inline static void reap_children(void) {
|
|
260
268
|
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
261
269
|
if (sigaction(SIGCHLD, &sa, 0) == -1) {
|
262
270
|
perror("Child reaping initialization failed");
|
263
|
-
exit(
|
271
|
+
kill(0, SIGINT), exit(errno);
|
264
272
|
}
|
265
273
|
}
|
266
274
|
|
275
|
+
static int defer_fork_pid_id = 0;
|
267
276
|
/**
|
268
277
|
* Forks the process, starts up a thread pool and waits for all tasks to run.
|
269
278
|
* All existing tasks will run in all processes (multiple times).
|
@@ -308,6 +317,7 @@ int defer_perform_in_fork(unsigned int process_count,
|
|
308
317
|
goto finish;
|
309
318
|
for (pids_count = 0; pids_count < process_count; pids_count++) {
|
310
319
|
if (!(pids[pids_count] = fork())) {
|
320
|
+
defer_fork_pid_id = pids_count + 1;
|
311
321
|
forked_pool = defer_pool_start(thread_count);
|
312
322
|
defer_pool_wait(forked_pool);
|
313
323
|
defer_perform();
|
@@ -344,6 +354,9 @@ finish:
|
|
344
354
|
* up. */
|
345
355
|
int defer_fork_is_active(void) { return forked_pool && forked_pool->flag; }
|
346
356
|
|
357
|
+
/** Returns the process number for the current working proceess. 0 == parent. */
|
358
|
+
int defer_fork_pid(void) { return defer_fork_pid_id; }
|
359
|
+
|
347
360
|
/* *****************************************************************************
|
348
361
|
Test
|
349
362
|
***************************************************************************** */
|
data/ext/iodine/defer.h
CHANGED
@@ -74,6 +74,14 @@ Return value is ignored.
|
|
74
74
|
*/
|
75
75
|
int defer_join_thread(void *p_thr);
|
76
76
|
|
77
|
+
/**
|
78
|
+
OVERRIDE THIS to replace the default pthread implementation.
|
79
|
+
|
80
|
+
Throttles or reschedules the current running thread. Default implementation
|
81
|
+
simply micro-sleeps.
|
82
|
+
*/
|
83
|
+
void defer_thread_throttle(unsigned long microsec);
|
84
|
+
|
77
85
|
/* *****************************************************************************
|
78
86
|
Child Process support (`fork`)
|
79
87
|
***************************************************************************** */
|
@@ -97,6 +105,8 @@ int defer_perform_in_fork(unsigned int process_count,
|
|
97
105
|
/** Returns TRUE (1) if the forked thread pool hadn't been signaled to finish
|
98
106
|
* up. */
|
99
107
|
int defer_fork_is_active(void);
|
108
|
+
/** Returns the process number for the current working proceess. 0 == parent. */
|
109
|
+
int defer_fork_pid(void);
|
100
110
|
|
101
111
|
#ifdef __cplusplus
|
102
112
|
} /* closing brace for extern "C" */
|
data/ext/iodine/facil.c
CHANGED
@@ -8,6 +8,7 @@ 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 "fio_list.h"
|
11
12
|
|
12
13
|
#include <errno.h>
|
13
14
|
#include <signal.h>
|
@@ -56,16 +57,22 @@ static inline void clear_connection_data_unsafe(intptr_t uuid,
|
|
56
57
|
inline static protocol_s *protocol_try_lock(intptr_t fd,
|
57
58
|
enum facil_protocol_lock_e type) {
|
58
59
|
if (spn_trylock(&fd_data(fd).lock))
|
59
|
-
|
60
|
+
goto would_block;
|
60
61
|
protocol_s *pr = fd_data(fd).protocol;
|
61
62
|
if (!pr) {
|
62
63
|
spn_unlock(&fd_data(fd).lock);
|
64
|
+
errno = EBADF;
|
63
65
|
return NULL;
|
64
66
|
}
|
65
|
-
if (spn_trylock(&prt_meta(pr).locks[type]))
|
66
|
-
|
67
|
+
if (spn_trylock(&prt_meta(pr).locks[type])) {
|
68
|
+
spn_unlock(&fd_data(fd).lock);
|
69
|
+
goto would_block;
|
70
|
+
}
|
67
71
|
spn_unlock(&fd_data(fd).lock);
|
68
72
|
return pr;
|
73
|
+
would_block:
|
74
|
+
errno = EWOULDBLOCK;
|
75
|
+
return NULL;
|
69
76
|
}
|
70
77
|
/** See `facil_protocol_try_lock` for details. */
|
71
78
|
inline static void protocol_unlock(protocol_s *pr,
|
@@ -363,9 +370,10 @@ listener_alloc(struct facil_listen_args settings) {
|
|
363
370
|
inline static void listener_on_start(size_t fd) {
|
364
371
|
intptr_t uuid = sock_fd2uuid(fd);
|
365
372
|
if (uuid < 0)
|
366
|
-
fprintf(stderr, "ERROR: listening socket dropped?\n"),
|
373
|
+
fprintf(stderr, "ERROR: listening socket dropped?\n"), kill(0, SIGINT),
|
374
|
+
exit(4);
|
367
375
|
if (evio_add(fd, (void *)uuid) < 0)
|
368
|
-
perror("Couldn't register listening socket"), exit(4);
|
376
|
+
perror("Couldn't register listening socket"), kill(0, SIGINT), exit(4);
|
369
377
|
fd_data(fd).active = facil_data->last_cycle;
|
370
378
|
// call the on_init callback
|
371
379
|
struct ListenerProtocol *listener =
|
@@ -540,7 +548,8 @@ static inline timer_protocol_s *timer_alloc(void (*task)(void *), void *arg,
|
|
540
548
|
inline static void timer_on_server_start(int fd) {
|
541
549
|
if (evio_add_timer(fd, (void *)sock_fd2uuid(fd),
|
542
550
|
prot2timer(fd_data(fd).protocol).milliseconds))
|
543
|
-
perror("Couldn't register a required timed event."),
|
551
|
+
perror("Couldn't register a required timed event."), kill(0, SIGINT),
|
552
|
+
exit(4);
|
544
553
|
}
|
545
554
|
|
546
555
|
/**
|
@@ -581,6 +590,261 @@ error:
|
|
581
590
|
return -1;
|
582
591
|
}
|
583
592
|
|
593
|
+
/* *****************************************************************************
|
594
|
+
Cluster Messaging
|
595
|
+
***************************************************************************** */
|
596
|
+
/* cluster state management */
|
597
|
+
static struct {
|
598
|
+
uintptr_t count;
|
599
|
+
fio_list_s handlers;
|
600
|
+
spn_lock_i lock;
|
601
|
+
struct facil_cluster_data_pipe {
|
602
|
+
intptr_t in;
|
603
|
+
intptr_t out;
|
604
|
+
} * pipes;
|
605
|
+
} facil_cluster_data = {.handlers.next = &facil_cluster_data.handlers,
|
606
|
+
.handlers.prev = &facil_cluster_data.handlers,
|
607
|
+
.lock = SPN_LOCK_INIT};
|
608
|
+
|
609
|
+
/* internal support for the default pub/sub cluster engin */
|
610
|
+
#pragma weak pubsub_cluster_init
|
611
|
+
void pubsub_cluster_init(void) {}
|
612
|
+
|
613
|
+
/* message handler */
|
614
|
+
typedef struct {
|
615
|
+
fio_list_s list;
|
616
|
+
void (*on_message)(void *data, uint32_t len);
|
617
|
+
uint32_t msg_type;
|
618
|
+
} fio_cluster_handler;
|
619
|
+
|
620
|
+
/* message sending and protocol. */
|
621
|
+
struct facil_cluster_msg_packet {
|
622
|
+
uint16_t count;
|
623
|
+
spn_lock_i lock;
|
624
|
+
struct facil_cluster_msg {
|
625
|
+
uint32_t len;
|
626
|
+
uint32_t msg_type;
|
627
|
+
} payload;
|
628
|
+
uint8_t data[];
|
629
|
+
};
|
630
|
+
|
631
|
+
/* pipe communication protocol object. */
|
632
|
+
struct facil_cluster_protocol {
|
633
|
+
protocol_s protocol;
|
634
|
+
size_t mem_size;
|
635
|
+
size_t read;
|
636
|
+
struct facil_cluster_msg *msg;
|
637
|
+
};
|
638
|
+
|
639
|
+
/* cluster handling of message */
|
640
|
+
static void facil_cluster_handle_msg(struct facil_cluster_msg *msg) {
|
641
|
+
fio_cluster_handler *hnd;
|
642
|
+
spn_lock(&facil_cluster_data.lock);
|
643
|
+
fio_list_for_each(fio_cluster_handler, list, hnd,
|
644
|
+
facil_cluster_data.handlers) {
|
645
|
+
if (hnd->msg_type == msg->msg_type) {
|
646
|
+
hnd->on_message(msg + 1, msg->len - sizeof(*msg));
|
647
|
+
}
|
648
|
+
}
|
649
|
+
spn_unlock(&facil_cluster_data.lock);
|
650
|
+
}
|
651
|
+
|
652
|
+
/* cluster service ID */
|
653
|
+
static char *facil_cluster_protocol_id = "facil_io_internal_cluster_protocol";
|
654
|
+
|
655
|
+
/* cluster protocol on_close callback */
|
656
|
+
static void facil_cluster_on_close(protocol_s *pr_) {
|
657
|
+
struct facil_cluster_protocol *pr = (struct facil_cluster_protocol *)pr_;
|
658
|
+
if (pr->msg)
|
659
|
+
free(pr->msg);
|
660
|
+
free(pr);
|
661
|
+
#if FACIL_PRINT_STATE == 1
|
662
|
+
if (defer_fork_is_active())
|
663
|
+
fprintf(stderr, "ERROR: facil cluster member (%d) closed prematurely.\n",
|
664
|
+
defer_fork_pid());
|
665
|
+
#endif
|
666
|
+
}
|
667
|
+
|
668
|
+
/* cluster protocol on_data callback */
|
669
|
+
static void facil_cluster_on_data(intptr_t uuid, protocol_s *pr_) {
|
670
|
+
struct facil_cluster_protocol *pr = (struct facil_cluster_protocol *)pr_;
|
671
|
+
while (1) {
|
672
|
+
errno = 0;
|
673
|
+
if (pr->read < 8 || pr->read < pr->msg->len) {
|
674
|
+
ssize_t read = sock_read(uuid, (void *)((uintptr_t)pr->msg + pr->read),
|
675
|
+
pr->mem_size - pr->read);
|
676
|
+
if (read <= 0) {
|
677
|
+
if (read < 0)
|
678
|
+
perror("ERROR: cluster pipe read error");
|
679
|
+
return;
|
680
|
+
}
|
681
|
+
pr->read += read;
|
682
|
+
if (pr->read < 8)
|
683
|
+
continue;
|
684
|
+
if (pr->mem_size < pr->msg->len) {
|
685
|
+
void *tmp = realloc(pr->msg, pr->msg->len);
|
686
|
+
if (!tmp)
|
687
|
+
perror("ERROR: (critical) cannot allocate memory for a clustered "
|
688
|
+
"message."),
|
689
|
+
kill(0, SIGINT), exit(errno);
|
690
|
+
pr->msg = tmp;
|
691
|
+
#if defined(FACIL_PRINT_STATE) && FACIL_PRINT_STATE == 1 && defined(DEBUG)
|
692
|
+
fprintf(stderr, "* Cluster (%d) Reallocated msg size to %u\n",
|
693
|
+
defer_fork_pid(), pr->msg->len);
|
694
|
+
#endif
|
695
|
+
pr->mem_size = pr->msg->len;
|
696
|
+
}
|
697
|
+
}
|
698
|
+
if ((size_t)(pr->msg->len) <= pr->read) {
|
699
|
+
facil_cluster_handle_msg(pr->msg);
|
700
|
+
pr->read = pr->read - pr->msg->len;
|
701
|
+
if (pr->read)
|
702
|
+
memcpy(pr->msg, (void *)((uintptr_t)pr->msg + pr->msg->len), pr->read);
|
703
|
+
}
|
704
|
+
}
|
705
|
+
}
|
706
|
+
|
707
|
+
/* Registers process specific information */
|
708
|
+
static void facil_cluster_register(void *arg1, void *arg2) {
|
709
|
+
(void)arg1;
|
710
|
+
(void)arg2;
|
711
|
+
if (facil_cluster_data.count < 2)
|
712
|
+
return;
|
713
|
+
// for (size_t i = 0; i < facil_cluster_data.count; i++) {
|
714
|
+
// if (i == (size_t)defer_fork_pid())
|
715
|
+
// continue;
|
716
|
+
// sock_close(facil_cluster_data.pipes[i].in);
|
717
|
+
// }
|
718
|
+
// sock_close(facil_cluster_data.pipes[defer_fork_pid()].out);
|
719
|
+
struct facil_cluster_protocol *pr = malloc(sizeof(*pr));
|
720
|
+
if (!pr)
|
721
|
+
perror("ERROR: (critical) cannot allocate memory for cluster."),
|
722
|
+
kill(0, SIGINT), exit(errno);
|
723
|
+
*pr = (struct facil_cluster_protocol){
|
724
|
+
.protocol.service = facil_cluster_protocol_id,
|
725
|
+
.protocol.on_data = facil_cluster_on_data,
|
726
|
+
.protocol.on_close = facil_cluster_on_close,
|
727
|
+
.protocol.ping = listener_ping,
|
728
|
+
};
|
729
|
+
pr->msg = calloc(1, 1024);
|
730
|
+
pr->mem_size = 1024;
|
731
|
+
if (!pr->msg)
|
732
|
+
perror("ERROR: (critical) cannot allocate memory for cluster."),
|
733
|
+
kill(0, SIGINT), exit(errno);
|
734
|
+
facil_attach(facil_cluster_data.pipes[defer_fork_pid()].in, &pr->protocol);
|
735
|
+
}
|
736
|
+
|
737
|
+
static void facil_cluster_init(uint16_t count) {
|
738
|
+
if (facil_cluster_data.pipes || count < 2)
|
739
|
+
return;
|
740
|
+
facil_cluster_data.pipes =
|
741
|
+
malloc((sizeof(struct facil_cluster_data_pipe) * count));
|
742
|
+
if (!facil_cluster_data.pipes)
|
743
|
+
goto error;
|
744
|
+
|
745
|
+
static protocol_s stub_protocol = {.service = NULL, .ping = listener_ping};
|
746
|
+
stub_protocol.service = facil_cluster_protocol_id;
|
747
|
+
|
748
|
+
facil_cluster_data.count = count;
|
749
|
+
int p_tmp[2];
|
750
|
+
|
751
|
+
for (size_t i = 0; i < count; i++) {
|
752
|
+
if (pipe(p_tmp))
|
753
|
+
goto error;
|
754
|
+
sock_set_non_block(p_tmp[0]);
|
755
|
+
sock_set_non_block(p_tmp[1]);
|
756
|
+
facil_cluster_data.pipes[i].in = sock_open(p_tmp[0]);
|
757
|
+
facil_cluster_data.pipes[i].out = sock_open(p_tmp[1]);
|
758
|
+
facil_attach(facil_cluster_data.pipes[i].in, &stub_protocol);
|
759
|
+
facil_attach(facil_cluster_data.pipes[i].out, &stub_protocol);
|
760
|
+
}
|
761
|
+
pubsub_cluster_init();
|
762
|
+
defer(facil_cluster_register, NULL, NULL);
|
763
|
+
return;
|
764
|
+
error:
|
765
|
+
perror("ERROR: (facil.io) Couldn't initialize cluster pipes");
|
766
|
+
kill(0, SIGINT), exit(errno);
|
767
|
+
}
|
768
|
+
|
769
|
+
static void facil_cluster_destroy(void) {
|
770
|
+
if (!facil_cluster_data.pipes)
|
771
|
+
return;
|
772
|
+
for (size_t i = 0; i < facil_cluster_data.count; i++) {
|
773
|
+
sock_close(facil_cluster_data.pipes[i].out);
|
774
|
+
sock_close(facil_cluster_data.pipes[i].in);
|
775
|
+
}
|
776
|
+
|
777
|
+
fio_cluster_handler *hnd;
|
778
|
+
while ((hnd = fio_list_pop(fio_cluster_handler, list,
|
779
|
+
facil_cluster_data.handlers)))
|
780
|
+
free(hnd);
|
781
|
+
free(facil_cluster_data.pipes);
|
782
|
+
facil_cluster_data.pipes = NULL;
|
783
|
+
}
|
784
|
+
|
785
|
+
void facil_cluster_set_handler(uint32_t msg_type,
|
786
|
+
void (*on_message)(void *data, uint32_t len)) {
|
787
|
+
fio_cluster_handler *hnd;
|
788
|
+
spn_lock(&facil_cluster_data.lock);
|
789
|
+
fio_list_for_each(fio_cluster_handler, list, hnd,
|
790
|
+
facil_cluster_data.handlers) {
|
791
|
+
if (hnd->msg_type == msg_type) {
|
792
|
+
hnd->on_message = on_message;
|
793
|
+
spn_unlock(&facil_cluster_data.lock);
|
794
|
+
return;
|
795
|
+
}
|
796
|
+
}
|
797
|
+
hnd = malloc(sizeof(*hnd));
|
798
|
+
*hnd = (fio_cluster_handler){.msg_type = msg_type, .on_message = on_message};
|
799
|
+
fio_list_push(fio_cluster_handler, list, facil_cluster_data.handlers, hnd);
|
800
|
+
spn_unlock(&facil_cluster_data.lock);
|
801
|
+
}
|
802
|
+
|
803
|
+
static void facil_msg_free(void *msg_) {
|
804
|
+
struct facil_cluster_msg_packet *msg = msg_;
|
805
|
+
spn_lock(&msg->lock);
|
806
|
+
msg->count--;
|
807
|
+
if (msg->count) {
|
808
|
+
spn_unlock(&msg->lock);
|
809
|
+
return;
|
810
|
+
}
|
811
|
+
free(msg);
|
812
|
+
}
|
813
|
+
int facil_cluster_send(uint32_t msg_type, void *data, uint32_t len) {
|
814
|
+
if (!defer_fork_is_active()) {
|
815
|
+
#ifdef DEBUG
|
816
|
+
fprintf(stderr,
|
817
|
+
"WARNING: (debug, ignored) `facil_cluster_send` can only be "
|
818
|
+
"called while facil.io is running.\n");
|
819
|
+
#endif
|
820
|
+
return -1;
|
821
|
+
}
|
822
|
+
if (facil_cluster_data.count < 2)
|
823
|
+
return 0;
|
824
|
+
if (!data || !len)
|
825
|
+
return -1;
|
826
|
+
struct facil_cluster_msg_packet *msg = malloc(len + sizeof(*msg));
|
827
|
+
if (!msg)
|
828
|
+
return -1;
|
829
|
+
msg->count = facil_cluster_data.count - 1;
|
830
|
+
msg->lock = SPN_LOCK_INIT;
|
831
|
+
msg->payload.len = len + sizeof(msg->payload);
|
832
|
+
msg->payload.msg_type = msg_type;
|
833
|
+
memcpy(msg->data, data, len);
|
834
|
+
for (int i = 0; i < (int)facil_cluster_data.count; i++) {
|
835
|
+
if (defer_fork_pid() == i)
|
836
|
+
continue;
|
837
|
+
if (sock_write2(.uuid = facil_cluster_data.pipes[i].out, .buffer = msg,
|
838
|
+
.offset =
|
839
|
+
((uintptr_t) &
|
840
|
+
(((struct facil_cluster_msg_packet *)(0))->payload)),
|
841
|
+
.length = msg->payload.len, .move = 1,
|
842
|
+
.dealloc = facil_msg_free))
|
843
|
+
perror("ERROR: Cluster `write` failed");
|
844
|
+
}
|
845
|
+
return 0;
|
846
|
+
}
|
847
|
+
|
584
848
|
/* *****************************************************************************
|
585
849
|
Running the server
|
586
850
|
***************************************************************************** */
|
@@ -685,6 +949,7 @@ static void facil_cleanup(void *arg) {
|
|
685
949
|
((struct facil_run_args *)arg)->on_finish();
|
686
950
|
defer_perform();
|
687
951
|
evio_close();
|
952
|
+
facil_cluster_destroy();
|
688
953
|
}
|
689
954
|
|
690
955
|
#undef facil_run
|
@@ -707,6 +972,7 @@ void facil_run(struct facil_run_args args) {
|
|
707
972
|
args.processes = 1;
|
708
973
|
if (!args.threads)
|
709
974
|
args.threads = 1;
|
975
|
+
facil_cluster_init(args.processes);
|
710
976
|
if (FACIL_PRINT_STATE) {
|
711
977
|
fprintf(stderr, "Server is running %u %s X %u %s, press ^C to stop\n",
|
712
978
|
args.processes, args.processes > 1 ? "workers" : "worker",
|
@@ -747,8 +1013,11 @@ int facil_attach(intptr_t uuid, protocol_s *protocol) {
|
|
747
1013
|
protocol->on_shutdown = mock_on_ev;
|
748
1014
|
protocol->rsv = 0;
|
749
1015
|
}
|
750
|
-
if (!sock_isvalid(uuid))
|
1016
|
+
if (!sock_isvalid(uuid)) {
|
1017
|
+
if (protocol)
|
1018
|
+
defer(deferred_on_close, protocol, NULL);
|
751
1019
|
return -1;
|
1020
|
+
}
|
752
1021
|
spn_lock(&uuid_data(uuid).lock);
|
753
1022
|
protocol_s *old_protocol = uuid_data(uuid).protocol;
|
754
1023
|
uuid_data(uuid).protocol = protocol;
|
@@ -787,7 +1056,7 @@ time_t facil_last_tick(void) { return facil_data->last_cycle; }
|
|
787
1056
|
*/
|
788
1057
|
protocol_s *facil_protocol_try_lock(intptr_t uuid,
|
789
1058
|
enum facil_protocol_lock_e type) {
|
790
|
-
if (sock_isvalid(uuid) || !uuid_data(uuid).protocol) {
|
1059
|
+
if (!sock_isvalid(uuid) || !uuid_data(uuid).protocol) {
|
791
1060
|
errno = EBADF;
|
792
1061
|
return NULL;
|
793
1062
|
}
|
data/ext/iodine/facil.h
CHANGED
@@ -287,6 +287,8 @@ void facil_run(struct facil_run_args args);
|
|
287
287
|
* The old protocol's `on_close` will be scheduled, if they both exist.
|
288
288
|
*
|
289
289
|
* Returns -1 on error and 0 on success.
|
290
|
+
*
|
291
|
+
* On error, the new protocol's `on_close` callback will be called.
|
290
292
|
*/
|
291
293
|
int facil_attach(intptr_t uuid, protocol_s *protocol);
|
292
294
|
|
@@ -399,6 +401,37 @@ struct facil_each_args_s {
|
|
399
401
|
int facil_each(struct facil_each_args_s args);
|
400
402
|
#define facil_each(...) facil_each((struct facil_each_args_s){__VA_ARGS__})
|
401
403
|
|
404
|
+
/* *****************************************************************************
|
405
|
+
Cluster specific API - cluster messaging.
|
406
|
+
***************************************************************************** */
|
407
|
+
|
408
|
+
/**
|
409
|
+
Sets a callback / handler for a message of type `msg_type`.
|
410
|
+
|
411
|
+
Callbacks are invoked using an O(n) matching, where `n` is the number of
|
412
|
+
registered callbacks.
|
413
|
+
|
414
|
+
The `msg_type` value can be any number less than 1,073,741,824. All values
|
415
|
+
starting at 1,073,741,824 are reserved for internal use.
|
416
|
+
*/
|
417
|
+
void facil_cluster_set_handler(uint32_t msg_type,
|
418
|
+
void (*on_message)(void *data, uint32_t len));
|
419
|
+
|
420
|
+
/** Sends a message of type `msg_type` to the **other** cluster processes.
|
421
|
+
|
422
|
+
`msg_type` should match a message type used when calling
|
423
|
+
`facil_cluster_set_handler` to set the appropriate callback.
|
424
|
+
|
425
|
+
Unknown `msg_type` values are silently ignored.
|
426
|
+
|
427
|
+
The `msg_type` value can be any number less than 1,073,741,824. All values
|
428
|
+
starting at 1,073,741,824 are reserved for internal use.
|
429
|
+
|
430
|
+
Callbacks are invoked using an O(n) matching, where `n` is the number of
|
431
|
+
registered callbacks.
|
432
|
+
*/
|
433
|
+
int facil_cluster_send(uint32_t msg_type, void *data, uint32_t len);
|
434
|
+
|
402
435
|
/* *****************************************************************************
|
403
436
|
Lower Level API - for special circumstances, use with care under .
|
404
437
|
***************************************************************************** */
|
@@ -0,0 +1,84 @@
|
|
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
|
+
#ifndef H_FACIL_IO_LIST_H
|
8
|
+
/**
|
9
|
+
A simple doubly linked list implementation... Doesn't do much, but does it well.
|
10
|
+
*/
|
11
|
+
#define H_FACIL_IO_LIST_H
|
12
|
+
|
13
|
+
/** The data structure for a doubly linked list. */
|
14
|
+
typedef struct fio_list_s {
|
15
|
+
struct fio_list_s *prev;
|
16
|
+
struct fio_list_s *next;
|
17
|
+
} fio_list_s;
|
18
|
+
|
19
|
+
/** An inline function that initializes a list's head. */
|
20
|
+
static inline __attribute__((unused)) void fio_list_init(fio_list_s *list) {
|
21
|
+
*list = (fio_list_s){.next = list, .prev = list};
|
22
|
+
}
|
23
|
+
|
24
|
+
/** A macro that evaluates to an initialized list head. */
|
25
|
+
#define FIO_LIST_INIT(name) \
|
26
|
+
(fio_list_s) { .next = &(name), .prev = &(name) }
|
27
|
+
|
28
|
+
/** Adds a list reference to the list at the specified position. */
|
29
|
+
static inline __attribute__((unused)) void fio_list_add(fio_list_s *pos,
|
30
|
+
fio_list_s *item) {
|
31
|
+
/* prepare item */
|
32
|
+
item->next = pos->next;
|
33
|
+
item->prev = pos;
|
34
|
+
/* inject item only after preperation (in case of misuse or memory tearing) */
|
35
|
+
pos->next = item;
|
36
|
+
item->next->prev = item;
|
37
|
+
}
|
38
|
+
|
39
|
+
/** Removes a list reference from the list. Returns the same reference. */
|
40
|
+
static inline __attribute__((unused)) fio_list_s *
|
41
|
+
fio_list_remove(fio_list_s *item) {
|
42
|
+
item->next->prev = item->prev;
|
43
|
+
item->prev->next = item->next;
|
44
|
+
*item = (fio_list_s){.next = item, .prev = item};
|
45
|
+
return item;
|
46
|
+
}
|
47
|
+
|
48
|
+
/** Takes a list pointer and returns a pointer to it's container. */
|
49
|
+
#define fio_list_object(type, member, plist) \
|
50
|
+
((type *)((uintptr_t)(plist) - (uintptr_t)(&(((type *)0)->member))))
|
51
|
+
|
52
|
+
/** iterates the whole list. */
|
53
|
+
#define fio_list_for_each(type, member, var, head) \
|
54
|
+
for (fio_list_s *pos = (head).next->next; \
|
55
|
+
&((var) = fio_list_object(type, member, pos->prev))->member != &(head); \
|
56
|
+
(var) = fio_list_object(type, member, pos), pos = pos->next)
|
57
|
+
|
58
|
+
/** Removes a member from the end of the list. */
|
59
|
+
#define fio_list_pop(type, member, head) \
|
60
|
+
(((head).prev == &(head)) \
|
61
|
+
? ((type *)(0x0)) \
|
62
|
+
: (fio_list_object(type, member, fio_list_remove((head).prev))))
|
63
|
+
|
64
|
+
/** Adds a member to the end of the list. */
|
65
|
+
#define fio_list_push(type, member, head, pitem) \
|
66
|
+
fio_list_add((head).prev, &(pitem)->member)
|
67
|
+
|
68
|
+
/** Removes a member from the beginning of the list. */
|
69
|
+
#define fio_list_shift(type, member, head) \
|
70
|
+
(((head).next == &(head)) \
|
71
|
+
? ((type *)(0x0)) \
|
72
|
+
: (fio_list_object(type, member, fio_list_remove((head).next))))
|
73
|
+
|
74
|
+
/** Adds a member to the beginning of the list. */
|
75
|
+
#define fio_list_unshift(type, member, head, pitem) \
|
76
|
+
fio_list_add((head).next, &(pitem)->member)
|
77
|
+
|
78
|
+
/** Tests if the list is empty. */
|
79
|
+
#define fio_list_is_empty(head) ((head).next == &(head))
|
80
|
+
|
81
|
+
/** Tests if the list is NOT empty. */
|
82
|
+
#define fio_list_any(head) ((head).next != &(head))
|
83
|
+
|
84
|
+
#endif
|
data/ext/iodine/http.c
CHANGED
@@ -9,6 +9,7 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
9
9
|
#include "http.h"
|
10
10
|
#include "http1.h"
|
11
11
|
|
12
|
+
#include <signal.h>
|
12
13
|
#include <string.h>
|
13
14
|
#include <strings.h>
|
14
15
|
#include <time.h>
|
@@ -54,13 +55,13 @@ int http_listen(const char *port, const char *address,
|
|
54
55
|
fprintf(
|
55
56
|
stderr,
|
56
57
|
"ERROR: http_listen requires the .on_request parameter to be set\n");
|
57
|
-
exit(11);
|
58
|
+
kill(0, SIGINT), exit(11);
|
58
59
|
}
|
59
60
|
http_on_open_func on_open_callback = http_get_on_open_func(&arg_settings);
|
60
61
|
if (!on_open_callback) {
|
61
62
|
fprintf(stderr, "ERROR: The requested HTTP protocol version isn't "
|
62
63
|
"supported at the moment.\n");
|
63
|
-
exit(11);
|
64
|
+
kill(0, SIGINT), exit(11);
|
64
65
|
}
|
65
66
|
http_settings_s *settings = malloc(sizeof(*settings));
|
66
67
|
*settings = arg_settings;
|
@@ -258,6 +259,25 @@ size_t http_date2str(char *target, struct tm *tmbuf) {
|
|
258
259
|
return -1; \
|
259
260
|
0; \
|
260
261
|
}))
|
262
|
+
static inline int hex2byte(uint8_t *dest, const uint8_t *source) {
|
263
|
+
if (source[0] >= '0' && source[0] <= '9')
|
264
|
+
*dest = (source[0] - '0');
|
265
|
+
else if ((source[0] >= 'a' && source[0] <= 'f') ||
|
266
|
+
(source[0] >= 'A' && source[0] <= 'F'))
|
267
|
+
*dest = (source[0] | 32) - 87;
|
268
|
+
else
|
269
|
+
return -1;
|
270
|
+
*dest <<= 4;
|
271
|
+
if (source[1] >= '0' && source[1] <= '9')
|
272
|
+
*dest |= (source[1] - '0');
|
273
|
+
else if ((source[1] >= 'a' && source[1] <= 'f') ||
|
274
|
+
(source[1] >= 'A' && source[1] <= 'F'))
|
275
|
+
*dest |= (source[1] | 32) - 87;
|
276
|
+
else
|
277
|
+
return -1;
|
278
|
+
return 0;
|
279
|
+
}
|
280
|
+
#undef hex_val_tmp
|
261
281
|
ssize_t http_decode_url(char *dest, const char *url_data, size_t length) {
|
262
282
|
char *pos = dest;
|
263
283
|
const char *end = url_data + length;
|
@@ -269,7 +289,9 @@ ssize_t http_decode_url(char *dest, const char *url_data, size_t length) {
|
|
269
289
|
} else if (*url_data == '%') {
|
270
290
|
// decode hex value
|
271
291
|
// this is a percent encoded value.
|
272
|
-
|
292
|
+
if (hex2byte((uint8_t *)pos, (uint8_t *)&url_data[1]))
|
293
|
+
return -1;
|
294
|
+
pos++;
|
273
295
|
url_data += 3;
|
274
296
|
} else
|
275
297
|
*(pos++) = *(url_data++);
|
@@ -288,7 +310,9 @@ ssize_t http_decode_url_unsafe(char *dest, const char *url_data) {
|
|
288
310
|
} else if (*url_data == '%') {
|
289
311
|
// decode hex value
|
290
312
|
// this is a percent encoded value.
|
291
|
-
|
313
|
+
if (hex2byte((uint8_t *)pos, (uint8_t *)&url_data[1]))
|
314
|
+
return -1;
|
315
|
+
pos++;
|
292
316
|
url_data += 3;
|
293
317
|
} else
|
294
318
|
*(pos++) = *(url_data++);
|
data/ext/iodine/http_request.c
CHANGED
@@ -7,6 +7,7 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
7
7
|
#include "http.h"
|
8
8
|
#include "http1_request.h"
|
9
9
|
|
10
|
+
#include <signal.h>
|
10
11
|
/* *****************************************************************************
|
11
12
|
Unsupported function placeholders
|
12
13
|
***************************************************************************** */
|
@@ -17,7 +18,7 @@ static void fail_destroy(http_request_s *req) {
|
|
17
18
|
(void)req;
|
18
19
|
fprintf(stderr, "ERROR: possible memory leak - request to be freed has "
|
19
20
|
"unsupported version.\n");
|
20
|
-
exit(9);
|
21
|
+
kill(0, SIGINT), exit(9);
|
21
22
|
}
|
22
23
|
|
23
24
|
static http_request_s *fail_dup(http_request_s *req) {
|
data/ext/iodine/iodine_core.c
CHANGED
@@ -616,11 +616,12 @@ static pthread_t sock_io_pthread;
|
|
616
616
|
|
617
617
|
static void *iodine_io_thread(void *arg) {
|
618
618
|
(void)arg;
|
619
|
+
struct timespec tm;
|
619
620
|
// static const struct timespec tm = {.tv_nsec = 524288UL};
|
620
621
|
while (sock_io_thread) {
|
621
622
|
sock_flush_all();
|
622
|
-
|
623
|
-
|
623
|
+
tm = (struct timespec){.tv_nsec = 524288UL, .tv_sec = 1};
|
624
|
+
nanosleep(&tm, NULL);
|
624
625
|
}
|
625
626
|
return NULL;
|
626
627
|
}
|
data/ext/iodine/sock.c
CHANGED
@@ -354,7 +354,6 @@ static int sock_write_buffer(int fd, struct packet_s *packet) {
|
|
354
354
|
fdinfo(fd).sent += written;
|
355
355
|
if (fdinfo(fd).sent == packet->buffer.len)
|
356
356
|
sock_packet_rotate_unsafe(fd);
|
357
|
-
return written;
|
358
357
|
}
|
359
358
|
return written;
|
360
359
|
}
|
@@ -368,12 +367,8 @@ static int sock_write_buffer_ext(int fd, struct packet_s *packet) {
|
|
368
367
|
fdinfo(fd).sent += written;
|
369
368
|
if (fdinfo(fd).sent == packet->buffer.len)
|
370
369
|
sock_packet_rotate_unsafe(fd);
|
371
|
-
return written;
|
372
370
|
}
|
373
|
-
|
374
|
-
errno == EINTR || errno == ENOTCONN))
|
375
|
-
return 0;
|
376
|
-
return -1;
|
371
|
+
return written;
|
377
372
|
}
|
378
373
|
|
379
374
|
static void sock_free_buffer_ext(packet_s *packet) {
|
@@ -415,13 +410,13 @@ static int sock_write_from_fd(int fd, struct packet_s *packet) {
|
|
415
410
|
goto read_error;
|
416
411
|
count = fdinfo(fd).rw_hooks->write(fd2uuid(fd), ext->buffer, count);
|
417
412
|
} while (count == BUFFER_FILE_READ_SIZE && packet->buffer.len);
|
418
|
-
if (count
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
413
|
+
if (count >= 0) {
|
414
|
+
fdinfo(fd).sent += count;
|
415
|
+
packet->buffer.len -= count;
|
416
|
+
if (!packet->buffer.len) {
|
417
|
+
sock_packet_rotate_unsafe(fd);
|
418
|
+
return 1;
|
419
|
+
}
|
425
420
|
}
|
426
421
|
return count;
|
427
422
|
|
@@ -853,17 +848,21 @@ ssize_t sock_read(intptr_t uuid, void *buf, size_t count) {
|
|
853
848
|
errno = EBADF;
|
854
849
|
return -1;
|
855
850
|
}
|
856
|
-
|
851
|
+
sock_rw_hook_s *rw = fdinfo(sock_uuid2fd(uuid)).rw_hooks;
|
857
852
|
unlock_fd(sock_uuid2fd(uuid));
|
853
|
+
if (count == 0)
|
854
|
+
return rw->read(uuid, buf, count);
|
855
|
+
int old_errno = errno;
|
856
|
+
ssize_t ret = rw->read(uuid, buf, count);
|
858
857
|
sock_touch(uuid);
|
859
858
|
if (ret > 0)
|
860
859
|
return ret;
|
861
860
|
if (ret < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR ||
|
862
|
-
errno == ENOTCONN))
|
861
|
+
errno == ENOTCONN)) {
|
862
|
+
errno = old_errno;
|
863
863
|
return 0;
|
864
|
-
|
864
|
+
}
|
865
865
|
sock_force_close(uuid);
|
866
|
-
errno = ret ? old_errno : ECONNRESET;
|
867
866
|
return -1;
|
868
867
|
}
|
869
868
|
|
@@ -978,23 +977,29 @@ ssize_t sock_flush(intptr_t uuid) {
|
|
978
977
|
return -1;
|
979
978
|
ssize_t ret;
|
980
979
|
lock_fd(fd);
|
980
|
+
sock_rw_hook_s *rw;
|
981
981
|
retry:
|
982
|
-
|
982
|
+
rw = fdinfo(fd).rw_hooks;
|
983
|
+
unlock_fd(fd);
|
984
|
+
while ((ret = rw->flush(fd)) > 0)
|
983
985
|
;
|
984
986
|
if (ret == -1) {
|
985
987
|
if (errno == EINTR)
|
986
988
|
goto retry;
|
987
|
-
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOTCONN
|
989
|
+
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOTCONN ||
|
990
|
+
errno == ENOSPC)
|
988
991
|
goto finish;
|
989
992
|
goto error;
|
990
993
|
}
|
994
|
+
lock_fd(fd);
|
991
995
|
while (fdinfo(fd).packet && (ret = fdinfo(fd).packet->metadata.write_func(
|
992
996
|
fd, fdinfo(fd).packet)) > 0)
|
993
997
|
;
|
994
998
|
if (ret == -1) {
|
995
999
|
if (errno == EINTR)
|
996
1000
|
goto retry;
|
997
|
-
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOTCONN
|
1001
|
+
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOTCONN ||
|
1002
|
+
errno == ENOSPC)
|
998
1003
|
goto finish;
|
999
1004
|
goto error;
|
1000
1005
|
}
|
@@ -1006,6 +1011,10 @@ finish:
|
|
1006
1011
|
return 0;
|
1007
1012
|
error:
|
1008
1013
|
unlock_fd(fd);
|
1014
|
+
// fprintf(stderr,
|
1015
|
+
// "ERROR: sock `write` failed"
|
1016
|
+
// " for %p with %d\n",
|
1017
|
+
// (void *)uuid, errno);
|
1009
1018
|
sock_force_close(uuid);
|
1010
1019
|
return -1;
|
1011
1020
|
}
|
@@ -1047,6 +1056,10 @@ buffer.
|
|
1047
1056
|
void sock_force_close(intptr_t uuid) {
|
1048
1057
|
if (validate_uuid(uuid))
|
1049
1058
|
return;
|
1059
|
+
// fprintf(stderr,
|
1060
|
+
// "ERROR: `sock_force_close` called"
|
1061
|
+
// " for %p with errno %d\n",
|
1062
|
+
// (void *)uuid, errno);
|
1050
1063
|
shutdown(sock_uuid2fd(uuid), SHUT_RDWR);
|
1051
1064
|
close(sock_uuid2fd(uuid));
|
1052
1065
|
clear_fd(sock_uuid2fd(uuid), 0);
|
@@ -1091,6 +1104,7 @@ ssize_t sock_buffer_send(intptr_t uuid, sock_buffer_s *buffer) {
|
|
1091
1104
|
tmp = &(*tmp)->metadata.next;
|
1092
1105
|
*tmp = packet;
|
1093
1106
|
unlock_fd(fd);
|
1107
|
+
defer(sock_flush_defer, (void *)uuid, NULL);
|
1094
1108
|
return 0;
|
1095
1109
|
}
|
1096
1110
|
|
data/ext/iodine/spnlock.inc
CHANGED
@@ -27,8 +27,8 @@ spinlock / sync for tasks
|
|
27
27
|
}
|
28
28
|
#define throttle_thread(micosec) \
|
29
29
|
{ \
|
30
|
-
const struct timespec tm = {.tv_nsec = (micosec),
|
31
|
-
.tv_sec =
|
30
|
+
const struct timespec tm = {.tv_nsec = (micosec & 0xfffff), \
|
31
|
+
.tv_sec = (micosec >> 20)}; \
|
32
32
|
nanosleep(&tm, NULL); \
|
33
33
|
}
|
34
34
|
#else /* no effective rescheduling, just spin... */
|
data/ext/iodine/websockets.c
CHANGED
@@ -715,8 +715,6 @@ cleanup:
|
|
715
715
|
// call the on_open callback
|
716
716
|
if (settings.on_open)
|
717
717
|
settings.on_open(ws);
|
718
|
-
// facil_defer(.uuid = ws->fd, .task = on_open,
|
719
|
-
// .arg = (void *)settings.on_open);
|
720
718
|
// update the protocol object, cleanning up the old one
|
721
719
|
facil_attach(ws->fd, (protocol_s *)ws);
|
722
720
|
return 0;
|
data/lib/iodine/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iodine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boaz Segev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-06-
|
11
|
+
date: 2017-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -129,6 +129,7 @@ files:
|
|
129
129
|
- ext/iodine/extconf.rb
|
130
130
|
- ext/iodine/facil.c
|
131
131
|
- ext/iodine/facil.h
|
132
|
+
- ext/iodine/fio_list.h
|
132
133
|
- ext/iodine/hex.c
|
133
134
|
- ext/iodine/hex.h
|
134
135
|
- ext/iodine/http.c
|