evt 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/dsh0416/evt.svg?branch=master)](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
|