ever 0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }