ever 0.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.
data/ext/ever/ever.h ADDED
@@ -0,0 +1,49 @@
1
+ #ifndef EVER_H
2
+ #define EVER_H
3
+
4
+ #include <execinfo.h>
5
+ #include "ruby.h"
6
+ #include "../libev/ev.h"
7
+
8
+ // debugging
9
+ #define OBJ_ID(obj) (NUM2LONG(rb_funcall(obj, rb_intern("object_id"), 0)))
10
+ #define INSPECT(str, obj) { printf(str); VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf(": %s\n", StringValueCStr(s)); }
11
+ #define TRACE_CALLER() { VALUE c = rb_funcall(rb_mKernel, rb_intern("caller"), 0); INSPECT("caller: ", c); }
12
+ #define TRACE_C_STACK() { \
13
+ void *entries[10]; \
14
+ size_t size = backtrace(entries, 10); \
15
+ char **strings = backtrace_symbols(entries, size); \
16
+ for (unsigned long i = 0; i < size; i++) printf("%s\n", strings[i]); \
17
+ free(strings); \
18
+ }
19
+
20
+ // exceptions
21
+ #define TEST_EXCEPTION(ret) (rb_obj_is_kind_of(ret, rb_eException) == Qtrue)
22
+ #define RAISE_EXCEPTION(e) rb_funcall(e, ID_invoke, 0);
23
+ #define RAISE_IF_EXCEPTION(ret) if (rb_obj_is_kind_of(ret, rb_eException) == Qtrue) { RAISE_EXCEPTION(ret); }
24
+ #define RAISE_IF_NOT_NIL(ret) if (ret != Qnil) { RAISE_EXCEPTION(ret); }
25
+
26
+ extern VALUE mEver;
27
+ extern VALUE cLoop;
28
+ extern VALUE cWatcher;
29
+
30
+ typedef struct Loop_t {
31
+ struct ev_loop *ev_loop;
32
+ struct ev_async break_async;
33
+
34
+ VALUE active_watchers;
35
+ VALUE free_watchers;
36
+ VALUE queued_events;
37
+
38
+ int stop;
39
+ int in_ev_loop;
40
+ } Loop_t;
41
+
42
+ void loop_emit(Loop_t *loop, VALUE key);
43
+ void loop_release_watcher(Loop_t *loop, VALUE key);
44
+
45
+ void Watcher_setup_io(VALUE watcher, Loop_t *loop, VALUE key, int fd, int events, int oneshot);
46
+ void Watcher_setup_timer(VALUE watcher, Loop_t *loop, VALUE key, double timeout, double interval);
47
+ void Watcher_stop(VALUE watcher);
48
+
49
+ #endif /* EVER_H */
@@ -0,0 +1,12 @@
1
+ #include "ever.h"
2
+
3
+ void Init_Loop();
4
+ void Init_Watcher();
5
+
6
+ VALUE mEver;
7
+
8
+ void Init_ever_ext() {
9
+ mEver = rb_define_module("Ever");
10
+ Init_Loop();
11
+ Init_Watcher();
12
+ }
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems'
4
+ require 'mkmf'
5
+
6
+ $defs << '-DEV_USE_LINUXAIO' if have_header('linux/aio_abi.h')
7
+ $defs << '-DEV_USE_SELECT' if have_header('sys/select.h')
8
+ $defs << '-DEV_USE_POLL' if have_type('port_event_t', 'poll.h')
9
+ $defs << '-DEV_USE_EPOLL' if have_header('sys/epoll.h')
10
+ $defs << '-DEV_USE_KQUEUE' if have_header('sys/event.h') && have_header('sys/queue.h')
11
+ $defs << '-DEV_USE_PORT' if have_type('port_event_t', 'port.h')
12
+ $defs << '-DHAVE_SYS_RESOURCE_H' if have_header('sys/resource.h')
13
+
14
+ $CFLAGS << " -Wno-comment"
15
+ $CFLAGS << " -Wno-unused-result"
16
+ $CFLAGS << " -Wno-dangling-else"
17
+ $CFLAGS << " -Wno-parentheses"
18
+
19
+ CONFIG['optflags'] << ' -fno-strict-aliasing' unless RUBY_PLATFORM =~ /mswin/
20
+
21
+ dir_config 'ever_ext'
22
+ create_makefile 'ever_ext'
data/ext/ever/libev.c ADDED
@@ -0,0 +1,2 @@
1
+ #include "libev.h"
2
+ #include "../libev/ev.c"
data/ext/ever/libev.h ADDED
@@ -0,0 +1,11 @@
1
+ #define EV_STANDALONE
2
+
3
+ /* keeps ev from requiring config.h */
4
+
5
+ #ifdef _WIN32
6
+ #define EV_SELECT_IS_WINSOCKET 1
7
+ #define EV_USE_MONOTONIC 0
8
+ #define EV_USE_REALTIME 0
9
+ #endif
10
+
11
+ #include "../libev/ev.h"
data/ext/ever/loop.c ADDED
@@ -0,0 +1,257 @@
1
+ #include "ever.h"
2
+ #include "ruby/io.h"
3
+
4
+ VALUE cLoop;
5
+ VALUE SYM_stop;
6
+ ID ID_ivar_io;
7
+
8
+ ////////////////////////////////////////////////////////////////////////////////
9
+
10
+ static inline void loop_run_ev_loop(Loop_t *loop) {
11
+ loop->in_ev_loop = 1;
12
+ ev_run(loop->ev_loop, EVRUN_ONCE);
13
+ loop->in_ev_loop = 0;
14
+ }
15
+
16
+ static inline void loop_yield_queued_events(Loop_t *loop) {
17
+ int len = RARRAY_LEN(loop->queued_events);
18
+ if (!len) return;
19
+
20
+ VALUE events = loop->queued_events;
21
+ loop->queued_events = rb_ary_new();
22
+ for (int i = 0; i < len; i++) {
23
+ rb_yield(RARRAY_AREF(events, i));
24
+ }
25
+ RB_GC_GUARD(events);
26
+ }
27
+
28
+ inline void loop_emit(Loop_t *loop, VALUE key) {
29
+ rb_ary_push(loop->queued_events, key);
30
+ }
31
+
32
+ static inline void loop_signal(Loop_t *loop) {
33
+ if (loop->in_ev_loop) {
34
+ // Since the loop will run until at least one event has occurred, we signal
35
+ // the selector's associated async watcher, which will cause the ev loop to
36
+ // return. In contrast to using `ev_break` to break out of the loop, which
37
+ // should be called from the same thread (from within the ev_loop), using an
38
+ // `ev_async` allows us to interrupt the event loop across threads.
39
+ ev_async_send(loop->ev_loop, &loop->break_async);
40
+ }
41
+ }
42
+
43
+ static inline VALUE loop_get_free_watcher(Loop_t *loop) {
44
+ VALUE watcher = rb_ary_pop(loop->free_watchers);
45
+ if (watcher != Qnil) return watcher;
46
+
47
+ return rb_class_new_instance(0, 0, cWatcher);
48
+ }
49
+
50
+ static inline void loop_watch_fd(Loop_t *loop, VALUE key, int fd, int events, int oneshot) {
51
+ VALUE watcher = loop_get_free_watcher(loop);
52
+ Watcher_setup_io(watcher, loop, key, fd, events, oneshot);
53
+ rb_hash_aset(loop->active_watchers, key, watcher);
54
+ }
55
+
56
+ static inline void loop_watch_timer(Loop_t *loop, VALUE key, double timeout, double interval) {
57
+ VALUE watcher = loop_get_free_watcher(loop);
58
+ Watcher_setup_timer(watcher, loop, key, timeout, interval);
59
+ rb_hash_aset(loop->active_watchers, key, watcher);
60
+ }
61
+
62
+ static inline int sym_to_events(VALUE rw) {
63
+ return RTEST(rw) ? EV_WRITE : EV_READ;
64
+ }
65
+
66
+ static inline int fd_from_io(VALUE io) {
67
+ rb_io_t *fptr;
68
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
69
+ if (underlying_io != Qnil) io = underlying_io;
70
+ GetOpenFile(io, fptr);
71
+ return fptr->fd;
72
+ }
73
+
74
+ inline void loop_release_watcher(Loop_t *loop, VALUE key) {
75
+ VALUE watcher = rb_hash_delete(loop->active_watchers, key);
76
+ if (watcher != Qnil)
77
+ rb_ary_push(loop->free_watchers, watcher);
78
+ }
79
+
80
+ ////////////////////////////////////////////////////////////////////////////////
81
+
82
+ static void Loop_mark(void *ptr) {
83
+ Loop_t *loop = ptr;
84
+ rb_gc_mark(loop->active_watchers);
85
+ rb_gc_mark(loop->free_watchers);
86
+ rb_gc_mark(loop->queued_events);
87
+ }
88
+
89
+ static void Loop_free(void *ptr) {
90
+ Loop_t *loop = ptr;
91
+
92
+ ev_async_stop(loop->ev_loop, &loop->break_async);
93
+ ev_loop_destroy(loop->ev_loop);
94
+ }
95
+
96
+ static size_t Loop_size(const void *ptr) {
97
+ return sizeof(Loop_t);
98
+ }
99
+
100
+ static const rb_data_type_t Loop_type = {
101
+ "Ever::Loop",
102
+ {Loop_mark, Loop_free, Loop_size,},
103
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
104
+ };
105
+
106
+ static VALUE Loop_allocate(VALUE klass) {
107
+ Loop_t *loop = ALLOC(Loop_t);
108
+ return TypedData_Wrap_Struct(klass, &Loop_type, loop);
109
+ }
110
+
111
+ #define GetLoop(obj, loop) \
112
+ TypedData_Get_Struct((obj), Loop_t, &Loop_type, (loop))
113
+
114
+ void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
115
+ // This callback does nothing, the break async is used solely for breaking out
116
+ // of a *blocking* event loop (waking it up) in a thread-safe, signal-safe manner
117
+ }
118
+
119
+ static VALUE Loop_initialize(VALUE self) {
120
+ Loop_t *loop;
121
+ GetLoop(self, loop);
122
+
123
+ loop->ev_loop = ev_loop_new(EVFLAG_NOSIGMASK);
124
+ // start async watcher used for breaking a poll op (from another thread)
125
+ ev_async_init(&loop->break_async, break_async_callback);
126
+ ev_async_start(loop->ev_loop, &loop->break_async);
127
+ // the break_async watcher is unreferenced, in order for Loop_poll to not
128
+ // block when no other watcher is active
129
+ ev_unref(loop->ev_loop);
130
+
131
+ loop->active_watchers = rb_hash_new();
132
+ loop->free_watchers = rb_ary_new();
133
+ loop->queued_events = rb_ary_new();
134
+
135
+ loop->stop = 0;
136
+ loop->in_ev_loop = 0;
137
+
138
+ return Qnil;
139
+ }
140
+
141
+ VALUE Loop_each(VALUE self) {
142
+ Loop_t *loop;
143
+ GetLoop(self, loop);
144
+
145
+ loop->stop = 0;
146
+ while (!loop->stop) {
147
+ if (RARRAY_LEN(loop->queued_events) == 0) loop_run_ev_loop(loop);
148
+ loop_yield_queued_events(loop);
149
+ }
150
+ return self;
151
+ }
152
+
153
+ VALUE Loop_next_event(VALUE self) {
154
+ Loop_t *loop;
155
+ GetLoop(self, loop);
156
+
157
+ if (RARRAY_LEN(loop->queued_events) == 0) loop_run_ev_loop(loop);
158
+ return rb_ary_shift(loop->queued_events);
159
+ }
160
+
161
+ VALUE Loop_emit(VALUE self, VALUE key) {
162
+ Loop_t *loop;
163
+ GetLoop(self, loop);
164
+
165
+ if (key == SYM_stop)
166
+ loop->stop = 1;
167
+ else
168
+ rb_ary_push(loop->queued_events, key);
169
+
170
+ loop_signal(loop);
171
+ return key;
172
+ }
173
+
174
+ VALUE Loop_signal(VALUE self) {
175
+ Loop_t *loop;
176
+ GetLoop(self, loop);
177
+
178
+ loop_signal(loop);
179
+ return self;
180
+ }
181
+
182
+ VALUE Loop_stop(VALUE self) {
183
+ Loop_t *loop;
184
+ GetLoop(self, loop);
185
+
186
+ loop->stop = 1;
187
+ loop_signal(loop);
188
+ return self;
189
+ }
190
+
191
+ VALUE Loop_watch_fd(VALUE self, VALUE key, VALUE fd, VALUE rw, VALUE oneshot) {
192
+ Loop_t *loop;
193
+ GetLoop(self, loop);
194
+
195
+ if (rb_hash_aref(loop->active_watchers, key) != Qnil)
196
+ rb_raise(rb_eRuntimeError, "Duplicate event key detected, event key must be unique");
197
+
198
+ loop_watch_fd(loop, key, NUM2INT(fd), sym_to_events(rw), RTEST(oneshot));
199
+ return self;
200
+ }
201
+
202
+ VALUE Loop_watch_io(VALUE self, VALUE key, VALUE io, VALUE rw, VALUE oneshot) {
203
+ Loop_t *loop;
204
+ GetLoop(self, loop);
205
+
206
+ if (rb_hash_aref(loop->active_watchers, key) != Qnil)
207
+ rb_raise(rb_eRuntimeError, "Duplicate event key detected, event key must be unique");
208
+
209
+ loop_watch_fd(loop, key, fd_from_io(io), sym_to_events(rw), RTEST(oneshot));
210
+ return self;
211
+ }
212
+
213
+ VALUE Loop_watch_timer(VALUE self, VALUE key, VALUE timeout, VALUE interval) {
214
+ Loop_t *loop;
215
+ GetLoop(self, loop);
216
+
217
+ if (rb_hash_aref(loop->active_watchers, key) != Qnil)
218
+ rb_raise(rb_eRuntimeError, "Duplicate event key detected, event key must be unique");
219
+
220
+ loop_watch_timer(loop, key, NUM2DBL(timeout), NUM2DBL(interval));
221
+ return self;
222
+ }
223
+
224
+ VALUE Loop_unwatch(VALUE self, VALUE key) {
225
+ Loop_t *loop;
226
+ GetLoop(self, loop);
227
+
228
+ VALUE watcher = rb_hash_delete(loop->active_watchers, key);
229
+ if (watcher == Qnil) return self;
230
+
231
+ Watcher_stop(watcher);
232
+ rb_ary_push(loop->free_watchers, watcher);
233
+
234
+ return self;
235
+ }
236
+
237
+ void Init_Loop() {
238
+ ev_set_allocator(xrealloc);
239
+
240
+ cLoop = rb_define_class_under(mEver, "Loop", rb_cObject);
241
+ rb_define_alloc_func(cLoop, Loop_allocate);
242
+
243
+ rb_define_method(cLoop, "initialize", Loop_initialize, 0);
244
+
245
+ rb_define_method(cLoop, "each", Loop_each, 0);
246
+ rb_define_method(cLoop, "next_event", Loop_next_event, 0);
247
+ rb_define_method(cLoop, "emit", Loop_emit, 1);
248
+ rb_define_method(cLoop, "signal", Loop_signal, 0);
249
+ rb_define_method(cLoop, "stop", Loop_stop, 0);
250
+ rb_define_method(cLoop, "watch_fd", Loop_watch_fd, 4);
251
+ rb_define_method(cLoop, "watch_io", Loop_watch_io, 4);
252
+ rb_define_method(cLoop, "watch_timer", Loop_watch_timer, 3);
253
+ rb_define_method(cLoop, "unwatch", Loop_unwatch, 1);
254
+
255
+ SYM_stop = ID2SYM(rb_intern("stop"));
256
+ ID_ivar_io = rb_intern("@io");
257
+ }
@@ -0,0 +1,137 @@
1
+ #include "ever.h"
2
+ #include "../libev/ev.h"
3
+
4
+ VALUE cWatcher;
5
+
6
+ enum watcher_type {
7
+ WATCHER_IO,
8
+ WATCHER_TIMER
9
+ };
10
+
11
+
12
+ typedef struct Watcher_t {
13
+ union {
14
+ struct ev_io io;
15
+ struct ev_timer timer;
16
+ };
17
+
18
+ Loop_t *loop;
19
+ enum watcher_type type;
20
+ VALUE key;
21
+ int oneshot;
22
+ } Watcher_t;
23
+
24
+ ////////////////////////////////////////////////////////////////////////////////
25
+
26
+ static void Watcher_mark(void *ptr) {
27
+ Watcher_t *watcher = ptr;
28
+ rb_gc_mark(watcher->key);
29
+ }
30
+
31
+ static void Watcher_free(void *ptr) {
32
+ xfree(ptr);
33
+ }
34
+
35
+ static size_t Watcher_size(const void *ptr) {
36
+ return sizeof(Watcher_t);
37
+ }
38
+
39
+ static const rb_data_type_t Watcher_type = {
40
+ "Ever::Watcher",
41
+ {Watcher_mark, Watcher_free, Watcher_size,},
42
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
43
+ };
44
+
45
+ static VALUE Watcher_allocate(VALUE klass) {
46
+ Watcher_t *watcher = ALLOC(Watcher_t);
47
+ return TypedData_Wrap_Struct(klass, &Watcher_type, watcher);
48
+ }
49
+
50
+ #define GetWatcher(obj, watcher) \
51
+ TypedData_Get_Struct((obj), Watcher_t, &Watcher_type, (watcher))
52
+
53
+ VALUE Watcher_initialize(VALUE self) {
54
+ return self;
55
+ }
56
+
57
+ ////////////////////////////////////////////////////////////////////////////////
58
+
59
+ inline void watcher_stop(Watcher_t *watcher) {
60
+ switch (watcher->type) {
61
+ case WATCHER_IO:
62
+ ev_io_stop(watcher->loop->ev_loop, &watcher->io);
63
+ return;
64
+ case WATCHER_TIMER:
65
+ ev_timer_stop(watcher->loop->ev_loop, &watcher->timer);
66
+ return;
67
+ }
68
+ }
69
+
70
+ void watcher_io_callback(EV_P_ ev_io *w, int revents)
71
+ {
72
+ Watcher_t *watcher = (Watcher_t *)w;
73
+ loop_emit(watcher->loop, watcher->key);
74
+ if (watcher->oneshot) {
75
+ watcher_stop(watcher);
76
+ loop_release_watcher(watcher->loop, watcher->key);
77
+ }
78
+ }
79
+
80
+ void watcher_timer_callback(EV_P_ ev_timer *w, int revents)
81
+ {
82
+ Watcher_t *watcher = (Watcher_t *)w;
83
+ loop_emit(watcher->loop, watcher->key);
84
+ if (watcher->oneshot) {
85
+ watcher_stop(watcher);
86
+ loop_release_watcher(watcher->loop, watcher->key);
87
+ }
88
+ }
89
+
90
+ static inline void watcher_setup_io(Watcher_t *watcher, Loop_t *loop, VALUE key, int fd, int events, int oneshot) {
91
+ watcher->type = WATCHER_IO;
92
+ watcher->loop = loop;
93
+ watcher->key = key;
94
+ watcher->oneshot = oneshot;
95
+
96
+ ev_io_init(&watcher->io, watcher_io_callback, fd, events);
97
+ ev_io_start(loop->ev_loop, &watcher->io);
98
+ }
99
+
100
+ static inline void watcher_setup_timer(Watcher_t *watcher, Loop_t *loop, VALUE key, double timeout, double interval) {
101
+ watcher->type = WATCHER_TIMER;
102
+ watcher->loop = loop;
103
+ watcher->key = key;
104
+ watcher->oneshot = interval == 0.;
105
+
106
+ ev_timer_init(&watcher->timer, watcher_timer_callback, timeout, interval);
107
+ ev_timer_start(loop->ev_loop, &watcher->timer);
108
+ }
109
+
110
+ ////////////////////////////////////////////////////////////////////////////////
111
+
112
+ inline void Watcher_setup_io(VALUE self, Loop_t *loop, VALUE key, int fd, int events, int oneshot) {
113
+ Watcher_t *watcher;
114
+ GetWatcher(self, watcher);
115
+
116
+ watcher_setup_io(watcher, loop, key, fd, events, oneshot);
117
+ }
118
+
119
+ inline void Watcher_setup_timer(VALUE self, Loop_t *loop, VALUE key, double timeout, double interval) {
120
+ Watcher_t *watcher;
121
+ GetWatcher(self, watcher);
122
+
123
+ watcher_setup_timer(watcher, loop, key, timeout, interval);
124
+ }
125
+
126
+ inline void Watcher_stop(VALUE self) {
127
+ Watcher_t *watcher;
128
+ GetWatcher(self, watcher);
129
+
130
+ watcher_stop(watcher);
131
+ }
132
+
133
+ void Init_Watcher() {
134
+ cWatcher = rb_define_class_under(mEver, "Watcher", rb_cObject);
135
+ rb_define_alloc_func(cWatcher, Watcher_allocate);
136
+ rb_define_method(cWatcher, "initialize", Watcher_initialize, 0);
137
+ }