evt 0.1.1 → 0.1.2
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 +4 -4
- data/.travis.yml +21 -2
- data/README.md +25 -1
- data/ext/evt/evt.c +114 -18
- data/ext/evt/evt.h +11 -0
- data/lib/evt.rb +4 -3
- data/lib/evt/io.rb +7 -0
- data/lib/evt/scheduler.rb +1 -9
- data/lib/evt/version.rb +3 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3b69b158f74a67ae86879e3a7019a32a1a6579bbf8cc707f7438ad68ffac2f4
|
4
|
+
data.tar.gz: 50aae949da244dde5e4f665e4eb08c2ea446a13a7f73bd3101a23000d9ac7a63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44b1a6e2b93d049f8c205910d7df2eb08221eb64a861b2e28870d39b0fac23fdc94a8ec100be574ba6bc63b991435068c7f79f3ce3fc4d13f101950974d903f7
|
7
|
+
data.tar.gz: e3db93cccddec122644ed83b249737ae35fbb18d70a8811e2e70f362fcfce3084be41b0a6421a966b407b63809888e72d528b640d4b2c636b2c95eaaa802210c
|
data/.travis.yml
CHANGED
@@ -1,6 +1,25 @@
|
|
1
1
|
---
|
2
2
|
language: ruby
|
3
3
|
cache: bundler
|
4
|
-
rvm:
|
5
|
-
|
4
|
+
rvm: 2.7.1
|
5
|
+
matrix:
|
6
|
+
include:
|
7
|
+
- rvm: 2.7.1
|
8
|
+
os: linux
|
9
|
+
- rvm: 2.7.1
|
10
|
+
os: osx
|
11
|
+
- rvm: ruby-head
|
12
|
+
allow_failures:
|
13
|
+
- rvm: ruby-head
|
14
|
+
fast_finish: true
|
15
|
+
|
6
16
|
before_install: gem install bundler -v 2.1.4
|
17
|
+
script:
|
18
|
+
- gem list -l
|
19
|
+
- bundle exec rake compile
|
20
|
+
- bundle exec rake
|
21
|
+
|
22
|
+
bundler_args: --jobs 1 --retry 3
|
23
|
+
|
24
|
+
notifications:
|
25
|
+
email: false
|
data/README.md
CHANGED
@@ -1,2 +1,26 @@
|
|
1
1
|
# evt
|
2
|
-
|
2
|
+
|
3
|
+
[](https://travis-ci.org/dsh0416/evt)
|
4
|
+
|
5
|
+
A Handcrafted Low-Level Event Handler designed as Ruby 3 Scheduler.
|
6
|
+
|
7
|
+
Supports `epoll`, `kqueue`, IOCP (WIP), and Ruby `select` fallback.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require 'evt'
|
11
|
+
|
12
|
+
rd, wr = IO.pipe
|
13
|
+
Thread.current.scheduler = Evt::Scheduler.new
|
14
|
+
|
15
|
+
hit = 0
|
16
|
+
fiber = Fiber.new do
|
17
|
+
scheduler.wait_readable(rd)
|
18
|
+
hit += 1
|
19
|
+
end
|
20
|
+
|
21
|
+
wr.write('Hello World')
|
22
|
+
fiber.resume
|
23
|
+
Thread.current.scheduler.run
|
24
|
+
|
25
|
+
puts hit # => 1
|
26
|
+
```
|
data/ext/evt/evt.c
CHANGED
@@ -1,26 +1,20 @@
|
|
1
|
-
#include
|
2
|
-
|
3
|
-
VALUE Scheduler = Qnil;
|
4
|
-
|
5
|
-
void Init_evt_ext();
|
6
|
-
VALUE method_scheduler_init(VALUE self);
|
7
|
-
VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest);
|
8
|
-
VALUE method_scheduler_deregister(VALUE self, VALUE io);
|
9
|
-
VALUE method_scheduler_wait(VALUE self);
|
1
|
+
#include "evt.h"
|
10
2
|
|
11
3
|
void Init_evt_ext()
|
12
4
|
{
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
5
|
+
Evt = rb_define_module("Evt");
|
6
|
+
Scheduler = rb_define_class_under(Evt, "Scheduler", rb_cObject);
|
7
|
+
rb_define_singleton_method(Scheduler, "backend", method_scheduler_backend, 0);
|
8
|
+
rb_define_method(Scheduler, "init_selector", method_scheduler_init, 0);
|
9
|
+
rb_define_method(Scheduler, "register", method_scheduler_register, 2);
|
10
|
+
rb_define_method(Scheduler, "deregister", method_scheduler_deregister, 1);
|
11
|
+
rb_define_method(Scheduler, "wait", method_scheduler_wait, 0);
|
18
12
|
}
|
19
13
|
|
20
14
|
|
21
15
|
#if defined(__linux__) // TODO: Do more checks for using epoll
|
22
16
|
#include <sys/epoll.h>
|
23
|
-
#define EPOLL_MAX_EVENTS
|
17
|
+
#define EPOLL_MAX_EVENTS 64
|
24
18
|
|
25
19
|
VALUE method_scheduler_init(VALUE self) {
|
26
20
|
rb_iv_set(self, "@epfd", INT2NUM(epoll_create(1))); // Size of epoll is ignored after Linux 2.6.8.
|
@@ -56,7 +50,7 @@ VALUE method_scheduler_deregister(VALUE self, VALUE io) {
|
|
56
50
|
}
|
57
51
|
|
58
52
|
VALUE method_scheduler_wait(VALUE self) {
|
59
|
-
int n, epfd, i, event_flag;
|
53
|
+
int n, epfd, i, event_flag, timeout;
|
60
54
|
VALUE next_timeout, obj_io, readables, writables, result;
|
61
55
|
ID id_next_timeout = rb_intern("next_timeout");
|
62
56
|
ID id_push = rb_intern("push");
|
@@ -66,10 +60,16 @@ VALUE method_scheduler_wait(VALUE self) {
|
|
66
60
|
readables = rb_ary_new();
|
67
61
|
writables = rb_ary_new();
|
68
62
|
|
63
|
+
if (next_timeout == Qnil) {
|
64
|
+
timeout = -1;
|
65
|
+
} else {
|
66
|
+
timeout = NUM2INT(next_timeout);
|
67
|
+
}
|
68
|
+
|
69
69
|
struct epoll_event* events = (struct epoll_event*) xmalloc(sizeof(struct epoll_event) * EPOLL_MAX_EVENTS);
|
70
70
|
|
71
|
-
n = epoll_wait(epfd, events, EPOLL_MAX_EVENTS,
|
72
|
-
// Check if n
|
71
|
+
n = epoll_wait(epfd, events, EPOLL_MAX_EVENTS, timeout);
|
72
|
+
// TODO: Check if n >= 0
|
73
73
|
|
74
74
|
for (i = 0; i < n; i++) {
|
75
75
|
event_flag = events[i].events;
|
@@ -89,6 +89,98 @@ VALUE method_scheduler_wait(VALUE self) {
|
|
89
89
|
xfree(events);
|
90
90
|
return result;
|
91
91
|
}
|
92
|
+
|
93
|
+
VALUE method_scheduler_backend() {
|
94
|
+
return rb_str_new_cstr("epoll");
|
95
|
+
}
|
96
|
+
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
|
97
|
+
#include <sys/event.h>
|
98
|
+
#define KQUEUE_MAX_EVENTS 64
|
99
|
+
|
100
|
+
VALUE method_scheduler_init(VALUE self) {
|
101
|
+
rb_iv_set(self, "@kq", INT2NUM(kqueue()));
|
102
|
+
return Qnil;
|
103
|
+
}
|
104
|
+
|
105
|
+
VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
|
106
|
+
struct kevent event;
|
107
|
+
u_short event_flags = 0;
|
108
|
+
ID id_fileno = rb_intern("fileno");
|
109
|
+
int kq = NUM2INT(rb_iv_get(self, "@kq"));
|
110
|
+
int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
|
111
|
+
int ruby_interest = NUM2INT(interest);
|
112
|
+
int readable = NUM2INT(rb_const_get(rb_cIO, rb_intern("WAIT_READABLE")));
|
113
|
+
int writable = NUM2INT(rb_const_get(rb_cIO, rb_intern("WAIT_WRITABLE")));
|
114
|
+
|
115
|
+
if (ruby_interest & readable) {
|
116
|
+
event_flags |= EVFILT_READ;
|
117
|
+
} else if (ruby_interest & writable) {
|
118
|
+
event_flags |= EVFILT_WRITE;
|
119
|
+
}
|
120
|
+
|
121
|
+
EV_SET(&event, fd, event_flags, EV_ADD|EV_ENABLE, 0, 0, (void*) io);
|
122
|
+
kevent(kq, &event, 1, NULL, 0, NULL); // TODO: Check the return value
|
123
|
+
return Qnil;
|
124
|
+
}
|
125
|
+
|
126
|
+
VALUE method_scheduler_deregister(VALUE self, VALUE io) {
|
127
|
+
struct kevent event;
|
128
|
+
ID id_fileno = rb_intern("fileno");
|
129
|
+
int kq = NUM2INT(rb_iv_get(self, "@kq"));
|
130
|
+
int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
|
131
|
+
EV_SET(&event, fd, 0, EV_DELETE, 0, 0, NULL);
|
132
|
+
kevent(kq, &event, 1, NULL, 0, NULL); // TODO: Check the return value
|
133
|
+
return Qnil;
|
134
|
+
}
|
135
|
+
|
136
|
+
VALUE method_scheduler_wait(VALUE self) {
|
137
|
+
int n, kq, i;
|
138
|
+
u_short event_flags = 0;
|
139
|
+
|
140
|
+
struct kevent* events; // Event Triggered
|
141
|
+
struct timespec timeout;
|
142
|
+
VALUE next_timeout, obj_io, readables, writables, result;
|
143
|
+
ID id_next_timeout = rb_intern("next_timeout");
|
144
|
+
ID id_push = rb_intern("push");
|
145
|
+
|
146
|
+
kq = NUM2INT(rb_iv_get(self, "@kq"));
|
147
|
+
next_timeout = rb_funcall(self, id_next_timeout, 0);
|
148
|
+
readables = rb_ary_new();
|
149
|
+
writables = rb_ary_new();
|
150
|
+
|
151
|
+
events = (struct kevent*) xmalloc(sizeof(struct kevent) * KQUEUE_MAX_EVENTS);
|
152
|
+
|
153
|
+
if (next_timeout == Qnil || NUM2INT(next_timeout) == -1) {
|
154
|
+
n = kevent(kq, NULL, 0, events, KQUEUE_MAX_EVENTS, NULL);
|
155
|
+
} else {
|
156
|
+
timeout.tv_sec = next_timeout / 1000;
|
157
|
+
timeout.tv_nsec = next_timeout % 1000 * 1000 * 1000;
|
158
|
+
n = kevent(kq, NULL, 0, events, KQUEUE_MAX_EVENTS, &timeout);
|
159
|
+
}
|
160
|
+
|
161
|
+
// TODO: Check if n >= 0
|
162
|
+
for (i = 0; i < n; i++) {
|
163
|
+
event_flags = events[i].filter;
|
164
|
+
if (event_flags & EVFILT_READ) {
|
165
|
+
obj_io = (VALUE) events[i].udata;
|
166
|
+
rb_funcall(readables, id_push, 1, obj_io);
|
167
|
+
} else if (event_flags & EVFILT_WRITE) {
|
168
|
+
obj_io = (VALUE) events[i].udata;
|
169
|
+
rb_funcall(writables, id_push, 1, obj_io);
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
result = rb_ary_new2(2);
|
174
|
+
rb_ary_store(result, 0, readables);
|
175
|
+
rb_ary_store(result, 1, writables);
|
176
|
+
|
177
|
+
xfree(events);
|
178
|
+
return result;
|
179
|
+
}
|
180
|
+
|
181
|
+
VALUE method_scheduler_backend() {
|
182
|
+
return rb_str_new_cstr("kqueue");
|
183
|
+
}
|
92
184
|
#else
|
93
185
|
// Fallback to IO.select
|
94
186
|
VALUE method_scheduler_init(VALUE self) {
|
@@ -119,4 +211,8 @@ VALUE method_scheduler_wait(VALUE self) {
|
|
119
211
|
|
120
212
|
return rb_funcall(rb_cIO, id_select, 4, readable_keys, writable_keys, rb_ary_new(), next_timeout);
|
121
213
|
}
|
214
|
+
|
215
|
+
VALUE method_scheduler_backend() {
|
216
|
+
return rb_str_new_cstr("ruby");
|
217
|
+
}
|
122
218
|
#endif
|
data/ext/evt/evt.h
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
|
3
|
+
VALUE Evt = Qnil;
|
4
|
+
VALUE Scheduler = Qnil;
|
5
|
+
|
6
|
+
void Init_evt_ext();
|
7
|
+
VALUE method_scheduler_init(VALUE self);
|
8
|
+
VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest);
|
9
|
+
VALUE method_scheduler_deregister(VALUE self, VALUE io);
|
10
|
+
VALUE method_scheduler_wait(VALUE self);
|
11
|
+
VALUE method_scheduler_backend();
|
data/lib/evt.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'evt/version'
|
2
|
-
|
4
|
+
require_relative 'evt/io'
|
5
|
+
require_relative 'evt_ext'
|
3
6
|
require_relative 'evt/scheduler'
|
4
7
|
|
5
8
|
module Evt
|
6
|
-
class Error < StandardError; end
|
7
|
-
# Your code goes here...
|
8
9
|
end
|
data/lib/evt/io.rb
ADDED
data/lib/evt/scheduler.rb
CHANGED
@@ -9,12 +9,7 @@ rescue LoadError
|
|
9
9
|
# Ignore.
|
10
10
|
end
|
11
11
|
|
12
|
-
class
|
13
|
-
WAIT_READABLE = 1
|
14
|
-
WAIT_WRITABLE = 3
|
15
|
-
end
|
16
|
-
|
17
|
-
class Scheduler
|
12
|
+
class Evt::Scheduler
|
18
13
|
def initialize
|
19
14
|
@readable = {}
|
20
15
|
@writable = {}
|
@@ -46,7 +41,6 @@ class Scheduler
|
|
46
41
|
|
47
42
|
def run
|
48
43
|
while @readable.any? or @writable.any? or @waiting.any?
|
49
|
-
# Can only handle file descriptors up to 1024...
|
50
44
|
readable, writable = self.wait
|
51
45
|
|
52
46
|
# puts "readable: #{readable}" if readable&.any?
|
@@ -162,9 +156,7 @@ class Scheduler
|
|
162
156
|
|
163
157
|
def fiber(&block)
|
164
158
|
fiber = Fiber.new(blocking: false, &block)
|
165
|
-
|
166
159
|
fiber.resume
|
167
|
-
|
168
160
|
return fiber
|
169
161
|
end
|
170
162
|
end
|
data/lib/evt/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: evt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delton Ding
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -42,8 +42,10 @@ files:
|
|
42
42
|
- Rakefile
|
43
43
|
- evt.gemspec
|
44
44
|
- ext/evt/evt.c
|
45
|
+
- ext/evt/evt.h
|
45
46
|
- ext/evt/extconf.rb
|
46
47
|
- lib/evt.rb
|
48
|
+
- lib/evt/io.rb
|
47
49
|
- lib/evt/scheduler.rb
|
48
50
|
- lib/evt/version.rb
|
49
51
|
homepage: https://github.com/dsh0416/evt
|