event 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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.