io-event 0.1.0 → 0.2.0

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: 9138d171a26780a13bfb5e983488d743de8e891112e976d02f10a73fedace606
4
+ data.tar.gz: 0e4c44a7f8f1379fc8de5dd3fa8ffde0bd4f6bffc1bdb25a0fb7cfb0dfd45bc7
5
5
  SHA512:
6
- metadata.gz: e9f333103f29c2e2a69f1573fefb11c01a23d2cf9cc56e30343e9e9f9ec6f5bd5f44408a95cbdd84bf9350b7eb7e3d2ea57df92702a97407e49ab7a6006b9506
7
- data.tar.gz: a35949d9fb40a0361b2627f3d998dfae230474a9fcb51f6361c0d3686560cc3d22749147e9a581dfc8f429ffd35b80d920a780400d66398e4de9c113d4122f8e
6
+ metadata.gz: 733f4341efdf63775b2faac16609a3f8574a2d17a77db919375afd04bbaa0730acca6fd6dc9849a4aa4177fbe8b9d2855afabbf64e5e31e8ce09ae8f0f6e46bc
7
+ data.tar.gz: a207ab4ef7a2097189f2e59fe4838a5aa877856a0b3ac9732b19340ffbd8ef73460aca159794dfaa3e488b9436fb614fccabd94151290a02e70e6f43281a754f
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,84 @@
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 <fcntl.h>
25
+ #include <unistd.h>
26
+
27
+ #ifdef HAVE_SYS_EVENTFD_H
28
+ #include <sys/eventfd.h>
29
+ #endif
30
+
31
+ #include "selector/selector.h"
32
+
33
+ #ifdef HAVE_SYS_EVENTFD_H
34
+ void IO_Event_Interrupt_open(struct IO_Event_Interrupt *interrupt)
35
+ {
36
+ interrupt->descriptor = eventfd2(0, EFD_CLOEXEC | EFD_NONBLOCK);
37
+ rb_update_max_fd(interrupt->descriptor);
38
+ }
39
+
40
+ void IO_Event_Interrupt_close(struct IO_Event_Interrupt *interrupt)
41
+ {
42
+ close(interrupt->descriptor);
43
+ }
44
+
45
+ void IO_Event_Interrupt_signal(struct IO_Event_Interrupt *interrupt)
46
+ {
47
+ uint64_t value = 1;
48
+ write(interrupt->descriptor, &value, sizeof(value));
49
+ }
50
+
51
+ void IO_Event_Interrupt_clear(struct IO_Event_Interrupt *interrupt)
52
+ {
53
+ uint64_t value = 0;
54
+ read(interrupt->descriptor, &value, sizeof(value));
55
+ }
56
+ #else
57
+ void IO_Event_Interrupt_open(struct IO_Event_Interrupt *interrupt)
58
+ {
59
+ #ifdef __linux__
60
+ pipe2(interrupt->descriptor, O_CLOEXEC | O_NONBLOCK);
61
+ #else
62
+ pipe(interrupt->descriptor);
63
+ IO_Event_Selector_nonblock_set(interrupt->descriptor[0]);
64
+ IO_Event_Selector_nonblock_set(interrupt->descriptor[1]);
65
+ #endif
66
+ }
67
+
68
+ void IO_Event_Interrupt_close(struct IO_Event_Interrupt *interrupt)
69
+ {
70
+ close(interrupt->descriptor[0]);
71
+ close(interrupt->descriptor[1]);
72
+ }
73
+
74
+ void IO_Event_Interrupt_signal(struct IO_Event_Interrupt *interrupt)
75
+ {
76
+ write(interrupt->descriptor[1], ".", 1);
77
+ }
78
+
79
+ void IO_Event_Interrupt_clear(struct IO_Event_Interrupt *interrupt)
80
+ {
81
+ char buffer[128];
82
+ read(interrupt->descriptor[0], buffer, sizeof(buffer));
83
+ }
84
+ #endif
@@ -0,0 +1,43 @@
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
+ #ifdef HAVE_SYS_EVENTFD_H
22
+ struct IO_Event_Interrupt {
23
+ int descriptor;
24
+ };
25
+
26
+ inline int IO_Event_Interrupt_descriptor(struct IO_Event_Interrupt *interrupt) {
27
+ return interrupt->descriptor;
28
+ }
29
+ #else
30
+ struct IO_Event_Interrupt {
31
+ int descriptor[2];
32
+ };
33
+
34
+ inline int IO_Event_Interrupt_descriptor(struct IO_Event_Interrupt *interrupt) {
35
+ return interrupt->descriptor[0];
36
+ }
37
+ #endif
38
+
39
+ void IO_Event_Interrupt_open(struct IO_Event_Interrupt *interrupt);
40
+ void IO_Event_Interrupt_close(struct IO_Event_Interrupt *interrupt);
41
+
42
+ void IO_Event_Interrupt_signal(struct IO_Event_Interrupt *interrupt);
43
+ void IO_Event_Interrupt_clear(struct IO_Event_Interrupt *interrupt);
@@ -26,6 +26,7 @@
26
26
  #include <errno.h>
27
27
 
28
28
  #include "pidfd.c"
29
+ #include "../interrupt.h"
29
30
 
30
31
  static VALUE IO_Event_Selector_EPoll = Qnil;
31
32
 
@@ -34,6 +35,8 @@ enum {EPOLL_MAX_EVENTS = 64};
34
35
  struct IO_Event_Selector_EPoll {
35
36
  struct IO_Event_Selector backend;
36
37
  int descriptor;
38
+ int blocked;
39
+ IO_Event_Interrupt interrupt;
37
40
  };
38
41
 
39
42
  void IO_Event_Selector_EPoll_Type_mark(void *_data)
@@ -47,6 +50,8 @@ void close_internal(struct IO_Event_Selector_EPoll *data) {
47
50
  if (data->descriptor >= 0) {
48
51
  close(data->descriptor);
49
52
  data->descriptor = -1;
53
+
54
+ IO_Event_Interrupt_close(&data->interrupt);
50
55
  }
51
56
  }
52
57
 
@@ -85,6 +90,21 @@ VALUE IO_Event_Selector_EPoll_allocate(VALUE self) {
85
90
  return instance;
86
91
  }
87
92
 
93
+ void IO_Event_Interrupt_add(IO_Event_Interrupt *interrupt, struct IO_Event_Selector_EPoll *data) {
94
+ int descriptor = IO_Event_Interrupt_descriptor(interrupt);
95
+
96
+ struct epoll_event event = {
97
+ .events = EPOLLIN|EPOLLRDHUP,
98
+ .data = {.ptr = NULL, .fd},
99
+ };
100
+
101
+ int result = epoll_ctl(data->descriptor, EPOLL_CTL_ADD, descriptor, &event);
102
+
103
+ if (result == -1) {
104
+ rb_sys_fail("IO_Event_Interrupt_addL:epoll_ctl");
105
+ }
106
+ }
107
+
88
108
  VALUE IO_Event_Selector_EPoll_initialize(VALUE self, VALUE loop) {
89
109
  struct IO_Event_Selector_EPoll *data = NULL;
90
110
  TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, data);
@@ -100,9 +120,19 @@ VALUE IO_Event_Selector_EPoll_initialize(VALUE self, VALUE loop) {
100
120
  rb_update_max_fd(data->descriptor);
101
121
  }
102
122
 
123
+ IO_Event_Interrupt_open(&data->interrupt);
124
+ IO_Event_Interrupt_add(&data->interrupt, data);
125
+
103
126
  return self;
104
127
  }
105
128
 
129
+ VALUE IO_Event_Selector_EPoll_loop(VALUE self) {
130
+ struct IO_Event_Selector_EPoll *data = NULL;
131
+ TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, data);
132
+
133
+ return data->backend.loop;
134
+ }
135
+
106
136
  VALUE IO_Event_Selector_EPoll_close(VALUE self) {
107
137
  struct IO_Event_Selector_EPoll *data = NULL;
108
138
  TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, data);
@@ -498,7 +528,9 @@ void * select_internal(void *_arguments) {
498
528
 
499
529
  static
500
530
  void select_internal_without_gvl(struct select_arguments *arguments) {
531
+ arguments->data->blocked = 1;
501
532
  rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
533
+ arguments->data->blocked = 0;
502
534
 
503
535
  if (arguments->count == -1) {
504
536
  rb_sys_fail("select_internal_without_gvl:epoll_wait");
@@ -540,6 +572,10 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
540
572
  VALUE fiber = (VALUE)arguments.events[i].data.ptr;
541
573
  VALUE result = INT2NUM(arguments.events[i].events);
542
574
 
575
+ if (arguments.events[i].data.fd == IO_Event_Interrupt_descriptor(&data->interrupt)) {
576
+ IO_Event_Interrupt_clear(&data->interrupt);
577
+ }
578
+
543
579
  // fprintf(stderr, "-> fiber=%p descriptor=%d\n", (void*)fiber, events[i].data.fd);
544
580
 
545
581
  IO_Event_Selector_fiber_transfer(fiber, 1, &result);
@@ -548,6 +584,20 @@ VALUE IO_Event_Selector_EPoll_select(VALUE self, VALUE duration) {
548
584
  return INT2NUM(arguments.count);
549
585
  }
550
586
 
587
+ VALUE IO_Event_Selector_URing_wakeup(VALUE self) {
588
+ struct IO_Event_Selector_EPoll *data = NULL;
589
+ TypedData_Get_Struct(self, struct IO_Event_Selector_EPoll, &IO_Event_Selector_EPoll_Type, data);
590
+
591
+ // If we are blocking, we can schedule a nop event to wake up the selector:
592
+ if (data->blocked) {
593
+ IO_Event_Interrupt_signal(data->interrupt);
594
+
595
+ return Qtrue;
596
+ }
597
+
598
+ return Qfalse;
599
+ }
600
+
551
601
  void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
552
602
  IO_Event_Selector_EPoll = rb_define_class_under(IO_Event_Selector, "EPoll", rb_cObject);
553
603
  rb_gc_register_mark_object(IO_Event_Selector_EPoll);
@@ -555,6 +605,8 @@ void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
555
605
  rb_define_alloc_func(IO_Event_Selector_EPoll, IO_Event_Selector_EPoll_allocate);
556
606
  rb_define_method(IO_Event_Selector_EPoll, "initialize", IO_Event_Selector_EPoll_initialize, 1);
557
607
 
608
+ rb_define_method(IO_Event_Selector_EPoll, "loop", IO_Event_Selector_EPoll_loop, 0);
609
+
558
610
  rb_define_method(IO_Event_Selector_EPoll, "transfer", IO_Event_Selector_EPoll_transfer, 0);
559
611
  rb_define_method(IO_Event_Selector_EPoll, "resume", IO_Event_Selector_EPoll_resume, -1);
560
612
  rb_define_method(IO_Event_Selector_EPoll, "yield", IO_Event_Selector_EPoll_yield, 0);
@@ -564,6 +616,7 @@ void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
564
616
  rb_define_method(IO_Event_Selector_EPoll, "ready?", IO_Event_Selector_EPoll_ready_p, 0);
565
617
 
566
618
  rb_define_method(IO_Event_Selector_EPoll, "select", IO_Event_Selector_EPoll_select, 1);
619
+ rb_define_method(IO_Event_Selector_EPoll, "wakeup", IO_Event_Selector_EPoll_wakeup, 0);
567
620
  rb_define_method(IO_Event_Selector_EPoll, "close", IO_Event_Selector_EPoll_close, 0);
568
621
 
569
622
  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,9 @@ 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;
564
575
  rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
576
+ arguments->data->blocked = 0;
565
577
 
566
578
  if (arguments->count == -1) {
567
579
  rb_sys_fail("select_internal_without_gvl:kevent");
@@ -614,15 +626,40 @@ VALUE IO_Event_Selector_KQueue_select(VALUE self, VALUE duration) {
614
626
  }
615
627
 
616
628
  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);
629
+ if (arguments.events[i].udata) {
630
+ VALUE fiber = (VALUE)arguments.events[i].udata;
631
+ VALUE result = INT2NUM(arguments.events[i].filter);
632
+
633
+ IO_Event_Selector_fiber_transfer(fiber, 1, &result);
634
+ }
621
635
  }
622
636
 
623
637
  return INT2NUM(arguments.count);
624
638
  }
625
639
 
640
+ VALUE IO_Event_Selector_KQueue_wakeup(VALUE self) {
641
+ struct IO_Event_Selector_KQueue *data = NULL;
642
+ TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, data);
643
+
644
+ if (data->blocked) {
645
+ struct kevent trigger = {0};
646
+
647
+ trigger.filter = EVFILT_USER;
648
+ trigger.flags = EV_ADD|EV_CLEAR;
649
+ trigger.fflags = NOTE_TRIGGER;
650
+
651
+ int result = kevent(data->descriptor, &trigger, 1, NULL, 0, NULL);
652
+
653
+ if (result == -1) {
654
+ rb_sys_fail("IO_Event_Selector_KQueue_wakeup:kevent");
655
+ }
656
+
657
+ return Qtrue;
658
+ }
659
+
660
+ return Qfalse;
661
+ }
662
+
626
663
  void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
627
664
  IO_Event_Selector_KQueue = rb_define_class_under(IO_Event_Selector, "KQueue", rb_cObject);
628
665
  rb_gc_register_mark_object(IO_Event_Selector_KQueue);
@@ -630,6 +667,8 @@ void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
630
667
  rb_define_alloc_func(IO_Event_Selector_KQueue, IO_Event_Selector_KQueue_allocate);
631
668
  rb_define_method(IO_Event_Selector_KQueue, "initialize", IO_Event_Selector_KQueue_initialize, 1);
632
669
 
670
+ rb_define_method(IO_Event_Selector_KQueue, "loop", IO_Event_Selector_KQueue_loop, 0);
671
+
633
672
  rb_define_method(IO_Event_Selector_KQueue, "transfer", IO_Event_Selector_KQueue_transfer, 0);
634
673
  rb_define_method(IO_Event_Selector_KQueue, "resume", IO_Event_Selector_KQueue_resume, -1);
635
674
  rb_define_method(IO_Event_Selector_KQueue, "yield", IO_Event_Selector_KQueue_yield, 0);
@@ -639,6 +678,7 @@ void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
639
678
  rb_define_method(IO_Event_Selector_KQueue, "ready?", IO_Event_Selector_KQueue_ready_p, 0);
640
679
 
641
680
  rb_define_method(IO_Event_Selector_KQueue, "select", IO_Event_Selector_KQueue_select, 1);
681
+ rb_define_method(IO_Event_Selector_KQueue, "wakeup", IO_Event_Selector_KQueue_wakeup, 0);
642
682
  rb_define_method(IO_Event_Selector_KQueue, "close", IO_Event_Selector_KQueue_close, 0);
643
683
 
644
684
  rb_define_method(IO_Event_Selector_KQueue, "io_wait", IO_Event_Selector_KQueue_io_wait, 3);
@@ -26,6 +26,7 @@
26
26
  #include <time.h>
27
27
 
28
28
  #include "pidfd.c"
29
+ #include "../interrupt.h"
29
30
 
30
31
  static const int DEBUG = 0;
31
32
 
@@ -37,6 +38,7 @@ struct IO_Event_Selector_URing {
37
38
  struct IO_Event_Selector backend;
38
39
  struct io_uring ring;
39
40
  size_t pending;
41
+ int blocked;
40
42
  };
41
43
 
42
44
  void IO_Event_Selector_URing_Type_mark(void *_data)
@@ -86,7 +88,8 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
86
88
  data->ring.ring_fd = -1;
87
89
 
88
90
  data->pending = 0;
89
-
91
+ data->blocked = 0;
92
+
90
93
  return instance;
91
94
  }
92
95
 
@@ -106,6 +109,13 @@ VALUE IO_Event_Selector_URing_initialize(VALUE self, VALUE loop) {
106
109
  return self;
107
110
  }
108
111
 
112
+ VALUE IO_Event_Selector_URing_loop(VALUE self) {
113
+ struct IO_Event_Selector_URing *data = NULL;
114
+ TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, data);
115
+
116
+ return data->backend.loop;
117
+ }
118
+
109
119
  VALUE IO_Event_Selector_URing_close(VALUE self) {
110
120
  struct IO_Event_Selector_URing *data = NULL;
111
121
  TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, data);
@@ -156,7 +166,8 @@ VALUE IO_Event_Selector_URing_raise(int argc, VALUE *argv, VALUE self)
156
166
 
157
167
  return IO_Event_Selector_raise(&data->backend, argc, argv);
158
168
  }
159
-
169
+
170
+ int blocked;
160
171
  VALUE IO_Event_Selector_URing_ready_p(VALUE self) {
161
172
  struct IO_Event_Selector_URing *data = NULL;
162
173
  TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, data);
@@ -171,14 +182,14 @@ int io_uring_submit_flush(struct IO_Event_Selector_URing *data) {
171
182
 
172
183
  // Try to submit:
173
184
  int result = io_uring_submit(&data->ring);
174
-
185
+
175
186
  if (result >= 0) {
176
187
  // If it was submitted, reset pending count:
177
188
  data->pending = 0;
178
189
  } else if (result != -EBUSY && result != -EAGAIN) {
179
190
  rb_syserr_fail(-result, "io_uring_submit_flush");
180
191
  }
181
-
192
+
182
193
  return result;
183
194
  }
184
195
 
@@ -194,7 +205,7 @@ int io_uring_submit_now(struct IO_Event_Selector_URing *data) {
194
205
  data->pending = 0;
195
206
  return result;
196
207
  }
197
-
208
+
198
209
  if (result == -EBUSY || result == -EAGAIN) {
199
210
  IO_Event_Selector_yield(&data->backend);
200
211
  } else {
@@ -214,7 +225,7 @@ struct io_uring_sqe * io_get_sqe(struct IO_Event_Selector_URing *data) {
214
225
  while (sqe == NULL) {
215
226
  // The submit queue is full, we need to drain it:
216
227
  io_uring_submit_now(data);
217
-
228
+
218
229
  sqe = io_uring_get_sqe(&data->ring);
219
230
  }
220
231
 
@@ -523,10 +534,8 @@ struct select_arguments {
523
534
  static
524
535
  void * select_internal(void *_arguments) {
525
536
  struct select_arguments * arguments = (struct select_arguments *)_arguments;
526
-
527
- io_uring_submit_flush(arguments->data);
528
-
529
537
  struct io_uring_cqe *cqe = NULL;
538
+
530
539
  arguments->result = io_uring_wait_cqe_timeout(&arguments->data->ring, &cqe, arguments->timeout);
531
540
 
532
541
  return NULL;
@@ -534,7 +543,11 @@ void * select_internal(void *_arguments) {
534
543
 
535
544
  static
536
545
  int select_internal_without_gvl(struct select_arguments *arguments) {
546
+ io_uring_submit_flush(arguments->data);
547
+
548
+ arguments->data->blocked = 1;
537
549
  rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
550
+ arguments->data->blocked = 0;
538
551
 
539
552
  if (arguments->result == -ETIME) {
540
553
  arguments->result = 0;
@@ -548,6 +561,8 @@ int select_internal_without_gvl(struct select_arguments *arguments) {
548
561
  return arguments->result;
549
562
  }
550
563
 
564
+ #define IO_EVENT_SELECTOR_URING_UDATA_INTERRUPT ((__u64) -2)
565
+
551
566
  static inline
552
567
  unsigned select_process_completions(struct io_uring *ring) {
553
568
  unsigned completed = 0;
@@ -563,6 +578,11 @@ unsigned select_process_completions(struct io_uring *ring) {
563
578
  continue;
564
579
  }
565
580
 
581
+ if (cqe->user_data == IO_EVENT_SELECTOR_URING_UDATA_INTERRUPT) {
582
+ io_uring_cq_advance(ring, 1);
583
+ IO_Event_Interrupt_clear();
584
+ }
585
+
566
586
  VALUE fiber = (VALUE)cqe->user_data;
567
587
  VALUE result = RB_INT2NUM(cqe->res);
568
588
 
@@ -587,7 +607,7 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
587
607
  int ready = IO_Event_Selector_queue_flush(&data->backend);
588
608
 
589
609
  int result = select_process_completions(&data->ring);
590
-
610
+
591
611
  // If the ready list was empty and we didn't process any completions:
592
612
  if (!ready && result == 0) {
593
613
  // We might need to wait for events:
@@ -605,7 +625,7 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
605
625
  // The timeout specified required "nonblocking" behaviour so we just flush the SQ if required:
606
626
  io_uring_submit_flush(data);
607
627
  }
608
-
628
+
609
629
  // After waiting/flushing the SQ, check if there are any completions:
610
630
  result = select_process_completions(&data->ring);
611
631
  }
@@ -613,6 +633,33 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
613
633
  return RB_INT2NUM(result);
614
634
  }
615
635
 
636
+ VALUE IO_Event_Selector_URing_wakeup(VALUE self) {
637
+ struct IO_Event_Selector_KQueue *data = NULL;
638
+ TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, data);
639
+
640
+ // If we are blocking, we can schedule a nop event to wake up the selector:
641
+ if (data->blocked) {
642
+ struct io_uring_sqe *sqe = NULL;
643
+
644
+ while (true) {
645
+ sqe = io_uring_get_sqe(&data->ring);
646
+ if (sqe) break;
647
+
648
+ rb_thread_schedule();
649
+
650
+ // It's possible we became unblocked already, so we can assume the selector has already cycled at least once:
651
+ if (!data->blocked) return Qfalse;
652
+ }
653
+
654
+ io_uring_prep_nop(sqe);
655
+ io_uring_submit(&backend->ring);
656
+
657
+ return Qtrue;
658
+ }
659
+
660
+ return Qfalse;
661
+ }
662
+
616
663
  void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
617
664
  IO_Event_Selector_URing = rb_define_class_under(IO_Event_Selector, "URing", rb_cObject);
618
665
  rb_gc_register_mark_object(IO_Event_Selector_URing);
@@ -620,6 +667,8 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
620
667
  rb_define_alloc_func(IO_Event_Selector_URing, IO_Event_Selector_URing_allocate);
621
668
  rb_define_method(IO_Event_Selector_URing, "initialize", IO_Event_Selector_URing_initialize, 1);
622
669
 
670
+ rb_define_method(IO_Event_Selector_URing, "loop", IO_Event_Selector_URing_loop, 1);
671
+
623
672
  rb_define_method(IO_Event_Selector_URing, "transfer", IO_Event_Selector_URing_transfer, 0);
624
673
  rb_define_method(IO_Event_Selector_URing, "resume", IO_Event_Selector_URing_resume, -1);
625
674
  rb_define_method(IO_Event_Selector_URing, "yield", IO_Event_Selector_URing_yield, 0);
@@ -629,6 +678,7 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
629
678
  rb_define_method(IO_Event_Selector_URing, "ready?", IO_Event_Selector_URing_ready_p, 0);
630
679
 
631
680
  rb_define_method(IO_Event_Selector_URing, "select", IO_Event_Selector_URing_select, 1);
681
+ rb_define_method(IO_Event_Selector_URing, "wakeup", IO_Event_Selector_URing_wakeup, 0);
632
682
  rb_define_method(IO_Event_Selector_URing, "close", IO_Event_Selector_URing_close, 0);
633
683
 
634
684
  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-3b24c5.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,6 +94,10 @@ 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
 
@@ -0,0 +1,59 @@
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
+ require_relative 'selector'
22
+
23
+ module IO::Event
24
+ # A thread safe synchronisation primative.
25
+ class Interrupt
26
+ def self.attach(selector)
27
+ self.new(selector)
28
+ end
29
+
30
+ def initialize(selector)
31
+ @selector = selector
32
+ @input, @output = ::IO.pipe
33
+
34
+ @fiber = Fiber.new do
35
+ while true
36
+ if @selector.io_wait(@fiber, @input, READABLE)
37
+ @input.read_nonblock(1)
38
+ end
39
+ end
40
+ end
41
+
42
+ @fiber.transfer
43
+ end
44
+
45
+ # Send a sigle byte interrupt.
46
+ def signal
47
+ @output.write('.')
48
+ @output.flush
49
+ end
50
+
51
+ def close
52
+ @input.close
53
+ @output.close
54
+ # @fiber.raise(::Interrupt)
55
+ end
56
+ end
57
+
58
+ private_constant :Interrupt
59
+ end
@@ -18,19 +18,34 @@
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
+ def wakeup
41
+ if @blocked
42
+ @interrupt.signal
43
+ end
31
44
  end
32
45
 
33
46
  def close
47
+ @interrupt.close
48
+
34
49
  @loop = nil
35
50
  @readable = nil
36
51
  @writable = nil
@@ -193,10 +208,14 @@ module IO::Event
193
208
 
194
209
  def select(duration = nil)
195
210
  if pop_ready
211
+ # If we have popped items from the ready list, they may influence the duration calculation, so we don't delay the event loop:
196
212
  duration = 0
197
213
  end
198
214
 
215
+ # The GVL ensures this is sufficiently synchronised for `#wakeup` to work correctly.
216
+ @blocked = true
199
217
  readable, writable, _ = ::IO.select(@readable.keys, @writable.keys, nil, duration)
218
+ @blocked = false
200
219
 
201
220
  ready = Hash.new(0)
202
221
 
@@ -213,6 +232,8 @@ module IO::Event
213
232
  ready.each do |fiber, events|
214
233
  fiber.transfer(events) if fiber.alive?
215
234
  end
235
+
236
+ return ready.size
216
237
  end
217
238
  end
218
239
  end
@@ -22,9 +22,9 @@ require_relative 'selector/select'
22
22
 
23
23
  module IO::Event
24
24
  # These constants are the same as those defined in IO.
25
- READABLE = 1
26
- PRIORITY = 2
27
- WRITABLE = 4
25
+ READABLE = IO::READABLE
26
+ PRIORITY = IO::PRIORITY
27
+ WRITABLE = IO::WRITABLE
28
28
 
29
29
  module Selector
30
30
  def self.default(env = ENV)
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module IO::Event
22
- VERSION = "0.1.0"
22
+ VERSION = "0.2.0"
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.0
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-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bake
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
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'
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