io-event 0.1.0 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d311214a03ab6047c7ba6952dfeeef690a453d0bd661df5e5419f477832b3cbd
4
- data.tar.gz: 51653482b73275b35b8ac590b5576d2d991170c463d540fd3e5b6dbd8c2d4555
3
+ metadata.gz: c0978ed0fd5d85399283cd8c4bb296f44662af2fad9a67e992000f3214898f5a
4
+ data.tar.gz: 154503dd7f0f52dc2ad166d262bb371f81fc60e148dd0c74eadb65051bc5e71f
5
5
  SHA512:
6
- metadata.gz: e9f333103f29c2e2a69f1573fefb11c01a23d2cf9cc56e30343e9e9f9ec6f5bd5f44408a95cbdd84bf9350b7eb7e3d2ea57df92702a97407e49ab7a6006b9506
7
- data.tar.gz: a35949d9fb40a0361b2627f3d998dfae230474a9fcb51f6361c0d3686560cc3d22749147e9a581dfc8f429ffd35b80d920a780400d66398e4de9c113d4122f8e
6
+ metadata.gz: a54cab01e6c314890284d9640e3404f6a34706402b2cbf6383bff69c6c341c0c84c56f64d1feebb46e875539e1baab95749ce7f41e5f15ae13af0d8b9e1e0c1a
7
+ data.tar.gz: 7b2891a70e1703616f104410d4f86898a5d9c2bfa6c8ecde0295bea1bfed6ee7efada50568bdf8f841e0360db370d209259f66f2cb17f97d2be47db8a1c2e101
data/ext/IO_Event.bundle CHANGED
Binary file
data/ext/Makefile CHANGED
@@ -139,8 +139,8 @@ target_prefix =
139
139
  LOCAL_LIBS =
140
140
  LIBS =
141
141
  ORIG_SRCS =
142
- SRCS = $(ORIG_SRCS) event.c selector.c kqueue.c
143
- OBJS = event.o selector.o kqueue.o
142
+ SRCS = $(ORIG_SRCS) event.c selector.c kqueue.c interrupt.c
143
+ OBJS = event.o selector.o kqueue.o interrupt.o
144
144
  HDRS = $(srcdir)/extconf.h
145
145
  LOCAL_HDRS =
146
146
  TARGET = IO_Event
data/ext/extconf.rb CHANGED
@@ -48,6 +48,9 @@ if have_header('sys/event.h')
48
48
  $srcs << "io/event/selector/kqueue.c"
49
49
  end
50
50
 
51
+ have_header('sys/eventfd.h')
52
+ $srcs << "io/event/interrupt.c"
53
+
51
54
  have_func("rb_io_descriptor")
52
55
  have_func("&rb_process_status_wait")
53
56
  have_func('rb_fiber_current')
data/ext/interrupt.o ADDED
Binary file
@@ -0,0 +1,108 @@
1
+ // Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in
11
+ // all copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ // THE SOFTWARE.
20
+
21
+ // static const int DEBUG = 0;
22
+
23
+ #include "interrupt.h"
24
+ #include <unistd.h>
25
+
26
+ #include "selector/selector.h"
27
+
28
+ #ifdef HAVE_SYS_EVENTFD_H
29
+ #include <sys/eventfd.h>
30
+
31
+ void IO_Event_Interrupt_open(struct IO_Event_Interrupt *interrupt)
32
+ {
33
+ interrupt->descriptor = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
34
+ rb_update_max_fd(interrupt->descriptor);
35
+ }
36
+
37
+ void IO_Event_Interrupt_close(struct IO_Event_Interrupt *interrupt)
38
+ {
39
+ close(interrupt->descriptor);
40
+ }
41
+
42
+ void IO_Event_Interrupt_signal(struct IO_Event_Interrupt *interrupt)
43
+ {
44
+ uint64_t value = 1;
45
+ ssize_t result = write(interrupt->descriptor, &value, sizeof(value));
46
+
47
+ if (result == -1) {
48
+ if (errno == EAGAIN || errno == EWOULDBLOCK) return;
49
+
50
+ rb_sys_fail("IO_Event_Interrupt_signal:write");
51
+ }
52
+ }
53
+
54
+ void IO_Event_Interrupt_clear(struct IO_Event_Interrupt *interrupt)
55
+ {
56
+ uint64_t value = 0;
57
+ ssize_t result = read(interrupt->descriptor, &value, sizeof(value));
58
+
59
+ if (result == -1) {
60
+ if (errno == EAGAIN || errno == EWOULDBLOCK) return;
61
+
62
+ rb_sys_fail("IO_Event_Interrupt_clear:read");
63
+ }
64
+ }
65
+ #else
66
+ void IO_Event_Interrupt_open(struct IO_Event_Interrupt *interrupt)
67
+ {
68
+ #ifdef __linux__
69
+ pipe2(interrupt->descriptor, O_CLOEXEC | O_NONBLOCK);
70
+ #else
71
+ pipe(interrupt->descriptor);
72
+ IO_Event_Selector_nonblock_set(interrupt->descriptor[0]);
73
+ IO_Event_Selector_nonblock_set(interrupt->descriptor[1]);
74
+ #endif
75
+
76
+ rb_update_max_fd(interrupt->descriptor[0]);
77
+ rb_update_max_fd(interrupt->descriptor[1]);
78
+ }
79
+
80
+ void IO_Event_Interrupt_close(struct IO_Event_Interrupt *interrupt)
81
+ {
82
+ close(interrupt->descriptor[0]);
83
+ close(interrupt->descriptor[1]);
84
+ }
85
+
86
+ void IO_Event_Interrupt_signal(struct IO_Event_Interrupt *interrupt)
87
+ {
88
+ ssize_t result = write(interrupt->descriptor[1], ".", 1);
89
+
90
+ if (result == -1) {
91
+ if (errno == EAGAIN || errno == EWOULDBLOCK) return;
92
+
93
+ rb_sys_fail("IO_Event_Interrupt_signal:write");
94
+ }
95
+ }
96
+
97
+ void IO_Event_Interrupt_clear(struct IO_Event_Interrupt *interrupt)
98
+ {
99
+ char buffer[128];
100
+ ssize_t result = read(interrupt->descriptor[0], buffer, sizeof(buffer));
101
+
102
+ if (result == -1) {
103
+ if (errno == EAGAIN || errno == EWOULDBLOCK) return;
104
+
105
+ rb_sys_fail("IO_Event_Interrupt_clear:read");
106
+ }
107
+ }
108
+ #endif
@@ -0,0 +1,47 @@
1
+ // Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in
11
+ // all copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ // THE SOFTWARE.
20
+
21
+ #pragma once
22
+
23
+ #include "extconf.h"
24
+
25
+ #ifdef HAVE_SYS_EVENTFD_H
26
+ struct IO_Event_Interrupt {
27
+ int descriptor;
28
+ };
29
+
30
+ inline int IO_Event_Interrupt_descriptor(struct IO_Event_Interrupt *interrupt) {
31
+ return interrupt->descriptor;
32
+ }
33
+ #else
34
+ struct IO_Event_Interrupt {
35
+ int descriptor[2];
36
+ };
37
+
38
+ inline int IO_Event_Interrupt_descriptor(struct IO_Event_Interrupt *interrupt) {
39
+ return interrupt->descriptor[0];
40
+ }
41
+ #endif
42
+
43
+ void IO_Event_Interrupt_open(struct IO_Event_Interrupt *interrupt);
44
+ void IO_Event_Interrupt_close(struct IO_Event_Interrupt *interrupt);
45
+
46
+ void IO_Event_Interrupt_signal(struct IO_Event_Interrupt *interrupt);
47
+ void IO_Event_Interrupt_clear(struct IO_Event_Interrupt *interrupt);
@@ -26,6 +26,9 @@
26
26
  #include <errno.h>
27
27
 
28
28
  #include "pidfd.c"
29
+ #include "../interrupt.h"
30
+
31
+ static const int DEBUG = 0;
29
32
 
30
33
  static VALUE IO_Event_Selector_EPoll = Qnil;
31
34
 
@@ -34,6 +37,8 @@ enum {EPOLL_MAX_EVENTS = 64};
34
37
  struct IO_Event_Selector_EPoll {
35
38
  struct IO_Event_Selector backend;
36
39
  int descriptor;
40
+ int blocked;
41
+ struct IO_Event_Interrupt interrupt;
37
42
  };
38
43
 
39
44
  void IO_Event_Selector_EPoll_Type_mark(void *_data)
@@ -47,6 +52,8 @@ void close_internal(struct IO_Event_Selector_EPoll *data) {
47
52
  if (data->descriptor >= 0) {
48
53
  close(data->descriptor);
49
54
  data->descriptor = -1;
55
+
56
+ IO_Event_Interrupt_close(&data->interrupt);
50
57
  }
51
58
  }
52
59
 
@@ -85,6 +92,21 @@ VALUE IO_Event_Selector_EPoll_allocate(VALUE self) {
85
92
  return instance;
86
93
  }
87
94
 
95
+ void IO_Event_Interrupt_add(struct IO_Event_Interrupt *interrupt, struct IO_Event_Selector_EPoll *data) {
96
+ int descriptor = IO_Event_Interrupt_descriptor(interrupt);
97
+
98
+ struct epoll_event event = {
99
+ .events = EPOLLIN|EPOLLRDHUP,
100
+ .data = {.ptr = NULL},
101
+ };
102
+
103
+ int result = epoll_ctl(data->descriptor, EPOLL_CTL_ADD, descriptor, &event);
104
+
105
+ if (result == -1) {
106
+ rb_sys_fail("IO_Event_Interrupt_add:epoll_ctl");
107
+ }
108
+ }
109
+
88
110
  VALUE IO_Event_Selector_EPoll_initialize(VALUE self, VALUE loop) {
89
111
  struct IO_Event_Selector_EPoll *data = NULL;
90
112
  TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, data);
@@ -100,9 +122,19 @@ VALUE IO_Event_Selector_EPoll_initialize(VALUE self, VALUE loop) {
100
122
  rb_update_max_fd(data->descriptor);
101
123
  }
102
124
 
125
+ IO_Event_Interrupt_open(&data->interrupt);
126
+ IO_Event_Interrupt_add(&data->interrupt, data);
127
+
103
128
  return self;
104
129
  }
105
130
 
131
+ VALUE IO_Event_Selector_EPoll_loop(VALUE self) {
132
+ struct IO_Event_Selector_EPoll *data = NULL;
133
+ TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, data);
134
+
135
+ return data->backend.loop;
136
+ }
137
+
106
138
  VALUE IO_Event_Selector_EPoll_close(VALUE self) {
107
139
  struct IO_Event_Selector_EPoll *data = NULL;
108
140
  TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, data);
@@ -498,7 +530,9 @@ void * select_internal(void *_arguments) {
498
530
 
499
531
  static
500
532
  void select_internal_without_gvl(struct select_arguments *arguments) {
533
+ arguments->data->blocked = 1;
501
534
  rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
535
+ arguments->data->blocked = 0;
502
536
 
503
537
  if (arguments->count == -1) {
504
538
  rb_sys_fail("select_internal_without_gvl:epoll_wait");
@@ -525,29 +559,54 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
525
559
  .timeout = 0
526
560
  };
527
561
 
562
+ // Process any currently pending events:
528
563
  select_internal_with_gvl(&arguments);
529
-
530
- // If the ready list was empty and no events were processed:
531
- if (!ready && arguments.count == 0) {
564
+
565
+ // If we:
566
+ // 1. Didn't process any ready fibers, and
567
+ // 2. Didn't process any events from non-blocking select (above), and
568
+ // 3. There are no items in the ready list,
569
+ // then we can perform a blocking select.
570
+ if (!ready && !arguments.count && !data->backend.ready) {
532
571
  arguments.timeout = make_timeout(duration);
533
572
 
534
573
  if (arguments.timeout != 0) {
574
+ // Wait for events to occur
535
575
  select_internal_without_gvl(&arguments);
536
576
  }
537
577
  }
538
578
 
539
579
  for (int i = 0; i < arguments.count; i += 1) {
540
- VALUE fiber = (VALUE)arguments.events[i].data.ptr;
541
- VALUE result = INT2NUM(arguments.events[i].events);
580
+ const struct epoll_event *event = &arguments.events[i];
581
+ if (DEBUG) fprintf(stderr, "-> ptr=%p events=%d\n", event->data.ptr, event->events);
542
582
 
543
- // fprintf(stderr, "-> fiber=%p descriptor=%d\n", (void*)fiber, events[i].data.fd);
544
-
545
- IO_Event_Selector_fiber_transfer(fiber, 1, &result);
583
+ if (event->data.ptr) {
584
+ VALUE fiber = (VALUE)event->data.ptr;
585
+ VALUE result = INT2NUM(event->events);
586
+
587
+ IO_Event_Selector_fiber_transfer(fiber, 1, &result);
588
+ } else {
589
+ IO_Event_Interrupt_clear(&data->interrupt);
590
+ }
546
591
  }
547
592
 
548
593
  return INT2NUM(arguments.count);
549
594
  }
550
595
 
596
+ VALUE IO_Event_Selector_EPoll_wakeup(VALUE self) {
597
+ struct IO_Event_Selector_EPoll *data = NULL;
598
+ TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, data);
599
+
600
+ // If we are blocking, we can schedule a nop event to wake up the selector:
601
+ if (data->blocked) {
602
+ IO_Event_Interrupt_signal(&data->interrupt);
603
+
604
+ return Qtrue;
605
+ }
606
+
607
+ return Qfalse;
608
+ }
609
+
551
610
  void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
552
611
  IO_Event_Selector_EPoll = rb_define_class_under(IO_Event_Selector, "EPoll", rb_cObject);
553
612
  rb_gc_register_mark_object(IO_Event_Selector_EPoll);
@@ -555,6 +614,8 @@ void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
555
614
  rb_define_alloc_func(IO_Event_Selector_EPoll, IO_Event_Selector_EPoll_allocate);
556
615
  rb_define_method(IO_Event_Selector_EPoll, "initialize", IO_Event_Selector_EPoll_initialize, 1);
557
616
 
617
+ rb_define_method(IO_Event_Selector_EPoll, "loop", IO_Event_Selector_EPoll_loop, 0);
618
+
558
619
  rb_define_method(IO_Event_Selector_EPoll, "transfer", IO_Event_Selector_EPoll_transfer, 0);
559
620
  rb_define_method(IO_Event_Selector_EPoll, "resume", IO_Event_Selector_EPoll_resume, -1);
560
621
  rb_define_method(IO_Event_Selector_EPoll, "yield", IO_Event_Selector_EPoll_yield, 0);
@@ -564,6 +625,7 @@ void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
564
625
  rb_define_method(IO_Event_Selector_EPoll, "ready?", IO_Event_Selector_EPoll_ready_p, 0);
565
626
 
566
627
  rb_define_method(IO_Event_Selector_EPoll, "select", IO_Event_Selector_EPoll_select, 1);
628
+ rb_define_method(IO_Event_Selector_EPoll, "wakeup", IO_Event_Selector_EPoll_wakeup, 0);
567
629
  rb_define_method(IO_Event_Selector_EPoll, "close", IO_Event_Selector_EPoll_close, 0);
568
630
 
569
631
  rb_define_method(IO_Event_Selector_EPoll, "io_wait", IO_Event_Selector_EPoll_io_wait, 3);
@@ -20,6 +20,8 @@
20
20
 
21
21
  #pragma once
22
22
 
23
+ #include <ruby.h>
24
+
23
25
  #define IO_EVENT_SELECTOR_EPOLL
24
26
 
25
27
  void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector);
@@ -33,6 +33,8 @@ enum {KQUEUE_MAX_EVENTS = 64};
33
33
  struct IO_Event_Selector_KQueue {
34
34
  struct IO_Event_Selector backend;
35
35
  int descriptor;
36
+
37
+ int blocked;
36
38
  };
37
39
 
38
40
  void IO_Event_Selector_KQueue_Type_mark(void *_data)
@@ -80,6 +82,7 @@ VALUE IO_Event_Selector_KQueue_allocate(VALUE self) {
80
82
 
81
83
  IO_Event_Selector_initialize(&data->backend, Qnil);
82
84
  data->descriptor = -1;
85
+ data->blocked = 0;
83
86
 
84
87
  return instance;
85
88
  }
@@ -103,6 +106,13 @@ VALUE IO_Event_Selector_KQueue_initialize(VALUE self, VALUE loop) {
103
106
  return self;
104
107
  }
105
108
 
109
+ VALUE IO_Event_Selector_KQueue_loop(VALUE self) {
110
+ struct IO_Event_Selector_KQueue *data = NULL;
111
+ TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, data);
112
+
113
+ return data->backend.loop;
114
+ }
115
+
106
116
  VALUE IO_Event_Selector_KQueue_close(VALUE self) {
107
117
  struct IO_Event_Selector_KQueue *data = NULL;
108
118
  TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, data);
@@ -561,7 +571,10 @@ void * select_internal(void *_arguments) {
561
571
 
562
572
  static
563
573
  void select_internal_without_gvl(struct select_arguments *arguments) {
574
+ arguments->data->blocked = 1;
575
+
564
576
  rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
577
+ arguments->data->blocked = 0;
565
578
 
566
579
  if (arguments->count == -1) {
567
580
  rb_sys_fail("select_internal_without_gvl:kevent");
@@ -592,18 +605,23 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
592
605
  }
593
606
  };
594
607
 
608
+ arguments.timeout = &arguments.storage;
609
+
595
610
  // We break this implementation into two parts.
596
611
  // (1) count = kevent(..., timeout = 0)
597
612
  // (2) without gvl: kevent(..., timeout = 0) if count == 0 and timeout != 0
598
613
  // This allows us to avoid releasing and reacquiring the GVL.
599
614
  // Non-comprehensive testing shows this gives a 1.5x speedup.
600
- arguments.timeout = &arguments.storage;
601
615
 
602
616
  // First do the syscall with no timeout to get any immediately available events:
603
617
  select_internal_with_gvl(&arguments);
604
618
 
605
- // If there were no pending events, if we have a timeout, wait for more events:
606
- if (!ready && arguments.count == 0) {
619
+ // If we:
620
+ // 1. Didn't process any ready fibers, and
621
+ // 2. Didn't process any events from non-blocking select (above), and
622
+ // 3. There are no items in the ready list,
623
+ // then we can perform a blocking select.
624
+ if (!ready && !arguments.count && !data->backend.ready) {
607
625
  arguments.timeout = make_timeout(duration, &arguments.storage);
608
626
 
609
627
  if (!timeout_nonblocking(arguments.timeout)) {
@@ -614,15 +632,40 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
614
632
  }
615
633
 
616
634
  for (int i = 0; i < arguments.count; i += 1) {
617
- VALUE fiber = (VALUE)arguments.events[i].udata;
618
- VALUE result = INT2NUM(arguments.events[i].filter);
619
-
620
- IO_Event_Selector_fiber_transfer(fiber, 1, &result);
635
+ if (arguments.events[i].udata) {
636
+ VALUE fiber = (VALUE)arguments.events[i].udata;
637
+ VALUE result = INT2NUM(arguments.events[i].filter);
638
+
639
+ IO_Event_Selector_fiber_transfer(fiber, 1, &result);
640
+ }
621
641
  }
622
642
 
623
643
  return INT2NUM(arguments.count);
624
644
  }
625
645
 
646
+ VALUE IO_Event_Selector_KQueue_wakeup(VALUE self) {
647
+ struct IO_Event_Selector_KQueue *data = NULL;
648
+ TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, data);
649
+
650
+ if (data->blocked) {
651
+ struct kevent trigger = {0};
652
+
653
+ trigger.filter = EVFILT_USER;
654
+ trigger.flags = EV_ADD|EV_CLEAR;
655
+ trigger.fflags = NOTE_TRIGGER;
656
+
657
+ int result = kevent(data->descriptor, &trigger, 1, NULL, 0, NULL);
658
+
659
+ if (result == -1) {
660
+ rb_sys_fail("IO_Event_Selector_KQueue_wakeup:kevent");
661
+ }
662
+
663
+ return Qtrue;
664
+ }
665
+
666
+ return Qfalse;
667
+ }
668
+
626
669
  void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
627
670
  IO_Event_Selector_KQueue = rb_define_class_under(IO_Event_Selector, "KQueue", rb_cObject);
628
671
  rb_gc_register_mark_object(IO_Event_Selector_KQueue);
@@ -630,6 +673,8 @@ void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
630
673
  rb_define_alloc_func(IO_Event_Selector_KQueue, IO_Event_Selector_KQueue_allocate);
631
674
  rb_define_method(IO_Event_Selector_KQueue, "initialize", IO_Event_Selector_KQueue_initialize, 1);
632
675
 
676
+ rb_define_method(IO_Event_Selector_KQueue, "loop", IO_Event_Selector_KQueue_loop, 0);
677
+
633
678
  rb_define_method(IO_Event_Selector_KQueue, "transfer", IO_Event_Selector_KQueue_transfer, 0);
634
679
  rb_define_method(IO_Event_Selector_KQueue, "resume", IO_Event_Selector_KQueue_resume, -1);
635
680
  rb_define_method(IO_Event_Selector_KQueue, "yield", IO_Event_Selector_KQueue_yield, 0);
@@ -639,6 +684,7 @@ void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
639
684
  rb_define_method(IO_Event_Selector_KQueue, "ready?", IO_Event_Selector_KQueue_ready_p, 0);
640
685
 
641
686
  rb_define_method(IO_Event_Selector_KQueue, "select", IO_Event_Selector_KQueue_select, 1);
687
+ rb_define_method(IO_Event_Selector_KQueue, "wakeup", IO_Event_Selector_KQueue_wakeup, 0);
642
688
  rb_define_method(IO_Event_Selector_KQueue, "close", IO_Event_Selector_KQueue_close, 0);
643
689
 
644
690
  rb_define_method(IO_Event_Selector_KQueue, "io_wait", IO_Event_Selector_KQueue_io_wait, 3);
@@ -18,6 +18,8 @@
18
18
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  // THE SOFTWARE.
20
20
 
21
+ #pragma once
22
+
21
23
  #include <ruby.h>
22
24
  #include <ruby/thread.h>
23
25
  #include <ruby/io.h>
@@ -37,6 +37,7 @@ struct IO_Event_Selector_URing {
37
37
  struct IO_Event_Selector backend;
38
38
  struct io_uring ring;
39
39
  size_t pending;
40
+ int blocked;
40
41
  };
41
42
 
42
43
  void IO_Event_Selector_URing_Type_mark(void *_data)
@@ -86,7 +87,8 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
86
87
  data->ring.ring_fd = -1;
87
88
 
88
89
  data->pending = 0;
89
-
90
+ data->blocked = 0;
91
+
90
92
  return instance;
91
93
  }
92
94
 
@@ -106,6 +108,13 @@ VALUE IO_Event_Selector_URing_initialize(VALUE self, VALUE loop) {
106
108
  return self;
107
109
  }
108
110
 
111
+ VALUE IO_Event_Selector_URing_loop(VALUE self) {
112
+ struct IO_Event_Selector_URing *data = NULL;
113
+ TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, data);
114
+
115
+ return data->backend.loop;
116
+ }
117
+
109
118
  VALUE IO_Event_Selector_URing_close(VALUE self) {
110
119
  struct IO_Event_Selector_URing *data = NULL;
111
120
  TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, data);
@@ -156,7 +165,8 @@ VALUE IO_Event_Selector_URing_raise(int argc, VALUE *argv, VALUE self)
156
165
 
157
166
  return IO_Event_Selector_raise(&data->backend, argc, argv);
158
167
  }
159
-
168
+
169
+ int blocked;
160
170
  VALUE IO_Event_Selector_URing_ready_p(VALUE self) {
161
171
  struct IO_Event_Selector_URing *data = NULL;
162
172
  TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, data);
@@ -171,14 +181,14 @@ int io_uring_submit_flush(struct IO_Event_Selector_URing *data) {
171
181
 
172
182
  // Try to submit:
173
183
  int result = io_uring_submit(&data->ring);
174
-
184
+
175
185
  if (result >= 0) {
176
186
  // If it was submitted, reset pending count:
177
187
  data->pending = 0;
178
188
  } else if (result != -EBUSY && result != -EAGAIN) {
179
189
  rb_syserr_fail(-result, "io_uring_submit_flush");
180
190
  }
181
-
191
+
182
192
  return result;
183
193
  }
184
194
 
@@ -194,7 +204,7 @@ int io_uring_submit_now(struct IO_Event_Selector_URing *data) {
194
204
  data->pending = 0;
195
205
  return result;
196
206
  }
197
-
207
+
198
208
  if (result == -EBUSY || result == -EAGAIN) {
199
209
  IO_Event_Selector_yield(&data->backend);
200
210
  } else {
@@ -214,7 +224,7 @@ struct io_uring_sqe * io_get_sqe(struct IO_Event_Selector_URing *data) {
214
224
  while (sqe == NULL) {
215
225
  // The submit queue is full, we need to drain it:
216
226
  io_uring_submit_now(data);
217
-
227
+
218
228
  sqe = io_uring_get_sqe(&data->ring);
219
229
  }
220
230
 
@@ -523,10 +533,8 @@ struct select_arguments {
523
533
  static
524
534
  void * select_internal(void *_arguments) {
525
535
  struct select_arguments * arguments = (struct select_arguments *)_arguments;
526
-
527
- io_uring_submit_flush(arguments->data);
528
-
529
536
  struct io_uring_cqe *cqe = NULL;
537
+
530
538
  arguments->result = io_uring_wait_cqe_timeout(&arguments->data->ring, &cqe, arguments->timeout);
531
539
 
532
540
  return NULL;
@@ -534,7 +542,11 @@ void * select_internal(void *_arguments) {
534
542
 
535
543
  static
536
544
  int select_internal_without_gvl(struct select_arguments *arguments) {
545
+ io_uring_submit_flush(arguments->data);
546
+
547
+ arguments->data->blocked = 1;
537
548
  rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
549
+ arguments->data->blocked = 0;
538
550
 
539
551
  if (arguments->result == -ETIME) {
540
552
  arguments->result = 0;
@@ -548,6 +560,8 @@ int select_internal_without_gvl(struct select_arguments *arguments) {
548
560
  return arguments->result;
549
561
  }
550
562
 
563
+ // #define IO_EVENT_SELECTOR_URING_UDATA_INTERRUPT ((__u64) -2)
564
+
551
565
  static inline
552
566
  unsigned select_process_completions(struct io_uring *ring) {
553
567
  unsigned completed = 0;
@@ -563,6 +577,10 @@ unsigned select_process_completions(struct io_uring *ring) {
563
577
  continue;
564
578
  }
565
579
 
580
+ // if (cqe->user_data == IO_EVENT_SELECTOR_URING_UDATA_INTERRUPT) {
581
+ // io_uring_cq_advance(ring, 1);
582
+ // }
583
+
566
584
  VALUE fiber = (VALUE)cqe->user_data;
567
585
  VALUE result = RB_INT2NUM(cqe->res);
568
586
 
@@ -587,9 +605,13 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
587
605
  int ready = IO_Event_Selector_queue_flush(&data->backend);
588
606
 
589
607
  int result = select_process_completions(&data->ring);
590
-
591
- // If the ready list was empty and we didn't process any completions:
592
- if (!ready && result == 0) {
608
+
609
+ // If we:
610
+ // 1. Didn't process any ready fibers, and
611
+ // 2. Didn't process any events from non-blocking select (above), and
612
+ // 3. There are no items in the ready list,
613
+ // then we can perform a blocking select.
614
+ if (!ready && !result && !data->backend.ready) {
593
615
  // We might need to wait for events:
594
616
  struct select_arguments arguments = {
595
617
  .data = data,
@@ -605,7 +627,7 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
605
627
  // The timeout specified required "nonblocking" behaviour so we just flush the SQ if required:
606
628
  io_uring_submit_flush(data);
607
629
  }
608
-
630
+
609
631
  // After waiting/flushing the SQ, check if there are any completions:
610
632
  result = select_process_completions(&data->ring);
611
633
  }
@@ -613,6 +635,33 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
613
635
  return RB_INT2NUM(result);
614
636
  }
615
637
 
638
+ VALUE IO_Event_Selector_URing_wakeup(VALUE self) {
639
+ struct IO_Event_Selector_URing *data = NULL;
640
+ TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, data);
641
+
642
+ // If we are blocking, we can schedule a nop event to wake up the selector:
643
+ if (data->blocked) {
644
+ struct io_uring_sqe *sqe = NULL;
645
+
646
+ while (true) {
647
+ sqe = io_uring_get_sqe(&data->ring);
648
+ if (sqe) break;
649
+
650
+ rb_thread_schedule();
651
+
652
+ // It's possible we became unblocked already, so we can assume the selector has already cycled at least once:
653
+ if (!data->blocked) return Qfalse;
654
+ }
655
+
656
+ io_uring_prep_nop(sqe);
657
+ io_uring_submit(&data->ring);
658
+
659
+ return Qtrue;
660
+ }
661
+
662
+ return Qfalse;
663
+ }
664
+
616
665
  void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
617
666
  IO_Event_Selector_URing = rb_define_class_under(IO_Event_Selector, "URing", rb_cObject);
618
667
  rb_gc_register_mark_object(IO_Event_Selector_URing);
@@ -620,6 +669,8 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
620
669
  rb_define_alloc_func(IO_Event_Selector_URing, IO_Event_Selector_URing_allocate);
621
670
  rb_define_method(IO_Event_Selector_URing, "initialize", IO_Event_Selector_URing_initialize, 1);
622
671
 
672
+ rb_define_method(IO_Event_Selector_URing, "loop", IO_Event_Selector_URing_loop, 0);
673
+
623
674
  rb_define_method(IO_Event_Selector_URing, "transfer", IO_Event_Selector_URing_transfer, 0);
624
675
  rb_define_method(IO_Event_Selector_URing, "resume", IO_Event_Selector_URing_resume, -1);
625
676
  rb_define_method(IO_Event_Selector_URing, "yield", IO_Event_Selector_URing_yield, 0);
@@ -629,6 +680,7 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
629
680
  rb_define_method(IO_Event_Selector_URing, "ready?", IO_Event_Selector_URing_ready_p, 0);
630
681
 
631
682
  rb_define_method(IO_Event_Selector_URing, "select", IO_Event_Selector_URing_select, 1);
683
+ rb_define_method(IO_Event_Selector_URing, "wakeup", IO_Event_Selector_URing_wakeup, 0);
632
684
  rb_define_method(IO_Event_Selector_URing, "close", IO_Event_Selector_URing_close, 0);
633
685
 
634
686
  rb_define_method(IO_Event_Selector_URing, "io_wait", IO_Event_Selector_URing_io_wait, 3);
@@ -25,4 +25,3 @@
25
25
  #define IO_EVENT_SELECTOR_URING
26
26
 
27
27
  void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector);
28
- VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration);
data/ext/kqueue.o CHANGED
Binary file
data/ext/mkmf.log CHANGED
@@ -113,6 +113,22 @@ checked program was:
113
113
 
114
114
  --------------------
115
115
 
116
+ have_header: checking for sys/eventfd.h... -------------------- no
117
+
118
+ "clang -E -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0/arm64-darwin21 -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0/ruby/backward -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0 -I. -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -O3 -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdivision-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wmissing-noreturn -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wextra-tokens -pipe -Wall conftest.c -o conftest.i"
119
+ conftest.c:3:10: fatal error: 'sys/eventfd.h' file not found
120
+ #include <sys/eventfd.h>
121
+ ^~~~~~~~~~~~~~~
122
+ 1 error generated.
123
+ checked program was:
124
+ /* begin */
125
+ 1: #include "ruby.h"
126
+ 2:
127
+ 3: #include <sys/eventfd.h>
128
+ /* end */
129
+
130
+ --------------------
131
+
116
132
  have_func: checking for rb_io_descriptor()... -------------------- no
117
133
 
118
134
  "clang -fdeclspec -o conftest -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0/arm64-darwin21 -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0/ruby/backward -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0 -I. -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -O3 -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdivision-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wmissing-noreturn -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wextra-tokens -pipe -Wall conftest.c -L. -L/Users/samuel/.rubies/ruby-3.0.3/lib -L/opt/local/lib -L. -fstack-protector-strong -L/opt/local/lib -m64 -lruby.3.0-static -framework Security -framework Foundation -lpthread -lgmp -ldl -lobjc "
@@ -141,7 +157,7 @@ checked program was:
141
157
  "clang -fdeclspec -o conftest -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0/arm64-darwin21 -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0/ruby/backward -I/Users/samuel/.rubies/ruby-3.0.3/include/ruby-3.0.0 -I. -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -O3 -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wdivision-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wmissing-noreturn -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wextra-tokens -pipe -Wall conftest.c -L. -L/Users/samuel/.rubies/ruby-3.0.3/lib -L/opt/local/lib -L. -fstack-protector-strong -L/opt/local/lib -m64 -lruby.3.0-static -framework Security -framework Foundation -lpthread -lgmp -ldl -lobjc "
142
158
  Undefined symbols for architecture arm64:
143
159
  "_rb_io_descriptor", referenced from:
144
- _t in conftest-3f9e69.o
160
+ _t in conftest-1e4d1a.o
145
161
  ld: symbol(s) not found for architecture arm64
146
162
  clang: error: linker command failed with exit code 1 (use -v to see invocation)
147
163
  checked program was:
@@ -28,6 +28,14 @@ module IO::Event
28
28
  @readable = {}
29
29
  @writable = {}
30
30
  @priority = {}
31
+
32
+ unless Fiber.current == selector.loop
33
+ raise "Selector must be initialized on event loop fiber!"
34
+ end
35
+ end
36
+
37
+ def wakeup
38
+ @selector.wakeup
31
39
  end
32
40
 
33
41
  def close
@@ -86,13 +94,17 @@ module IO::Event
86
94
  end
87
95
 
88
96
  def select(duration = nil)
97
+ unless Fiber.current == @selector.loop
98
+ raise "Selector must be run on event loop fiber!"
99
+ end
100
+
89
101
  @selector.select(duration)
90
102
  end
91
103
 
92
104
  private
93
105
 
94
106
  def register_readable(fiber, io, events)
95
- if (events & READABLE) > 0
107
+ if (events & IO::READABLE) > 0
96
108
  if @readable.key?(io)
97
109
  raise "Cannot wait for #{io} to become readable from multiple fibers."
98
110
  end
@@ -110,7 +122,7 @@ module IO::Event
110
122
  end
111
123
 
112
124
  def register_writable(fiber, io, events)
113
- if (events & WRITABLE) > 0
125
+ if (events & IO::WRITABLE) > 0
114
126
  if @writable.key?(io)
115
127
  raise "Cannot wait for #{io} to become writable from multiple fibers."
116
128
  end
@@ -128,7 +140,7 @@ module IO::Event
128
140
  end
129
141
 
130
142
  def register_priority(fiber, io, events)
131
- if (events & PRIORITY) > 0
143
+ if (events & IO::PRIORITY) > 0
132
144
  if @priority.key?(io)
133
145
  raise "Cannot wait for #{io} to become priority from multiple fibers."
134
146
  end
@@ -0,0 +1,57 @@
1
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module IO::Event
22
+ # A thread safe synchronisation primative.
23
+ class Interrupt
24
+ def self.attach(selector)
25
+ self.new(selector)
26
+ end
27
+
28
+ def initialize(selector)
29
+ @selector = selector
30
+ @input, @output = ::IO.pipe
31
+
32
+ @fiber = Fiber.new do
33
+ while true
34
+ if @selector.io_wait(@fiber, @input, IO::READABLE)
35
+ @input.read_nonblock(1)
36
+ end
37
+ end
38
+ end
39
+
40
+ @fiber.transfer
41
+ end
42
+
43
+ # Send a sigle byte interrupt.
44
+ def signal
45
+ @output.write('.')
46
+ @output.flush
47
+ end
48
+
49
+ def close
50
+ @input.close
51
+ @output.close
52
+ # @fiber.raise(::Interrupt)
53
+ end
54
+ end
55
+
56
+ private_constant :Interrupt
57
+ end
@@ -18,19 +18,39 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
+ require_relative '../interrupt'
22
+
21
23
  module IO::Event
22
24
  module Selector
23
25
  class Select
24
26
  def initialize(loop)
25
27
  @loop = loop
26
28
 
27
- @readable = {}
28
- @writable = {}
29
+ @readable = Hash.new.compare_by_identity
30
+ @writable = Hash.new.compare_by_identity
31
+
32
+ @blocked = false
29
33
 
30
34
  @ready = Queue.new
35
+ @interrupt = Interrupt.attach(self)
36
+ end
37
+
38
+ attr :loop
39
+
40
+ # If the event loop is currently blocked,
41
+ def wakeup
42
+ if @blocked
43
+ @interrupt.signal
44
+
45
+ return true
46
+ end
47
+
48
+ return false
31
49
  end
32
50
 
33
51
  def close
52
+ @interrupt.close
53
+
34
54
  @loop = nil
35
55
  @readable = nil
36
56
  @writable = nil
@@ -97,12 +117,12 @@ module IO::Event
97
117
  def io_wait(fiber, io, events)
98
118
  remove_readable = remove_writable = false
99
119
 
100
- if (events & READABLE) > 0 or (events & PRIORITY) > 0
120
+ if (events & IO::READABLE) > 0 or (events & IO::PRIORITY) > 0
101
121
  @readable[io] = fiber
102
122
  remove_readable = true
103
123
  end
104
124
 
105
- if (events & WRITABLE) > 0
125
+ if (events & IO::WRITABLE) > 0
106
126
  @writable[io] = fiber
107
127
  remove_writable = true
108
128
  end
@@ -123,9 +143,9 @@ module IO::Event
123
143
 
124
144
  case result = io.read_nonblock(maximum_size, exception: false)
125
145
  when :wait_readable
126
- self.io_wait(fiber, io, READABLE)
146
+ self.io_wait(fiber, io, IO::READABLE)
127
147
  when :wait_writable
128
- self.io_wait(fiber, io, WRITABLE)
148
+ self.io_wait(fiber, io, IO::WRITABLE)
129
149
  else
130
150
  break unless result
131
151
 
@@ -147,9 +167,9 @@ module IO::Event
147
167
  chunk = buffer.to_str(offset, length)
148
168
  case result = io.write_nonblock(chunk, exception: false)
149
169
  when :wait_readable
150
- self.io_wait(fiber, io, READABLE)
170
+ self.io_wait(fiber, io, IO::READABLE)
151
171
  when :wait_writable
152
- self.io_wait(fiber, io, WRITABLE)
172
+ self.io_wait(fiber, io, IO::WRITABLE)
153
173
  else
154
174
  offset += result
155
175
  length -= result
@@ -169,7 +189,7 @@ module IO::Event
169
189
  w.close
170
190
  end
171
191
 
172
- self.io_wait(fiber, r, READABLE)
192
+ self.io_wait(fiber, r, IO::READABLE)
173
193
 
174
194
  return thread.value
175
195
  ensure
@@ -193,26 +213,32 @@ module IO::Event
193
213
 
194
214
  def select(duration = nil)
195
215
  if pop_ready
216
+ # If we have popped items from the ready list, they may influence the duration calculation, so we don't delay the event loop:
196
217
  duration = 0
197
218
  end
198
219
 
220
+ @blocked = true
221
+ duration = 0 unless @ready.empty?
199
222
  readable, writable, _ = ::IO.select(@readable.keys, @writable.keys, nil, duration)
223
+ @blocked = false
200
224
 
201
225
  ready = Hash.new(0)
202
226
 
203
227
  readable&.each do |io|
204
228
  fiber = @readable.delete(io)
205
- ready[fiber] |= READABLE
229
+ ready[fiber] |= IO::READABLE
206
230
  end
207
231
 
208
232
  writable&.each do |io|
209
233
  fiber = @writable.delete(io)
210
- ready[fiber] |= WRITABLE
234
+ ready[fiber] |= IO::WRITABLE
211
235
  end
212
236
 
213
237
  ready.each do |fiber, events|
214
238
  fiber.transfer(events) if fiber.alive?
215
239
  end
240
+
241
+ return ready.size
216
242
  end
217
243
  end
218
244
  end
@@ -21,11 +21,6 @@
21
21
  require_relative 'selector/select'
22
22
 
23
23
  module IO::Event
24
- # These constants are the same as those defined in IO.
25
- READABLE = 1
26
- PRIORITY = 2
27
- WRITABLE = 4
28
-
29
24
  module Selector
30
25
  def self.default(env = ENV)
31
26
  if name = env['IO_EVENT_SELECTOR']&.to_sym
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module IO::Event
22
- VERSION = "0.1.0"
22
+ VERSION = "0.2.3"
23
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io-event
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-06 00:00:00.000000000 Z
11
+ date: 2021-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bake
@@ -53,33 +53,19 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rspec
56
+ name: sus
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.0'
61
+ version: '0.5'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '3.0'
69
- - !ruby/object:Gem::Dependency
70
- name: timers
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
68
+ version: '0.5'
83
69
  description:
84
70
  email:
85
71
  executables: []
@@ -92,8 +78,11 @@ files:
92
78
  - ext/event.o
93
79
  - ext/extconf.h
94
80
  - ext/extconf.rb
81
+ - ext/interrupt.o
95
82
  - ext/io/event/event.c
96
83
  - ext/io/event/event.h
84
+ - ext/io/event/interrupt.c
85
+ - ext/io/event/interrupt.h
97
86
  - ext/io/event/selector/epoll.c
98
87
  - ext/io/event/selector/epoll.h
99
88
  - ext/io/event/selector/kqueue.c
@@ -108,6 +97,7 @@ files:
108
97
  - ext/selector.o
109
98
  - lib/io/event.rb
110
99
  - lib/io/event/debug/selector.rb
100
+ - lib/io/event/interrupt.rb
111
101
  - lib/io/event/selector.rb
112
102
  - lib/io/event/selector/select.rb
113
103
  - lib/io/event/version.rb