io-event 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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