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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 84f989dded41a91d68df5799a63f428dec2dedd9
4
- data.tar.gz: 7810e73f1f50a5ac9428419d8f17fa6d31aeb8b4
3
+ metadata.gz: efa1a3d3d054e8668d5e7f79bf04e61d5f0b169b
4
+ data.tar.gz: fe096a9cc9f6e6a1196c3c3091228c6d3a72a539
5
5
  SHA512:
6
- metadata.gz: 607d4b9e6956149302d33e4f3b9a097a83ace382be08a7b55a80c86e4f57630c5dc0abcb7668ab69469a6aeb834f68606087d40f10ac5e3d8cc1f1714cd73e2b
7
- data.tar.gz: 4651ef17299e34d1eb83d7fd673e57340b3ec060c3d0b633918ef6876644624072e02f9c930acec4c1a010a4a5ce4ead2a5aa106d134b9882bbe75a36b0a00d2
6
+ metadata.gz: 6a6d75ec543937ff9f660cf643b442bd8adc8f4c31deac8d9233570385a6ae38a8743c4f1360b7bf3215401019d254e1fbb31c9dc57a1a197ee3c56aad97a1a6
7
+ data.tar.gz: 980fa44bbba6a0ad6c43c5f8383482261540a6c602b52a98ad716d7bb51ff7635f8fd58aaeb0bfb60bdcbed74218fa98a9ea8edc3cb2122cb5ed1d88c104098e
@@ -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.
@@ -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 16384UL
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(9);
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 > 524288UL)
185
- throttle = 524288UL;
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(1);
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
  ***************************************************************************** */
@@ -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" */
@@ -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
- return NULL;
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
- pr = NULL;
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"), exit(4);
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."), exit(4);
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
  }
@@ -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
@@ -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
- *(pos++) = (hex_val(url_data[1]) << 4) | hex_val(url_data[2]);
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
- *(pos++) = (hex_val(url_data[1]) << 4) | hex_val(url_data[2]);
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++);
@@ -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) {
@@ -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
- throttle_thread(524288UL);
623
- // nanosleep(&tm, NULL);
623
+ tm = (struct timespec){.tv_nsec = 524288UL, .tv_sec = 1};
624
+ nanosleep(&tm, NULL);
624
625
  }
625
626
  return NULL;
626
627
  }
@@ -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
- if (written < 0 && (errno == EWOULDBLOCK || errno == EAGAIN ||
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 < 0)
419
- return -1;
420
- fdinfo(fd).sent += count;
421
- packet->buffer.len -= count;
422
- if (!packet->buffer.len) {
423
- sock_packet_rotate_unsafe(fd);
424
- return 1;
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
- ssize_t ret = fdinfo(sock_uuid2fd(uuid)).rw_hooks->read(uuid, buf, count);
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
- int old_errno = errno;
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
- while ((ret = fdinfo(fd).rw_hooks->flush(fd)) > 0)
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
 
@@ -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 = /* (micosec >> 20)*/ 1}; \
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... */
@@ -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;
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.3.5'.freeze
2
+ VERSION = '0.3.6'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
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-02 00:00:00.000000000 Z
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