event 0.2.2 → 0.2.3

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: 1e032d8ef7daf3803999a2ac9cba5a72e95f6a3b6551f4724be451eb026b03e0
4
- data.tar.gz: 1cdb73bcd9a5f52483faa2da537c1033ebfaba64b41abc80734b475afbd1fd72
3
+ metadata.gz: 9a363339a1d384ea4d844450be2ff46cbc091bc0aca240e869491bf6a5d61300
4
+ data.tar.gz: b1b3c431481f4499bf6605010392283acfb6f54a59aed232f25217d2cce4a221
5
5
  SHA512:
6
- metadata.gz: 471fbc3ded644b8bfadbc4e1cca562f926bcd203141a59c6981e5ca85e225a9a1b09df4ed2f0792077272b2652ef50a81ea208c0fd44bd35b05158bfdb949c9b
7
- data.tar.gz: a22b2804acdc8ad4dc5bc300fb23aa5991a2d69db301c2d6d5d71016643848e13cb1d9392c6367eebf4f3b179357b60c86cdbcd5b2bad8053c0dd0e1b42f38f9
6
+ metadata.gz: 3321631a8be1353038cdc9c63a2b6b17f02ea66924b11f5fb9126f9db1072ea4ee59141858f26e8879e1b52fe265471a2552afcab56cddd84b1e28d1611235d9
7
+ data.tar.gz: 7196977bd770153c5be35d16701e32a635c84cf53edf2880b4c5f095b8644825c7dcd1254a4da8f72655fb48e3ac3ec529d2ae2496691fb9ead30bb595cae357
data/ext/event/Makefile CHANGED
@@ -12,12 +12,12 @@ NULLCMD = :
12
12
  #### Start of system configuration section. ####
13
13
 
14
14
  srcdir = .
15
- topdir = /home/samuel/.rubies/ruby-3.0.0/include/ruby-3.0.0
15
+ topdir = /Users/samuel/.rubies/ruby-3.0.0/include/ruby-3.0.0
16
16
  hdrdir = $(topdir)
17
- arch_hdrdir = /home/samuel/.rubies/ruby-3.0.0/include/ruby-3.0.0/x86_64-linux
17
+ arch_hdrdir = /Users/samuel/.rubies/ruby-3.0.0/include/ruby-3.0.0/x86_64-darwin20
18
18
  PATH_SEPARATOR = :
19
19
  VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby:$(srcdir)/backend
20
- prefix = $(DESTDIR)/home/samuel/.rubies/ruby-3.0.0
20
+ prefix = $(DESTDIR)/Users/samuel/.rubies/ruby-3.0.0
21
21
  rubysitearchprefix = $(rubylibprefix)/$(sitearch)
22
22
  rubyarchprefix = $(rubylibprefix)/$(arch)
23
23
  rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
@@ -50,7 +50,7 @@ dvidir = $(docdir)
50
50
  htmldir = $(docdir)
51
51
  infodir = $(datarootdir)/info
52
52
  docdir = $(datarootdir)/doc/$(PACKAGE)
53
- oldincludedir = $(DESTDIR)/usr/include
53
+ oldincludedir = $(SDKROOT)/usr/include
54
54
  includedir = $(prefix)/include
55
55
  runstatedir = $(localstatedir)/run
56
56
  localstatedir = $(prefix)/var
@@ -65,12 +65,12 @@ archdir = $(rubyarchdir)
65
65
 
66
66
 
67
67
  CC_WRAPPER =
68
- CC = gcc
69
- CXX = g++
68
+ CC = clang -fdeclspec
69
+ CXX = clang++ -fdeclspec
70
70
  LIBRUBY = $(LIBRUBY_A)
71
71
  LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
72
- LIBRUBYARG_SHARED = -Wl,-rpath,$(libdir) -L$(libdir)
73
- LIBRUBYARG_STATIC = -Wl,-rpath,$(libdir) -L$(libdir) -l$(RUBY_SO_NAME)-static $(MAINLIBS)
72
+ LIBRUBYARG_SHARED =
73
+ LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static -framework Security -framework Foundation $(MAINLIBS)
74
74
  empty =
75
75
  OUTFLAG = -o $(empty)
76
76
  COUTFLAG = -o $(empty)
@@ -81,31 +81,31 @@ cflags = $(optflags) $(debugflags) $(warnflags)
81
81
  cxxflags =
82
82
  optflags = -O3
83
83
  debugflags = -ggdb3
84
- warnflags = -Wall -Wextra -Wdeprecated-declarations -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wwrite-strings -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable
84
+ warnflags = -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
85
85
  cppflags =
86
- CCDLFLAGS = -fPIC
87
- CFLAGS = $(CCDLFLAGS) $(cflags) $(ARCH_FLAG)
86
+ CCDLFLAGS = -fno-common
87
+ CFLAGS = $(CCDLFLAGS) $(cflags) -pipe -Wall $(ARCH_FLAG)
88
88
  INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
89
89
  DEFS =
90
- CPPFLAGS = -DRUBY_EXTCONF_H=\"$(RUBY_EXTCONF_H)\" $(DEFS) $(cppflags)
90
+ CPPFLAGS = -DRUBY_EXTCONF_H=\"$(RUBY_EXTCONF_H)\" -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT $(DEFS) $(cppflags)
91
91
  CXXFLAGS = $(CCDLFLAGS) $(ARCH_FLAG)
92
- ldflags = -L. -fstack-protector-strong -rdynamic -Wl,-export-dynamic
93
- dldflags = -Wl,--compress-debug-sections=zlib
92
+ ldflags = -L. -fstack-protector-strong -L/opt/local/lib
93
+ dldflags = -Wl,-undefined,dynamic_lookup -Wl,-multiply_defined,suppress -L/opt/local/lib
94
94
  ARCH_FLAG =
95
95
  DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
96
- LDSHARED = $(CC) -shared
97
- LDSHAREDXX = $(CXX) -shared
98
- AR = gcc-ar
96
+ LDSHARED = $(CC) -dynamic -bundle
97
+ LDSHAREDXX = $(CXX) -dynamic -bundle
98
+ AR = ar
99
99
  EXEEXT =
100
100
 
101
101
  RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)
102
- RUBY_SO_NAME = ruby
102
+ RUBY_SO_NAME = ruby.3.0
103
103
  RUBYW_INSTALL_NAME =
104
104
  RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
105
105
  RUBYW_BASE_NAME = rubyw
106
106
  RUBY_BASE_NAME = ruby
107
107
 
108
- arch = x86_64-linux
108
+ arch = x86_64-darwin20
109
109
  sitearch = $(arch)
110
110
  ruby_version = 3.0.0
111
111
  ruby = $(bindir)/$(RUBY_BASE_NAME)
@@ -114,8 +114,8 @@ ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h
114
114
 
115
115
  RM = rm -f
116
116
  RM_RF = $(RUBY) -run -e rm -- -rf
117
- RMDIRS = rmdir --ignore-fail-on-non-empty -p
118
- MAKEDIRS = /usr/bin/mkdir -p
117
+ RMDIRS = rmdir -p
118
+ MAKEDIRS = /opt/local/bin/gmkdir -p
119
119
  INSTALL = /usr/bin/install -c
120
120
  INSTALL_PROG = $(INSTALL) -m 0755
121
121
  INSTALL_DATA = $(INSTALL) -m 644
@@ -125,8 +125,8 @@ TOUCH = exit >
125
125
  #### End of system configuration section. ####
126
126
 
127
127
  preload =
128
- libpath = . $(libdir)
129
- LIBPATH = -L. -L$(libdir) -Wl,-rpath,$(libdir)
128
+ libpath = . $(libdir) /opt/local/lib
129
+ LIBPATH = -L. -L$(libdir) -L/opt/local/lib
130
130
  DEFFILE =
131
131
 
132
132
  CLEANFILES = mkmf.log
@@ -137,16 +137,16 @@ extout =
137
137
  extout_prefix =
138
138
  target_prefix = /event
139
139
  LOCAL_LIBS =
140
- LIBS = -luring -lm -lc
140
+ LIBS =
141
141
  ORIG_SRCS = event.c
142
- SRCS = $(ORIG_SRCS) event.c uring.c epoll.c
143
- OBJS = event.o uring.o epoll.o
142
+ SRCS = $(ORIG_SRCS) event.c kqueue.c
143
+ OBJS = event.o kqueue.o
144
144
  HDRS = $(srcdir)/event.h $(srcdir)/extconf.h
145
145
  LOCAL_HDRS =
146
146
  TARGET = event
147
147
  TARGET_NAME = event
148
148
  TARGET_ENTRY = Init_$(TARGET_NAME)
149
- DLLIB = $(TARGET).so
149
+ DLLIB = $(TARGET).bundle
150
150
  EXTSTATIC =
151
151
  STATIC_LIB =
152
152
 
@@ -260,6 +260,7 @@ $(TARGET_SO): $(OBJS) Makefile
260
260
  $(ECHO) linking shared-object event/$(DLLIB)
261
261
  -$(Q)$(RM) $(@)
262
262
  $(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
263
+ $(Q) $(POSTLINK)
263
264
 
264
265
 
265
266
 
@@ -25,3 +25,5 @@ enum Event {
25
25
  ERROR = 8,
26
26
  HANGUP = 16
27
27
  };
28
+
29
+ #include <ruby/thread.h>
@@ -28,7 +28,7 @@
28
28
  static VALUE Event_Backend_EPoll = Qnil;
29
29
  static ID id_fileno, id_transfer;
30
30
 
31
- static const unsigned EPOLL_MAX_EVENTS = 64;
31
+ enum {EPOLL_MAX_EVENTS = 64};
32
32
 
33
33
  struct Event_Backend_EPoll {
34
34
  VALUE loop;
@@ -212,28 +212,71 @@ int make_timeout(VALUE duration) {
212
212
  rb_raise(rb_eRuntimeError, "unable to convert timeout");
213
213
  }
214
214
 
215
+ struct select_arguments {
216
+ struct Event_Backend_EPoll *data;
217
+
218
+ int count;
219
+ struct epoll_event events[EPOLL_MAX_EVENTS];
220
+
221
+ int timeout;
222
+ };
223
+
224
+ static
225
+ void * select_internal(void *_arguments) {
226
+ struct select_arguments * arguments = (struct select_arguments *)_arguments;
227
+
228
+ arguments->count = epoll_wait(arguments->data->descriptor, arguments->events, EPOLL_MAX_EVENTS, arguments->timeout);
229
+
230
+ return NULL;
231
+ }
232
+
233
+ static
234
+ void select_internal_without_gvl(struct select_arguments *arguments) {
235
+ rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
236
+
237
+ if (arguments->count == -1) {
238
+ rb_sys_fail("select_internal_without_gvl:epoll_wait");
239
+ }
240
+ }
241
+
242
+ static
243
+ void select_internal_with_gvl(struct select_arguments *arguments) {
244
+ select_internal((void *)arguments);
245
+
246
+ if (arguments->count == -1) {
247
+ rb_sys_fail("select_internal_with_gvl:epoll_wait");
248
+ }
249
+ }
250
+
215
251
  VALUE Event_Backend_EPoll_select(VALUE self, VALUE duration) {
216
252
  struct Event_Backend_EPoll *data = NULL;
217
253
  TypedData_Get_Struct(self, struct Event_Backend_EPoll, &Event_Backend_EPoll_Type, data);
218
254
 
219
- struct epoll_event events[EPOLL_MAX_EVENTS];
255
+ struct select_arguments arguments = {
256
+ .data = data,
257
+ .timeout = 0
258
+ };
220
259
 
221
- int count = epoll_wait(data->descriptor, events, EPOLL_MAX_EVENTS, make_timeout(duration));
260
+ select_internal_without_gvl(&arguments);
222
261
 
223
- if (count == -1) {
224
- rb_sys_fail("epoll_wait");
262
+ if (arguments.count == 0) {
263
+ arguments.timeout = make_timeout(duration);
264
+
265
+ if (arguments.timeout != 0) {
266
+ select_internal_with_gvl(&arguments);
267
+ }
225
268
  }
226
269
 
227
- for (int i = 0; i < count; i += 1) {
228
- VALUE fiber = (VALUE)events[i].data.ptr;
229
- VALUE result = INT2NUM(events[i].events);
270
+ for (int i = 0; i < arguments.count; i += 1) {
271
+ VALUE fiber = (VALUE)arguments.events[i].data.ptr;
272
+ VALUE result = INT2NUM(arguments.events[i].events);
230
273
 
231
274
  // fprintf(stderr, "-> fiber=%p descriptor=%d\n", (void*)fiber, events[i].data.fd);
232
275
 
233
276
  rb_funcall(fiber, id_transfer, 1, result);
234
277
  }
235
278
 
236
- return INT2NUM(count);
279
+ return INT2NUM(arguments.count);
237
280
  }
238
281
 
239
282
  void Init_Event_Backend_EPoll(VALUE Event_Backend) {
@@ -28,7 +28,7 @@
28
28
  static VALUE Event_Backend_KQueue = Qnil;
29
29
  static ID id_fileno, id_transfer;
30
30
 
31
- static const unsigned KQUEUE_MAX_EVENTS = 64;
31
+ enum {KQUEUE_MAX_EVENTS = 64};
32
32
 
33
33
  struct Event_Backend_KQueue {
34
34
  VALUE loop;
@@ -97,50 +97,112 @@ VALUE Event_Backend_KQueue_initialize(VALUE self, VALUE loop) {
97
97
  return self;
98
98
  }
99
99
 
100
- static inline
101
- u_short kqueue_filter_from_events(int events) {
102
- u_short filter = 0;
100
+ static
101
+ int io_add_filters(int descriptor, int ident, int events, VALUE fiber) {
102
+ int count = 0;
103
+ struct kevent kevents[2] = {0};
104
+
105
+ if (events & READABLE) {
106
+ kevents[count].ident = ident;
107
+ kevents[count].filter = EVFILT_READ;
108
+ kevents[count].flags = EV_ADD | EV_ENABLE | EV_ONESHOT;
109
+ kevents[count].udata = (void*)fiber;
110
+
111
+ // #ifdef EV_OOBAND
112
+ // if (events & PRIORITY) {
113
+ // kevents[count].flags |= EV_OOBAND;
114
+ // }
115
+ // #endif
116
+
117
+ count++;
118
+ }
119
+
120
+ if (events & WRITABLE) {
121
+ kevents[count].ident = ident;
122
+ kevents[count].filter = EVFILT_WRITE;
123
+ kevents[count].flags = EV_ADD | EV_ENABLE | EV_ONESHOT;
124
+ kevents[count].udata = (void*)fiber;
125
+ count++;
126
+ }
127
+
128
+ int result = kevent(descriptor, kevents, count, NULL, 0, NULL);
103
129
 
104
- if (events & READABLE) filter |= EVFILT_READ;
105
- if (events & PRIORITY) filter |= EV_OOBAND;
106
- if (events & WRITABLE) filter |= EVFILT_WRITE;
130
+ if (result == -1) {
131
+ rb_sys_fail("kevent(register)");
132
+ }
107
133
 
108
- return filter;
134
+ return events;
109
135
  }
110
136
 
111
- static inline
112
- int events_from_kqueue_filter(u_short filter) {
113
- int events = 0;
137
+ static
138
+ void io_remove_filters(int descriptor, int ident, int events) {
139
+ int count = 0;
140
+ struct kevent kevents[2] = {0};
114
141
 
115
- if (filter & EVFILT_READ) events |= READABLE;
116
- if (filter & EV_OOBAND) events |= PRIORITY;
117
- if (filter & EVFILT_WRITE) events |= WRITABLE;
142
+ if (events & READABLE) {
143
+ kevents[count].ident = ident;
144
+ kevents[count].filter = EVFILT_READ;
145
+ kevents[count].flags = EV_DELETE;
146
+
147
+ count++;
148
+ }
149
+
150
+ if (events & WRITABLE) {
151
+ kevents[count].ident = ident;
152
+ kevents[count].filter = EVFILT_WRITE;
153
+ kevents[count].flags = EV_DELETE;
154
+ count++;
155
+ }
118
156
 
119
- return INT2NUM(events);
157
+ // Ignore the result.
158
+ kevent(descriptor, kevents, count, NULL, 0, NULL);
120
159
  }
121
160
 
161
+ struct io_wait_arguments {
162
+ struct Event_Backend_KQueue *data;
163
+ int events;
164
+ int descriptor;
165
+ };
166
+
167
+ static
168
+ VALUE io_wait_rescue(VALUE _arguments, VALUE exception) {
169
+ struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
170
+
171
+ io_remove_filters(arguments->data->descriptor, arguments->descriptor, arguments->events);
172
+
173
+ rb_exc_raise(exception);
174
+ };
175
+
176
+ static inline
177
+ int events_from_kqueue_filter(int filter) {
178
+ if (filter == EVFILT_READ) return READABLE;
179
+ if (filter == EVFILT_WRITE) return WRITABLE;
180
+
181
+ return 0;
182
+ }
183
+
184
+ static
185
+ VALUE io_wait_transfer(VALUE _arguments) {
186
+ struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
187
+
188
+ VALUE result = rb_funcall(arguments->data->loop, id_transfer, 0);
189
+
190
+ return INT2NUM(events_from_kqueue_filter(NUM2INT(result)));
191
+ };
192
+
122
193
  VALUE Event_Backend_KQueue_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE events) {
123
194
  struct Event_Backend_KQueue *data = NULL;
124
195
  TypedData_Get_Struct(self, struct Event_Backend_KQueue, &Event_Backend_KQueue_Type, data);
125
196
 
126
- struct kevent event = {0};
127
-
128
197
  int descriptor = NUM2INT(rb_funcall(io, id_fileno, 0));
129
198
 
130
- event.ident = descriptor;
131
- event.filter = kqueue_filters_from_events(events);
132
- event.flags = EV_ADD | EV_ENABLE | EV_ONESHOT;
133
- event.udata = (void*)fiber;
134
-
135
- // A better approach is to batch all changes:
136
- int result = kevent(data->descriptor, &event, 1, NULL, 0, NULL);
137
-
138
- if (result == -1) {
139
- rb_sys_fail("kevent");
140
- }
199
+ struct io_wait_arguments io_wait_arguments = {
200
+ .events = io_add_filters(data->descriptor, descriptor, NUM2INT(events), fiber),
201
+ .data = data,
202
+ .descriptor = descriptor,
203
+ };
141
204
 
142
- VALUE result = rb_funcall(data->loop, id_transfer, 0);
143
- return INT2NUM(events_from_kqueue_filter(NUM2INT(result)));
205
+ return rb_rescue(io_wait_transfer, (VALUE)&io_wait_arguments, io_wait_rescue, (VALUE)&io_wait_arguments);
144
206
  }
145
207
 
146
208
  static
@@ -158,7 +220,7 @@ struct timespec * make_timeout(VALUE duration, struct timespec * storage) {
158
220
 
159
221
  else if (RB_FLOAT_TYPE_P(duration)) {
160
222
  double value = RFLOAT_VALUE(duration);
161
- time_t seconds = duration;
223
+ time_t seconds = value;
162
224
 
163
225
  storage->tv_sec = seconds;
164
226
  storage->tv_nsec = (value - seconds) * 1000000000L;
@@ -169,26 +231,89 @@ struct timespec * make_timeout(VALUE duration, struct timespec * storage) {
169
231
  rb_raise(rb_eRuntimeError, "unable to convert timeout");
170
232
  }
171
233
 
234
+ static
235
+ int timeout_nonblocking(struct timespec * timespec) {
236
+ return timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0;
237
+ }
238
+
239
+ struct select_arguments {
240
+ struct Event_Backend_KQueue *data;
241
+
242
+ int count;
243
+ struct kevent events[KQUEUE_MAX_EVENTS];
244
+
245
+ struct timespec storage;
246
+ struct timespec *timeout;
247
+ };
248
+
249
+ static
250
+ void * select_internal(void *_arguments) {
251
+ struct select_arguments * arguments = (struct select_arguments *)_arguments;
252
+
253
+ arguments->count = kevent(arguments->data->descriptor, NULL, 0, arguments->events, arguments->count, arguments->timeout);
254
+
255
+ return NULL;
256
+ }
257
+
258
+ static
259
+ void select_internal_without_gvl(struct select_arguments *arguments) {
260
+ rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
261
+
262
+ if (arguments->count == -1) {
263
+ rb_sys_fail("select_internal_without_gvl:kevent");
264
+ }
265
+ }
266
+
267
+ static
268
+ void select_internal_with_gvl(struct select_arguments *arguments) {
269
+ select_internal((void *)arguments);
270
+
271
+ if (arguments->count == -1) {
272
+ rb_sys_fail("select_internal_with_gvl:kevent");
273
+ }
274
+ }
275
+
172
276
  VALUE Event_Backend_KQueue_select(VALUE self, VALUE duration) {
173
277
  struct Event_Backend_KQueue *data = NULL;
174
278
  TypedData_Get_Struct(self, struct Event_Backend_KQueue, &Event_Backend_KQueue_Type, data);
175
279
 
176
- struct kevent events[KQUEUE_MAX_EVENTS];
177
- struct timespec storage;
280
+ struct select_arguments arguments = {
281
+ .data = data,
282
+ .count = KQUEUE_MAX_EVENTS,
283
+ .storage = {
284
+ .tv_sec = 0,
285
+ .tv_nsec = 0
286
+ }
287
+ };
178
288
 
179
- int count = kevent(data->descriptor, NULL, 0, events, KQUEUE_MAX_EVENTS, make_timeout(duration, &storage));
289
+ // We break this implementation into two parts.
290
+ // (1) count = kevent(..., timeout = 0)
291
+ // (2) without gvl: kevent(..., timeout = 0) if count == 0 and timeout != 0
292
+ // This allows us to avoid releasing and reacquiring the GVL.
293
+ // Non-comprehensive testing shows this gives a 1.5x speedup.
294
+ arguments.timeout = &arguments.storage;
180
295
 
181
- if (count == -1) {
182
- rb_sys_fail("kevent");
296
+ // First do the syscall with no timeout to get any immediately available events:
297
+ select_internal_with_gvl(&arguments);
298
+
299
+ // If there were no pending events, if we have a timeout, wait for more events:
300
+ if (arguments.count == 0) {
301
+ arguments.timeout = make_timeout(duration, &arguments.storage);
302
+
303
+ if (!timeout_nonblocking(arguments.timeout)) {
304
+ arguments.count = KQUEUE_MAX_EVENTS;
305
+
306
+ select_internal_without_gvl(&arguments);
307
+ }
183
308
  }
184
309
 
185
- for (int i = 0; i < count; i += 1) {
186
- VALUE fiber = (VALUE)events[i].udata;
187
- VALUE result = INT2NUM(events[i].filter);
310
+ for (int i = 0; i < arguments.count; i += 1) {
311
+ VALUE fiber = (VALUE)arguments.events[i].udata;
312
+ VALUE result = INT2NUM(arguments.events[i].filter);
188
313
  rb_funcall(fiber, id_transfer, 1, result);
189
314
  }
190
315
 
191
- return INT2NUM(count);
316
+ return INT2NUM(arguments.count);
192
317
  }
193
318
 
194
319
  void Init_Event_Backend_KQueue(VALUE Event_Backend) {
@@ -28,8 +28,8 @@
28
28
  static VALUE Event_Backend_URing = Qnil;
29
29
  static ID id_fileno, id_transfer;
30
30
 
31
- static const int URING_ENTRIES = 64;
32
- static const int URING_MAX_EVENTS = 64;
31
+ enum {URING_ENTRIES = 64};
32
+ enum {URING_MAX_EVENTS = 64};
33
33
 
34
34
  struct Event_Backend_URing {
35
35
  VALUE loop;
@@ -122,28 +122,76 @@ int events_from_poll_flags(short flags) {
122
122
  return events;
123
123
  }
124
124
 
125
+ struct io_wait_arguments {
126
+ struct Event_Backend_URing *data;
127
+ VALUE fiber;
128
+ short flags;
129
+ };
130
+
131
+ static
132
+ VALUE io_wait_rescue(VALUE _arguments, VALUE exception) {
133
+ struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
134
+ struct Event_Backend_URing *data = arguments->data;
135
+
136
+ struct io_uring_sqe *sqe = Event_Backend_URing_io_uring_get_sqe(data);
137
+
138
+ // fprintf(stderr, "poll_remove(%p, %p)\n", sqe, (void*)arguments->fiber);
139
+
140
+ io_uring_prep_poll_remove(sqe, (void*)arguments->fiber);
141
+ io_uring_submit(&data->ring);
142
+
143
+ rb_exc_raise(exception);
144
+ };
145
+
146
+ static
147
+ VALUE io_wait_transfer(VALUE _arguments) {
148
+ struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
149
+ struct Event_Backend_URing *data = arguments->data;
150
+
151
+ VALUE result = rb_funcall(data->loop, id_transfer, 0);
152
+
153
+ // We explicitly filter the resulting events based on the requested events.
154
+ // In some cases, poll will report events we didn't ask for.
155
+ short flags = arguments->flags & NUM2INT(result);
156
+
157
+ return INT2NUM(events_from_poll_flags(flags));
158
+ };
159
+
160
+ struct io_uring_sqe *Event_Backend_URing_io_uring_get_sqe(struct Event_Backend_URing *data) {
161
+ struct io_uring_sqe *sqe = NULL;
162
+
163
+ while (true) {
164
+ sqe = io_uring_get_sqe(&data->ring);
165
+ if (sqe != NULL) {
166
+ return sqe;
167
+ }
168
+ // The sqe is full, we need to poll before submitting more events.
169
+ Event_Backend_URing_select(self, INT2NUM(0));
170
+ }
171
+ }
172
+
125
173
  VALUE Event_Backend_URing_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE events) {
126
174
  struct Event_Backend_URing *data = NULL;
127
175
  TypedData_Get_Struct(self, struct Event_Backend_URing, &Event_Backend_URing_Type, data);
128
176
 
129
177
  int descriptor = NUM2INT(rb_funcall(io, id_fileno, 0));
130
- struct io_uring_sqe *sqe = io_uring_get_sqe(&data->ring);
178
+ struct io_uring_sqe *sqe = Event_Backend_URing_io_uring_get_sqe(data);
131
179
 
132
180
  short flags = poll_flags_from_events(NUM2INT(events));
133
181
 
134
- // fprintf(stderr, "poll_add(%p, %d, %d)\n", sqe, descriptor, flags);
182
+ // fprintf(stderr, "poll_add(%p, %d, %d, %p)\n", sqe, descriptor, flags, (void*)fiber);
135
183
 
136
184
  io_uring_prep_poll_add(sqe, descriptor, flags);
137
185
  io_uring_sqe_set_data(sqe, (void*)fiber);
138
186
  io_uring_submit(&data->ring);
139
187
 
140
- VALUE result = rb_funcall(data->loop, id_transfer, 0);
141
-
142
- // We explicitly filter the resulting events based on the requested events.
143
- // In some cases, poll will report events we didn't ask for.
144
- flags &= NUM2INT(result);
188
+ struct io_wait_arguments io_wait_arguments = {
189
+ .data = data,
190
+ .fiber = fiber,
191
+ .flags = flags
192
+ };
145
193
 
146
- return INT2NUM(events_from_poll_flags(flags));
194
+ return rb_rescue(io_wait_transfer, (VALUE)&io_wait_arguments, io_wait_rescue, (VALUE)&io_wait_arguments);
147
195
  }
148
196
 
149
197
  inline static
@@ -176,7 +224,7 @@ VALUE Event_Backend_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffe
176
224
  resize_to_capacity(buffer, NUM2SIZET(offset), NUM2SIZET(length));
177
225
 
178
226
  int descriptor = NUM2INT(rb_funcall(io, id_fileno, 0));
179
- struct io_uring_sqe *sqe = io_uring_get_sqe(&data->ring);
227
+ struct io_uring_sqe *sqe = Event_Backend_URing_io_uring_get_sqe(data);
180
228
 
181
229
  struct iovec iovecs[1];
182
230
  iovecs[0].iov_base = RSTRING_PTR(buffer) + NUM2SIZET(offset);
@@ -208,7 +256,7 @@ VALUE Event_Backend_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buff
208
256
  }
209
257
 
210
258
  int descriptor = NUM2INT(rb_funcall(io, id_fileno, 0));
211
- struct io_uring_sqe *sqe = io_uring_get_sqe(&data->ring);
259
+ struct io_uring_sqe *sqe = Event_Backend_URing_io_uring_get_sqe(data);
212
260
 
213
261
  struct iovec iovecs[1];
214
262
  iovecs[0].iov_base = RSTRING_PTR(buffer) + NUM2SIZET(offset);
@@ -244,7 +292,7 @@ struct __kernel_timespec * make_timeout(VALUE duration, struct __kernel_timespec
244
292
 
245
293
  else if (RB_FLOAT_TYPE_P(duration)) {
246
294
  double value = RFLOAT_VALUE(duration);
247
- time_t seconds = duration;
295
+ time_t seconds = value;
248
296
 
249
297
  storage->tv_sec = seconds;
250
298
  storage->tv_nsec = (value - seconds) * 1000000000L;
@@ -255,36 +303,84 @@ struct __kernel_timespec * make_timeout(VALUE duration, struct __kernel_timespec
255
303
  rb_raise(rb_eRuntimeError, "unable to convert timeout");
256
304
  }
257
305
 
306
+ static
307
+ int timeout_nonblocking(struct __kernel_timespec *timespec) {
308
+ return timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0;
309
+ }
310
+
311
+ struct select_arguments {
312
+ struct Event_Backend_URing *data;
313
+
314
+ int count;
315
+ struct io_uring_cqe **cqes;
316
+
317
+ struct __kernel_timespec storage;
318
+ struct __kernel_timespec *timeout;
319
+ };
320
+
321
+ static
322
+ void * select_internal(void *_arguments) {
323
+ struct select_arguments * arguments = (struct select_arguments *)_arguments;
324
+
325
+ arguments->count = io_uring_wait_cqes(&arguments->data->ring, arguments->cqes, 1, arguments->timeout, NULL);
326
+
327
+ // If waiting resulted in a timeout, there are 0 events.
328
+ if (arguments->count == -ETIME) {
329
+ arguments->count = 0;
330
+ }
331
+
332
+ return NULL;
333
+ }
334
+
335
+ static
336
+ int select_internal_without_gvl(struct select_arguments *arguments) {
337
+ rb_thread_call_without_gvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
338
+
339
+ if (arguments->count < 0) {
340
+ rb_syserr_fail(-arguments->count, "select_internal_without_gvl:io_uring_wait_cqes");
341
+ }
342
+
343
+ return arguments->count;
344
+ }
345
+
258
346
  VALUE Event_Backend_URing_select(VALUE self, VALUE duration) {
259
347
  struct Event_Backend_URing *data = NULL;
260
348
  TypedData_Get_Struct(self, struct Event_Backend_URing, &Event_Backend_URing_Type, data);
261
349
 
262
350
  struct io_uring_cqe *cqes[URING_MAX_EVENTS];
263
- struct __kernel_timespec storage;
264
351
 
352
+ // This is a non-blocking operation:
265
353
  int result = io_uring_peek_batch_cqe(&data->ring, cqes, URING_MAX_EVENTS);
266
354
 
267
- // fprintf(stderr, "result = %d\n", result);
268
-
269
355
  if (result < 0) {
270
356
  rb_syserr_fail(-result, strerror(-result));
271
- } else if (result == 0 && duration != Qnil) {
272
- result = io_uring_wait_cqes(&data->ring, cqes, 1, make_timeout(duration, &storage), NULL);
357
+ } else if (result == 0) {
358
+ // We might need to wait for events:
359
+ struct select_arguments arguments = {
360
+ .data = data,
361
+ .cqes = cqes,
362
+ .timeout = NULL,
363
+ };
273
364
 
274
- // fprintf(stderr, "result (timeout) = %d\n", result);
365
+ arguments.timeout = make_timeout(duration, &arguments.storage);
275
366
 
276
- if (result == -ETIME) {
277
- result = 0;
278
- } else if (result < 0) {
279
- rb_syserr_fail(-result, strerror(-result));
367
+ if (!timeout_nonblocking(arguments.timeout)) {
368
+ result = select_internal_without_gvl(&arguments);
280
369
  }
281
370
  }
282
371
 
372
+ // fprintf(stderr, "cqes count=%d\n", result);
373
+
283
374
  for (int i = 0; i < result; i += 1) {
375
+ // If the operation was cancelled, or the operation has no user data (fiber):
376
+ if (cqes[i]->res == -ECANCELED || cqes[i]->user_data == 0) {
377
+ continue;
378
+ }
379
+
284
380
  VALUE fiber = (VALUE)io_uring_cqe_get_data(cqes[i]);
285
381
  VALUE result = INT2NUM(cqes[i]->res);
286
382
 
287
- // fprintf(stderr, "cqes[i]->res = %d\n", cqes[i]->res);
383
+ // fprintf(stderr, "cqes[i] res=%d user_data=%p\n", cqes[i]->res, (void*)cqes[i]->user_data);
288
384
 
289
385
  io_uring_cqe_seen(&data->ring, cqes[i]);
290
386
 
@@ -25,3 +25,4 @@
25
25
  #define EVENT_BACKEND_URING
26
26
 
27
27
  void Init_Event_Backend_URing(VALUE Event_Backend);
28
+ VALUE Event_Backend_URing_select(VALUE self, VALUE duration);
Binary file
data/ext/event/event.o ADDED
Binary file
@@ -0,0 +1,4 @@
1
+ #ifndef EXTCONF_H
2
+ #define EXTCONF_H
3
+ #define HAVE_SYS_EVENT_H 1
4
+ #endif
data/ext/event/extconf.rb CHANGED
@@ -28,6 +28,8 @@ extension_name = 'event'
28
28
  # The destination
29
29
  dir_config(extension_name)
30
30
 
31
+ $CFLAGS << " -Wall"
32
+
31
33
  $srcs = ["event.c"]
32
34
  $VPATH << "$(srcdir)/backend"
33
35
 
Binary file
@@ -0,0 +1,73 @@
1
+ have_library: checking for -luring... -------------------- no
2
+
3
+ "clang -fdeclspec -o conftest -I/Users/samuel/.rubies/ruby-3.0.0/include/ruby-3.0.0/x86_64-darwin20 -I/Users/samuel/.rubies/ruby-3.0.0/include/ruby-3.0.0/ruby/backward -I/Users/samuel/.rubies/ruby-3.0.0/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.0/lib -L/opt/local/lib -L. -fstack-protector-strong -L/opt/local/lib -lruby.3.0-static -framework Security -framework Foundation -lpthread -lgmp -ldl -lobjc "
4
+ checked program was:
5
+ /* begin */
6
+ 1: #include "ruby.h"
7
+ 2:
8
+ 3: int main(int argc, char **argv)
9
+ 4: {
10
+ 5: return !!argv[argc];
11
+ 6: }
12
+ /* end */
13
+
14
+ "clang -fdeclspec -o conftest -I/Users/samuel/.rubies/ruby-3.0.0/include/ruby-3.0.0/x86_64-darwin20 -I/Users/samuel/.rubies/ruby-3.0.0/include/ruby-3.0.0/ruby/backward -I/Users/samuel/.rubies/ruby-3.0.0/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.0/lib -L/opt/local/lib -L. -fstack-protector-strong -L/opt/local/lib -lruby.3.0-static -framework Security -framework Foundation -lpthread -lgmp -ldl -lobjc -luring "
15
+ ld: library not found for -luring
16
+ clang: error: linker command failed with exit code 1 (use -v to see invocation)
17
+ checked program was:
18
+ /* begin */
19
+ 1: #include "ruby.h"
20
+ 2:
21
+ 3: /*top*/
22
+ 4: extern int t(void);
23
+ 5: int main(int argc, char **argv)
24
+ 6: {
25
+ 7: if (argc > 1000000) {
26
+ 8: int (* volatile tp)(void)=(int (*)(void))&t;
27
+ 9: printf("%d", (*tp)());
28
+ 10: }
29
+ 11:
30
+ 12: return !!argv[argc];
31
+ 13: }
32
+ 14:
33
+ 15: int t(void) { ; return 0; }
34
+ /* end */
35
+
36
+ --------------------
37
+
38
+ have_header: checking for sys/epoll.h... -------------------- no
39
+
40
+ "clang -E -I/Users/samuel/.rubies/ruby-3.0.0/include/ruby-3.0.0/x86_64-darwin20 -I/Users/samuel/.rubies/ruby-3.0.0/include/ruby-3.0.0/ruby/backward -I/Users/samuel/.rubies/ruby-3.0.0/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"
41
+ conftest.c:3:10: fatal error: 'sys/epoll.h' file not found
42
+ #include <sys/epoll.h>
43
+ ^~~~~~~~~~~~~
44
+ 1 error generated.
45
+ checked program was:
46
+ /* begin */
47
+ 1: #include "ruby.h"
48
+ 2:
49
+ 3: #include <sys/epoll.h>
50
+ /* end */
51
+
52
+ --------------------
53
+
54
+ have_header: checking for sys/event.h... -------------------- yes
55
+
56
+ "clang -E -I/Users/samuel/.rubies/ruby-3.0.0/include/ruby-3.0.0/x86_64-darwin20 -I/Users/samuel/.rubies/ruby-3.0.0/include/ruby-3.0.0/ruby/backward -I/Users/samuel/.rubies/ruby-3.0.0/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"
57
+ checked program was:
58
+ /* begin */
59
+ 1: #include "ruby.h"
60
+ 2:
61
+ 3: #include <sys/event.h>
62
+ /* end */
63
+
64
+ --------------------
65
+
66
+ extconf.h is:
67
+ /* begin */
68
+ 1: #ifndef EXTCONF_H
69
+ 2: #define EXTCONF_H
70
+ 3: #define HAVE_SYS_EVENT_H 1
71
+ 4: #endif
72
+ /* end */
73
+
@@ -29,15 +29,23 @@ module Event
29
29
  end
30
30
 
31
31
  def io_wait(fiber, io, events)
32
+ remove_readable = remove_writable = false
33
+
32
34
  if (events & READABLE) > 0 or (events & PRIORITY) > 0
33
35
  @readable[io] = fiber
36
+ remove_readable = true
34
37
  end
35
38
 
36
39
  if (events & WRITABLE) > 0
37
40
  @writable[io] = fiber
41
+ remove_writable = true
38
42
  end
39
43
 
40
44
  @loop.transfer
45
+
46
+ ensure
47
+ @readable.delete(io) if remove_readable
48
+ @writable.delete(io) if remove_writable
41
49
  end
42
50
 
43
51
  def select(duration = nil)
data/lib/event/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Event
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.3"
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.2.2
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-04-28 00:00:00.000000000 Z
11
+ date: 2021-05-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bake
@@ -81,9 +81,14 @@ files:
81
81
  - ext/event/backend/kqueue.h
82
82
  - ext/event/backend/uring.c
83
83
  - ext/event/backend/uring.h
84
+ - ext/event/event.bundle
84
85
  - ext/event/event.c
85
86
  - ext/event/event.h
87
+ - ext/event/event.o
88
+ - ext/event/extconf.h
86
89
  - ext/event/extconf.rb
90
+ - ext/event/kqueue.o
91
+ - ext/event/mkmf.log
87
92
  - lib/event.rb
88
93
  - lib/event/backend/select.rb
89
94
  - lib/event/debug/selector.rb
@@ -108,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
113
  - !ruby/object:Gem::Version
109
114
  version: '0'
110
115
  requirements: []
111
- rubygems_version: 3.2.3
116
+ rubygems_version: 3.2.15
112
117
  signing_key:
113
118
  specification_version: 4
114
119
  summary: An event loop.