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 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