event 0.1.2 → 0.2.1

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: 287fe2e9885bd34fd2595c49589c2cfe751799e34f738a18e93f9ccdf13f3ffd
4
- data.tar.gz: 88cec8cd2badba810501a9a3aef7309dcb76d87ef0c6029bca147ba2d3eea09e
3
+ metadata.gz: 820b9a8295d89939388b1fa56c818addaad4e46a3af90eb0318a6c4d4338b729
4
+ data.tar.gz: a925d6a51efeacb281c68cdb51840fe81a1c97aa22297c684f7a441d9ee2afc3
5
5
  SHA512:
6
- metadata.gz: 956801d0c052c06efe6cf126e76472a02f2271d8476a0501f262ab1d71b0e97992fc5bf2ffce33b753f510179ec42f8f5529152d9bd48ed6029b4009ba754d3c
7
- data.tar.gz: 3f35bf37920170794733e5b3671aa9e0e1a3a64620dde2209310d5c8143866ce92289c5efe6b9f2010f271b2452b3efdb9bbb3d2c3c72d80cefd847154746355
6
+ metadata.gz: a591c0f50e2f1fa19d895eea4a94141b6d6ed2f359325f61400f67c35a51e1f89ff4a8806ae764042f0e747612a0c291554bed423c83ae9ec58afbc24bf20c3b
7
+ data.tar.gz: e32064f234f542aee8bff87e19e1c3647c08e054c962d4943674e7a17167860f5898bc76b1c134494fea1a46a8f2a40da0b30d5516470950eecd02df470893c8
@@ -0,0 +1,27 @@
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
+ enum Event {
22
+ READABLE = 1,
23
+ PRIORITY = 2,
24
+ WRITABLE = 4,
25
+ ERROR = 8,
26
+ HANGUP = 16
27
+ };
@@ -19,6 +19,7 @@
19
19
  // THE SOFTWARE.
20
20
 
21
21
  #include "kqueue.h"
22
+ #include "backend.h"
22
23
 
23
24
  #include <sys/epoll.h>
24
25
  #include <time.h>
@@ -27,8 +28,6 @@
27
28
  static VALUE Event_Backend_EPoll = Qnil;
28
29
  static ID id_fileno, id_transfer;
29
30
 
30
- static const int READABLE = 1, PRIORITY = 2, WRITABLE = 4;
31
-
32
31
  static const unsigned EPOLL_MAX_EVENTS = 1024;
33
32
 
34
33
  struct Event_Backend_EPoll {
@@ -97,32 +96,66 @@ VALUE Event_Backend_EPoll_initialize(VALUE self, VALUE loop) {
97
96
  return self;
98
97
  }
99
98
 
100
- VALUE Event_Backend_EPoll_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE events) {
101
- struct Event_Backend_EPoll *data = NULL;
102
- TypedData_Get_Struct(self, struct Event_Backend_EPoll, &Event_Backend_EPoll_Type, data);
99
+ static inline
100
+ uint32_t epoll_flags_from_events(int events) {
101
+ uint32_t flags = 0;
103
102
 
104
- struct epoll_event event = {0};
103
+ if (events & READABLE) flags |= EPOLLIN;
104
+ if (events & PRIORITY) flags |= EPOLLPRI;
105
+ if (events & WRITABLE) flags |= EPOLLOUT;
105
106
 
106
- int descriptor = NUM2INT(rb_funcall(io, id_fileno, 0));
107
- int duplicate = -1;
107
+ flags |= EPOLLRDHUP;
108
+ flags |= EPOLLONESHOT;
109
+
110
+ return flags;
111
+ }
112
+
113
+ static inline
114
+ int events_from_epoll_flags(uint32_t flags) {
115
+ int events = 0;
108
116
 
109
- int mask = NUM2INT(events);
117
+ if (flags & EPOLLIN) events |= READABLE;
118
+ if (flags & EPOLLPRI) events |= PRIORITY;
119
+ if (flags & EPOLLOUT) events |= WRITABLE;
110
120
 
111
- if (mask & READABLE) {
112
- event.events |= EPOLLIN;
113
- }
121
+ return events;
122
+ }
123
+
124
+ struct io_wait_arguments {
125
+ struct Event_Backend_EPoll *data;
126
+ int duplicate;
127
+ };
128
+
129
+ static
130
+ VALUE io_wait_ensure(VALUE _arguments) {
131
+ struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
114
132
 
115
- if (mask & PRIORITY) {
116
- event.events |= EPOLLPRI;
133
+ if (arguments->duplicate >= 0) {
134
+ close(arguments->duplicate);
117
135
  }
118
136
 
119
- if (mask & WRITABLE) {
120
- event.events |= EPOLLOUT;
121
- }
137
+ return Qnil;
138
+ };
139
+
140
+ static
141
+ VALUE io_wait_transfer(VALUE _arguments) {
142
+ struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
143
+
144
+ VALUE result = rb_funcall(arguments->data->loop, id_transfer, 0);
145
+
146
+ return INT2NUM(events_from_epoll_flags(NUM2INT(result)));
147
+ };
148
+
149
+ VALUE Event_Backend_EPoll_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE events) {
150
+ struct Event_Backend_EPoll *data = NULL;
151
+ TypedData_Get_Struct(self, struct Event_Backend_EPoll, &Event_Backend_EPoll_Type, data);
152
+
153
+ struct epoll_event event = {0};
122
154
 
123
- event.events |= EPOLLRDHUP;
124
- event.events |= EPOLLONESHOT;
155
+ int descriptor = NUM2INT(rb_funcall(io, id_fileno, 0));
156
+ int duplicate = -1;
125
157
 
158
+ event.events = epoll_flags_from_events(NUM2INT(events));
126
159
  event.data.ptr = (void*)fiber;
127
160
 
128
161
  // A better approach is to batch all changes:
@@ -144,13 +177,12 @@ VALUE Event_Backend_EPoll_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE event
144
177
  rb_sys_fail("epoll_ctl");
145
178
  }
146
179
 
147
- rb_funcall(data->loop, id_transfer, 0);
180
+ struct io_wait_arguments io_wait_arguments = {
181
+ .data = data,
182
+ .duplicate = duplicate
183
+ };
148
184
 
149
- if (duplicate >= 0) {
150
- close(duplicate);
151
- }
152
-
153
- return Qnil;
185
+ return rb_ensure(io_wait_transfer, (VALUE)&io_wait_arguments, io_wait_ensure, (VALUE)&io_wait_arguments);
154
186
  }
155
187
 
156
188
  static
@@ -186,7 +218,9 @@ VALUE Event_Backend_EPoll_select(VALUE self, VALUE duration) {
186
218
 
187
219
  for (int i = 0; i < count; i += 1) {
188
220
  VALUE fiber = (VALUE)events[i].data.ptr;
189
- rb_funcall(fiber, id_transfer, 0);
221
+ VALUE result = INT2NUM(events[i].events);
222
+
223
+ rb_funcall(fiber, id_transfer, 1, result);
190
224
  }
191
225
 
192
226
  return INT2NUM(count);
@@ -19,6 +19,7 @@
19
19
  // THE SOFTWARE.
20
20
 
21
21
  #include "kqueue.h"
22
+ #include "backend.h"
22
23
 
23
24
  #include <sys/event.h>
24
25
  #include <sys/ioctl.h>
@@ -27,7 +28,12 @@
27
28
  static VALUE Event_Backend_KQueue = Qnil;
28
29
  static ID id_fileno, id_transfer;
29
30
 
30
- static const int READABLE = 1, PRIORITY = 2, WRITABLE = 4;
31
+ static const int
32
+ READABLE = 1,
33
+ PRIORITY = 2,
34
+ WRITABLE = 4,
35
+ ERROR = 8,
36
+ HANGUP = 16;
31
37
 
32
38
  static const unsigned KQUEUE_MAX_EVENTS = 1024;
33
39
 
@@ -98,30 +104,40 @@ VALUE Event_Backend_KQueue_initialize(VALUE self, VALUE loop) {
98
104
  return self;
99
105
  }
100
106
 
107
+ static inline
108
+ u_short kqueue_filter_from_events(int events) {
109
+ u_short filter = 0;
110
+
111
+ if (events & READABLE) filter |= EVFILT_READ;
112
+ if (events & PRIORITY) filter |= EV_OOBAND;
113
+ if (events & WRITABLE) filter |= EVFILT_WRITE;
114
+
115
+ return filter;
116
+ }
117
+
118
+ static inline
119
+ int events_from_kqueue_filter(u_short filter) {
120
+ int events = 0;
121
+
122
+ if (filter & EVFILT_READ) events |= READABLE;
123
+ if (filter & EV_OOBAND) events |= PRIORITY;
124
+ if (filter & EVFILT_WRITE) events |= WRITABLE;
125
+
126
+ return INT2NUM(events);
127
+ }
128
+
101
129
  VALUE Event_Backend_KQueue_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE events) {
102
130
  struct Event_Backend_KQueue *data = NULL;
103
131
  TypedData_Get_Struct(self, struct Event_Backend_KQueue, &Event_Backend_KQueue_Type, data);
104
132
 
105
- struct kevent event;
106
- u_short flags = 0;
133
+ struct kevent event = {0};
107
134
 
108
135
  int descriptor = NUM2INT(rb_funcall(io, id_fileno, 0));
109
136
 
110
- int mask = NUM2INT(events);
111
-
112
- if (mask & READABLE) {
113
- flags |= EVFILT_READ;
114
- }
115
-
116
- if (mask & PRIORITY) {
117
- flags |= EV_OOBAND;
118
- }
119
-
120
- if (mask & WRITABLE) {
121
- flags |= EVFILT_WRITE;
122
- }
123
-
124
- EV_SET(&event, descriptor, flags, EV_ADD|EV_ENABLE|EV_ONESHOT, 0, 0, (void*)fiber);
137
+ event.ident = descriptor;
138
+ event.filter = kqueue_filters_from_events(events);
139
+ event.flags = EV_ADD | EV_ENABLE | EV_ONESHOT;
140
+ event.udata = (void*)fiber;
125
141
 
126
142
  // A better approach is to batch all changes:
127
143
  int result = kevent(data->descriptor, &event, 1, NULL, 0, NULL);
@@ -130,9 +146,8 @@ VALUE Event_Backend_KQueue_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE even
130
146
  rb_sys_fail("kevent");
131
147
  }
132
148
 
133
- rb_funcall(data->loop, id_transfer, 0);
134
-
135
- return Qnil;
149
+ VALUE result = rb_funcall(data->loop, id_transfer, 0);
150
+ return INT2NUM(events_from_kqueue_filter(NUM2INT(result)));
136
151
  }
137
152
 
138
153
  static
@@ -176,7 +191,8 @@ VALUE Event_Backend_KQueue_select(VALUE self, VALUE duration) {
176
191
 
177
192
  for (int i = 0; i < count; i += 1) {
178
193
  VALUE fiber = (VALUE)events[i].udata;
179
- rb_funcall(fiber, id_transfer, 0);
194
+ VALUE result = INT2NUM(events[i].filter);
195
+ rb_funcall(fiber, id_transfer, 1, result);
180
196
  }
181
197
 
182
198
  return INT2NUM(count);
@@ -19,15 +19,15 @@
19
19
  // THE SOFTWARE.
20
20
 
21
21
  #include "uring.h"
22
+ #include "backend.h"
22
23
 
23
24
  #include <liburing.h>
25
+ #include <poll.h>
24
26
  #include <time.h>
25
27
 
26
28
  static VALUE Event_Backend_URing = Qnil;
27
29
  static ID id_fileno, id_transfer;
28
30
 
29
- static const int READABLE = 1, PRIORITY = 2, WRITABLE = 4;
30
-
31
31
  static const int URING_ENTRIES = 1024;
32
32
  static const int URING_MAX_EVENTS = 1024;
33
33
 
@@ -96,6 +96,31 @@ VALUE Event_Backend_URing_initialize(VALUE self, VALUE loop) {
96
96
  return self;
97
97
  }
98
98
 
99
+ static inline
100
+ short poll_flags_from_events(int events) {
101
+ short flags = 0;
102
+
103
+ if (events & READABLE) flags |= POLLIN;
104
+ if (events & PRIORITY) flags |= POLLPRI;
105
+ if (events & WRITABLE) flags |= POLLOUT;
106
+
107
+ flags |= POLLERR;
108
+ flags |= POLLHUP;
109
+
110
+ return flags;
111
+ }
112
+
113
+ static inline
114
+ int events_from_poll_flags(short flags) {
115
+ int events = 0;
116
+
117
+ if (flags & POLLIN) events |= READABLE;
118
+ if (flags & POLLPRI) events |= PRIORITY;
119
+ if (flags & POLLOUT) events |= WRITABLE;
120
+
121
+ return events;
122
+ }
123
+
99
124
  VALUE Event_Backend_URing_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE events) {
100
125
  struct Event_Backend_URing *data = NULL;
101
126
  TypedData_Get_Struct(self, struct Event_Backend_URing, &Event_Backend_URing_Type, data);
@@ -103,32 +128,21 @@ VALUE Event_Backend_URing_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE event
103
128
  int descriptor = NUM2INT(rb_funcall(io, id_fileno, 0));
104
129
  struct io_uring_sqe *sqe = io_uring_get_sqe(data->ring);
105
130
 
106
- int mask = NUM2INT(events);
107
- short flags = 0;
108
-
109
- if (mask & READABLE) {
110
- flags |= POLL_IN;
111
- }
112
-
113
- if (mask & PRIORITY) {
114
- flags |= POLL_PRI;
115
- }
131
+ short flags = poll_flags_from_events(NUM2INT(events));
116
132
 
117
- if (mask & WRITABLE) {
118
- flags |= POLL_OUT;
119
- }
120
-
121
133
  // fprintf(stderr, "poll_add(%p, %d, %d)\n", sqe, descriptor, flags);
122
-
134
+
123
135
  io_uring_prep_poll_add(sqe, descriptor, flags);
124
136
  io_uring_sqe_set_data(sqe, (void*)fiber);
125
137
  io_uring_submit(data->ring);
126
138
 
127
- // fprintf(stderr, "count = %d, errno = %d\n", count, errno);
128
-
129
- rb_funcall(data->loop, id_transfer, 0);
139
+ VALUE result = rb_funcall(data->loop, id_transfer, 0);
140
+
141
+ // We explicitly filter the resulting events based on the requested events.
142
+ // In some cases, poll will report events we didn't ask for.
143
+ flags &= NUM2INT(result);
130
144
 
131
- return Qnil;
145
+ return INT2NUM(events_from_poll_flags(flags));
132
146
  }
133
147
 
134
148
  static
@@ -196,7 +210,9 @@ VALUE Event_Backend_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffe
196
210
  io_uring_prep_readv(sqe, descriptor, iovecs, 1, 0);
197
211
  io_uring_sqe_set_data(sqe, (void*)fiber);
198
212
  io_uring_submit(data->ring);
199
-
213
+
214
+ // fprintf(stderr, "prep_readv(%p, %d, %ld)\n", sqe, descriptor, iovecs[0].iov_len);
215
+
200
216
  int result = NUM2INT(rb_funcall(data->loop, id_transfer, 0));
201
217
 
202
218
  if (result < 0) {
@@ -227,6 +243,8 @@ VALUE Event_Backend_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buff
227
243
  io_uring_sqe_set_data(sqe, (void*)fiber);
228
244
  io_uring_submit(data->ring);
229
245
 
246
+ // fprintf(stderr, "prep_writev(%p, %d, %ld)\n", sqe, descriptor, iovecs[0].iov_len);
247
+
230
248
  int result = NUM2INT(rb_funcall(data->loop, id_transfer, 0));
231
249
 
232
250
  if (result < 0) {
@@ -242,33 +260,37 @@ VALUE Event_Backend_URing_select(VALUE self, VALUE duration) {
242
260
 
243
261
  struct io_uring_cqe *cqes[URING_MAX_EVENTS];
244
262
  struct __kernel_timespec storage;
245
-
246
- if (duration != Qnil) {
247
- int result = io_uring_wait_cqe_timeout(data->ring, cqes, make_timeout(duration, &storage));
263
+
264
+ int result = io_uring_peek_batch_cqe(data->ring, cqes, URING_MAX_EVENTS);
265
+
266
+ // fprintf(stderr, "result = %d\n", result);
267
+
268
+ if (result < 0) {
269
+ rb_syserr_fail(-result, strerror(-result));
270
+ } else if (result == 0) {
271
+ result = io_uring_wait_cqes(data->ring, cqes, 1, make_timeout(duration, &storage), NULL);
272
+
273
+ // fprintf(stderr, "result (timeout) = %d\n", result);
248
274
 
249
275
  if (result == -ETIME) {
250
- // Timeout.
276
+ result = 0;
251
277
  } else if (result < 0) {
252
278
  rb_syserr_fail(-result, strerror(-result));
253
279
  }
254
280
  }
255
281
 
256
- int count = io_uring_peek_batch_cqe(data->ring, cqes, URING_MAX_EVENTS);
257
-
258
- if (count == -1) {
259
- rb_sys_fail("io_uring_peek_batch_cqe");
260
- }
261
-
262
- for (int i = 0; i < count; i += 1) {
282
+ for (int i = 0; i < result; i += 1) {
263
283
  VALUE fiber = (VALUE)io_uring_cqe_get_data(cqes[i]);
264
284
  VALUE result = INT2NUM(cqes[i]->res);
265
-
285
+
286
+ // fprintf(stderr, "cqes[i]->res = %d\n", cqes[i]->res);
287
+
266
288
  io_uring_cqe_seen(data->ring, cqes[i]);
267
289
 
268
290
  rb_funcall(fiber, id_transfer, 1, result);
269
291
  }
270
292
 
271
- return INT2NUM(count);
293
+ return INT2NUM(result);
272
294
  }
273
295
 
274
296
  void Init_Event_Backend_URing(VALUE Event_Backend) {
@@ -43,20 +43,20 @@ module Event
43
43
  def select(duration = nil)
44
44
  readable, writable, _ = IO.select(@readable.keys, @writable.keys, nil, duration)
45
45
 
46
- ready = {}
46
+ ready = Hash.new(0)
47
47
 
48
48
  readable&.each do |io|
49
49
  fiber = @readable.delete(io)
50
- ready[fiber] = true
50
+ ready[fiber] |= READABLE
51
51
  end
52
52
 
53
53
  writable&.each do |io|
54
54
  fiber = @writable.delete(io)
55
- ready[fiber] = true
55
+ ready[fiber] |= WRITABLE
56
56
  end
57
57
 
58
- ready.each_key do |fiber|
59
- fiber.transfer
58
+ ready.each do |fiber, events|
59
+ fiber.transfer(events)
60
60
  end
61
61
  end
62
62
  end
data/lib/event/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Event
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: event
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.1
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-04-26 00:00:00.000000000 Z
11
+ date: 2021-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bake
@@ -73,6 +73,7 @@ extensions:
73
73
  - ext/event/extconf.rb
74
74
  extra_rdoc_files: []
75
75
  files:
76
+ - ext/event/backend/backend.h
76
77
  - ext/event/backend/epoll.c
77
78
  - ext/event/backend/epoll.h
78
79
  - ext/event/backend/kqueue.c