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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4c45ef9b7d8ed527d6280ca836777fb2c54822e23c9414c0cdeb234561115f47
4
- data.tar.gz: 9543f98474500b00ae1a68009a261db1538d986e71bb7d9437bc9f190aff3c38
3
+ metadata.gz: e3b69b158f74a67ae86879e3a7019a32a1a6579bbf8cc707f7438ad68ffac2f4
4
+ data.tar.gz: 50aae949da244dde5e4f665e4eb08c2ea446a13a7f73bd3101a23000d9ac7a63
5
5
  SHA512:
6
- metadata.gz: d9fe5b68df1d9e72d10314744ac173bc4f99a3d420034d861b4d1685c1f36bfbe24c993c9686c5862d45926dd8ddb901759d913dfb0ab39d730c380bfa27f3d4
7
- data.tar.gz: cfbe9a5c112c71d96a737f29d9b4c5e1fd078b060331d999b7007a846b8e2a71f4de2eeebfc53e22dae8c33bf89afb15df5b36205b17f43f6f7e00096d3950f7
6
+ metadata.gz: 44b1a6e2b93d049f8c205910d7df2eb08221eb64a861b2e28870d39b0fac23fdc94a8ec100be574ba6bc63b991435068c7f79f3ce3fc4d13f101950974d903f7
7
+ data.tar.gz: e3db93cccddec122644ed83b249737ae35fbb18d70a8811e2e70f362fcfce3084be41b0a6421a966b407b63809888e72d528b640d4b2c636b2c95eaaa802210c
@@ -1,6 +1,25 @@
1
1
  ---
2
2
  language: ruby
3
3
  cache: bundler
4
- rvm:
5
- - ruby-head
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
- A low-level Event Handler designed for Ruby 3 Scheduler
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
+ ```
@@ -1,26 +1,20 @@
1
- #include <ruby.h>
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
- Scheduler = rb_define_class("Scheduler", rb_cObject);
14
- rb_define_method(Scheduler, "init_selector", method_scheduler_init, 0);
15
- rb_define_method(Scheduler, "register", method_scheduler_register, 2);
16
- rb_define_method(Scheduler, "deregister", method_scheduler_deregister, 1);
17
- rb_define_method(Scheduler, "wait", method_scheduler_wait, 0);
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 65535
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, next_timeout);
72
- // Check if n > 0
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
@@ -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
- require 'evt_ext'
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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Patch for 2.7.1 when tests
4
+ class IO
5
+ WAIT_READABLE = 1
6
+ WAIT_WRITABLE = 3
7
+ end
@@ -9,12 +9,7 @@ rescue LoadError
9
9
  # Ignore.
10
10
  end
11
11
 
12
- class IO
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Evt
2
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
3
5
  end
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.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-17 00:00:00.000000000 Z
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