evt 0.1.2 → 0.2.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: e3b69b158f74a67ae86879e3a7019a32a1a6579bbf8cc707f7438ad68ffac2f4
4
- data.tar.gz: 50aae949da244dde5e4f665e4eb08c2ea446a13a7f73bd3101a23000d9ac7a63
3
+ metadata.gz: 2a40fa492b119e2e67823ab476e39454de3027efbfab820c5d38a4562314f43a
4
+ data.tar.gz: 357b5b8d9c00ca7ed7df013aa6826e5ec85e72f329009272eaf985d7db519abf
5
5
  SHA512:
6
- metadata.gz: 44b1a6e2b93d049f8c205910d7df2eb08221eb64a861b2e28870d39b0fac23fdc94a8ec100be574ba6bc63b991435068c7f79f3ce3fc4d13f101950974d903f7
7
- data.tar.gz: e3db93cccddec122644ed83b249737ae35fbb18d70a8811e2e70f362fcfce3084be41b0a6421a966b407b63809888e72d528b640d4b2c636b2c95eaaa802210c
6
+ metadata.gz: '09cf37c36d93a8fa0144a324934055ede6063203f506af41fcd6a2dd8ebeb0509ff5996726ab92badac63e020b1b56301d18eed967c7fa830286aafde1b38001'
7
+ data.tar.gz: 869b7f1d95960362d2049ebfddb3a18fdad4a2558f64d27f7e5306f57025592a2402086f2ded46291a95f0fd4fe200f9040e83a449e29a61e0245e25e8f8d1ad
@@ -0,0 +1,54 @@
1
+ name: CI Tests
2
+ on:
3
+ pull_request:
4
+ push:
5
+ branches:
6
+ - master
7
+ schedule:
8
+ - cron: '0 7 * * SUN'
9
+ jobs:
10
+ test:
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ include:
15
+ - { os: ubuntu-20.04, ruby: '3.0', backend: epoll }
16
+ - { os: ubuntu-20.04, ruby: '3.0', backend: ruby, disable-epoll: 1 }
17
+ - { os: macos-11.0, ruby: '3.0', backend: kqueue }
18
+ - { os: macos-11.0, ruby: '3.0', backend: ruby, disable-kqueue: 1 }
19
+ - { os: windows-2019, ruby: mingw, backend: ruby }
20
+ - { os: windows-2019, ruby: mswin, backend: ruby }
21
+ name: test ${{ matrix.os }} ${{ matrix.ruby }} ${{ matrix.backend }}
22
+ runs-on: ${{ matrix.os }}
23
+ timeout-minutes: 5
24
+ steps:
25
+ - uses: actions/checkout@v2
26
+ - uses: ruby/setup-ruby@master
27
+ with:
28
+ ruby-version: ${{ matrix.ruby }}
29
+ bundler-cache: false
30
+ - name: Install Dependencies
31
+ run: |
32
+ gem install bundler
33
+ bundle install --jobs 4 --retry 3
34
+ - name: Compile
35
+ env:
36
+ DISABLE_EPOLL: ${{ matrix.disable-epoll }}
37
+ DISABLE_KQUEUE: ${{ matrix.disable-kqueue }}
38
+ run: rake compile
39
+ - name: Test
40
+ run: rake
41
+ build:
42
+ runs-on: ubuntu-20.04
43
+ steps:
44
+ - uses: actions/checkout@v2
45
+ - uses: ruby/setup-ruby@master
46
+ with:
47
+ ruby-version: '3.0'
48
+ bundler-cache: false
49
+ - name: Install Dependencies
50
+ run: |
51
+ gem install bundler
52
+ bundle install --jobs 4 --retry 3
53
+ - name: Build
54
+ run: gem build evt.gemspec
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
  /*.gem
10
10
  /lib/*.bundle
11
11
  /lib/*.so
12
+ Gemfile.lock
data/Gemfile CHANGED
@@ -3,5 +3,5 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in evt.gemspec
4
4
  gemspec
5
5
 
6
- gem "rake", "~> 12.0"
6
+ gem "rake", "~> 13.0"
7
7
  gem "minitest", "~> 5.0"
data/README.md CHANGED
@@ -1,26 +1,88 @@
1
- # evt
1
+ # Evt
2
2
 
3
- [![Build Status](https://travis-ci.org/dsh0416/evt.svg?branch=master)](https://travis-ci.org/dsh0416/evt)
3
+ The Event Library that designed for Ruby 3.0 Fiber Scheduler.
4
4
 
5
- A Handcrafted Low-Level Event Handler designed as Ruby 3 Scheduler.
5
+ **This gem is still under development, APIs and features are not stable. Advices and PRs are highly welcome.**
6
6
 
7
- Supports `epoll`, `kqueue`, IOCP (WIP), and Ruby `select` fallback.
7
+ [![CI Tests](https://github.com/dsh0416/evt/workflows/CI%20Tests/badge.svg)](https://github.com/dsh0416/evt/actions?query=workflow%3A%22CI+Tests%22)
8
+ [![Gem Version](https://badge.fury.io/rb/evt.svg)](https://rubygems.org/gems/evt)
9
+ [![Downloads](https://ruby-gem-downloads-badge.herokuapp.com/evt?type=total)](https://rubygems.org/gems/evt)
10
+
11
+ ## Features
12
+
13
+ ### IO Backend Support
14
+
15
+ | | Linux | Windows | macOS | FreeBSD |
16
+ | --------------- | ----------- | ------------| ----------- | ----------- |
17
+ | io_uring | ✅ (See 1) | ❌ | ❌ | ❌ |
18
+ | epoll | ✅ (See 2) | ❌ | ❌ | ❌ |
19
+ | kqueue | ❌ | ❌ | ✅ (⚠️ See 5) | ✅ |
20
+ | IOCP | ❌ | ❌ (⚠️See 3) | ❌ | ❌ |
21
+ | Ruby (`select`) | ✅ Fallback | ✅ (⚠️See 4) | ✅ Fallback | ✅ Fallback |
22
+
23
+ 1. when liburing is installed
24
+ 2. when kernel version >= 2.6.8
25
+ 3. WOULD NOT WORK until `FILE_FLAG_OVERLAPPED` is included in I/O initialization process.
26
+ 4. Some I/Os are not able to be nonblock under Windows. See [Scheduler Docs](https://docs.ruby-lang.org/en/master/doc/scheduler_md.html#label-IO).
27
+ 5. `kqueue` performance in Darwin is very poor. **MAY BE DISABLED IN THE FUTURE.**
28
+
29
+ ### Benchmark
30
+
31
+ The benchmark is running under `v0.2.2` version. See [evt-server-benchmark](https://github.com/dsh0416/evt-server-benchmark) for test code, the test is running under a single-thread server.
32
+
33
+ The test command is `wrk -t4 -c8192 -d30s http://localhost:3001`.
34
+
35
+ All of the systems have set their file descriptor limit to maximum.
36
+
37
+ | OS | CPU | Memory | Backend | req/s |
38
+ | ----- | ----------- | ------ | ---------------------- | -------- |
39
+ | Linux | Ryzen 2700x | 64GB | epoll | 54680.08 |
40
+ | Linux | Ryzen 2700x | 64GB | io_uring | 50245.53 |
41
+ | Linux | Ryzen 2700x | 64GB | IO.select (using poll) | 44159.23 |
42
+ | macOS | i7-6820HQ | 16GB | kqueue | 37855.53 |
43
+ | macOS | i7-6820HQ | 16GB | IO.select (using poll) | 28293.36 |
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ gem install evt
49
+ ```
50
+
51
+ ## Usage
8
52
 
9
53
  ```ruby
10
54
  require 'evt'
11
55
 
12
56
  rd, wr = IO.pipe
13
- Thread.current.scheduler = Evt::Scheduler.new
57
+ scheduler = Evt::Scheduler.new
58
+
59
+ Fiber.set_scheduler scheduler
14
60
 
15
- hit = 0
16
- fiber = Fiber.new do
17
- scheduler.wait_readable(rd)
18
- hit += 1
61
+ Fiber.schedule do
62
+ message = rd.read(20)
63
+ puts message
64
+ rd.close
19
65
  end
20
66
 
21
- wr.write('Hello World')
22
- fiber.resume
23
- Thread.current.scheduler.run
67
+ Fiber.schedule do
68
+ wr.write("Hello World")
69
+ wr.close
70
+ end
71
+
72
+ scheduler.run
24
73
 
25
- puts hit # => 1
74
+ # "Hello World"
26
75
  ```
76
+
77
+ ## Roadmap
78
+
79
+ - [x] Support epoll/kqueue/select
80
+ - [x] Upgrade to the latest Scheduler API
81
+ - [x] Support io_uring
82
+ - [x] Support iov features of io_uring
83
+ - [x] Support IOCP (**NOT ENABLED YET**)
84
+ - [x] Setup tests with Ruby 3
85
+ - [x] Selectable backend compilation by environment variable
86
+ - [ ] Support IOCP with iov features
87
+ - [ ] Setup more tests for production purpose
88
+ - [ ] Documentation for usages
@@ -6,11 +6,11 @@ Gem::Specification.new do |spec|
6
6
  spec.authors = ["Delton Ding"]
7
7
  spec.email = ["dsh0416@gmail.com"]
8
8
 
9
- spec.summary = "A low-level Event Handler designed for Ruby 3 Scheduler"
9
+ spec.summary = "The Event Library that designed for Ruby 3.0 Fiber Scheluer."
10
10
  spec.description = "A low-level Event Handler designed for Ruby 3 Scheduler for better performance"
11
11
  spec.homepage = "https://github.com/dsh0416/evt"
12
12
  spec.license = 'BSD-3-Clause'
13
- spec.required_ruby_version = '>= 2.7.1'
13
+ spec.required_ruby_version = '>= 3.0.0.rc1'
14
14
 
15
15
  spec.metadata["homepage_uri"] = spec.homepage
16
16
  spec.metadata["source_code_uri"] = "https://github.com/dsh0416/evt"
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  # Specify which files should be added to the gem when it is released.
19
19
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
20
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
21
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|.vscode)/}) }
22
22
  end
23
23
  spec.require_paths = ["lib"]
24
24
  spec.extensions = ['ext/evt/extconf.rb']
@@ -0,0 +1,89 @@
1
+ #ifndef EPOLL_H
2
+ #define EPOLL_G
3
+ #include "evt.h"
4
+
5
+ #if HAVE_SYS_EPOLL_H
6
+ VALUE method_scheduler_init(VALUE self) {
7
+ rb_iv_set(self, "@epfd", INT2NUM(epoll_create(1))); // Size of epoll is ignored after Linux 2.6.8.
8
+ return Qnil;
9
+ }
10
+
11
+ VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
12
+ struct epoll_event event;
13
+ ID id_fileno = rb_intern("fileno");
14
+ int epfd = NUM2INT(rb_iv_get(self, "@epfd"));
15
+ int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
16
+ int ruby_interest = NUM2INT(interest);
17
+ int readable = NUM2INT(rb_const_get(rb_cIO, rb_intern("READABLE")));
18
+ int writable = NUM2INT(rb_const_get(rb_cIO, rb_intern("WRITABLE")));
19
+
20
+ if (ruby_interest & readable) {
21
+ event.events |= EPOLLIN;
22
+ }
23
+
24
+ if (ruby_interest & writable) {
25
+ event.events |= EPOLLOUT;
26
+ }
27
+
28
+ event.data.ptr = (void*) io;
29
+
30
+ epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
31
+ return Qnil;
32
+ }
33
+
34
+ VALUE method_scheduler_deregister(VALUE self, VALUE io) {
35
+ ID id_fileno = rb_intern("fileno");
36
+ int epfd = NUM2INT(rb_iv_get(self, "@epfd"));
37
+ int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
38
+ epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); // Require Linux 2.6.9 for NULL event.
39
+ return Qnil;
40
+ }
41
+
42
+ VALUE method_scheduler_wait(VALUE self) {
43
+ int n, epfd, i, event_flag, timeout;
44
+ VALUE next_timeout, obj_io, readables, writables, result;
45
+ ID id_next_timeout = rb_intern("next_timeout");
46
+ ID id_push = rb_intern("push");
47
+
48
+ epfd = NUM2INT(rb_iv_get(self, "@epfd"));
49
+ next_timeout = rb_funcall(self, id_next_timeout, 0);
50
+ readables = rb_ary_new();
51
+ writables = rb_ary_new();
52
+
53
+ if (next_timeout == Qnil) {
54
+ timeout = -1;
55
+ } else {
56
+ timeout = NUM2INT(next_timeout);
57
+ }
58
+
59
+ struct epoll_event events[EPOLL_MAX_EVENTS];
60
+
61
+ n = epoll_wait(epfd, events, EPOLL_MAX_EVENTS, timeout);
62
+ if (n < 0) {
63
+ rb_raise(rb_eIOError, "unable to call epoll_wait");
64
+ }
65
+
66
+ for (i = 0; i < n; i++) {
67
+ event_flag = events[i].events;
68
+ if (event_flag & EPOLLIN) {
69
+ obj_io = (VALUE) events[i].data.ptr;
70
+ rb_funcall(readables, id_push, 1, obj_io);
71
+ }
72
+
73
+ if (event_flag & EPOLLOUT) {
74
+ obj_io = (VALUE) events[i].data.ptr;
75
+ rb_funcall(writables, id_push, 1, obj_io);
76
+ }
77
+ }
78
+
79
+ result = rb_ary_new2(2);
80
+ rb_ary_store(result, 0, readables);
81
+ rb_ary_store(result, 1, writables);
82
+ return result;
83
+ }
84
+
85
+ VALUE method_scheduler_backend(VALUE klass) {
86
+ return rb_str_new_cstr("epoll");
87
+ }
88
+ #endif
89
+ #endif
@@ -1,218 +1,36 @@
1
+ #ifndef EVT_C
2
+ #define EVT_C
3
+
1
4
  #include "evt.h"
2
5
 
3
6
  void Init_evt_ext()
4
7
  {
5
8
  Evt = rb_define_module("Evt");
6
9
  Scheduler = rb_define_class_under(Evt, "Scheduler", rb_cObject);
10
+ Payload = rb_define_class_under(Scheduler, "Payload", rb_cObject);
11
+ Fiber = rb_define_class("Fiber", rb_cObject);
7
12
  rb_define_singleton_method(Scheduler, "backend", method_scheduler_backend, 0);
8
13
  rb_define_method(Scheduler, "init_selector", method_scheduler_init, 0);
9
14
  rb_define_method(Scheduler, "register", method_scheduler_register, 2);
10
15
  rb_define_method(Scheduler, "deregister", method_scheduler_deregister, 1);
11
16
  rb_define_method(Scheduler, "wait", method_scheduler_wait, 0);
12
- }
13
-
14
-
15
- #if defined(__linux__) // TODO: Do more checks for using epoll
16
- #include <sys/epoll.h>
17
- #define EPOLL_MAX_EVENTS 64
18
-
19
- VALUE method_scheduler_init(VALUE self) {
20
- rb_iv_set(self, "@epfd", INT2NUM(epoll_create(1))); // Size of epoll is ignored after Linux 2.6.8.
21
- return Qnil;
22
- }
23
-
24
- VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
25
- struct epoll_event event;
26
- ID id_fileno = rb_intern("fileno");
27
- int epfd = NUM2INT(rb_iv_get(self, "@epfd"));
28
- int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
29
- int ruby_interest = NUM2INT(interest);
30
- int readable = NUM2INT(rb_const_get(rb_cIO, rb_intern("WAIT_READABLE")));
31
- int writable = NUM2INT(rb_const_get(rb_cIO, rb_intern("WAIT_WRITABLE")));
32
-
33
- if (ruby_interest & readable) {
34
- event.events |= EPOLLIN;
35
- } else if (ruby_interest & writable) {
36
- event.events |= EPOLLOUT;
37
- }
38
- event.data.ptr = (void*) io;
39
-
40
- epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
41
- return Qnil;
42
- }
43
-
44
- VALUE method_scheduler_deregister(VALUE self, VALUE io) {
45
- ID id_fileno = rb_intern("fileno");
46
- int epfd = NUM2INT(rb_iv_get(self, "@epfd"));
47
- int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
48
- epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); // Require Linux 2.6.9 for NULL event.
49
- return Qnil;
50
- }
51
-
52
- VALUE method_scheduler_wait(VALUE self) {
53
- int n, epfd, i, event_flag, timeout;
54
- VALUE next_timeout, obj_io, readables, writables, result;
55
- ID id_next_timeout = rb_intern("next_timeout");
56
- ID id_push = rb_intern("push");
57
-
58
- epfd = NUM2INT(rb_iv_get(self, "@epfd"));
59
- next_timeout = rb_funcall(self, id_next_timeout, 0);
60
- readables = rb_ary_new();
61
- writables = rb_ary_new();
62
-
63
- if (next_timeout == Qnil) {
64
- timeout = -1;
65
- } else {
66
- timeout = NUM2INT(next_timeout);
67
- }
68
-
69
- struct epoll_event* events = (struct epoll_event*) xmalloc(sizeof(struct epoll_event) * EPOLL_MAX_EVENTS);
70
-
71
- n = epoll_wait(epfd, events, EPOLL_MAX_EVENTS, timeout);
72
- // TODO: Check if n >= 0
73
-
74
- for (i = 0; i < n; i++) {
75
- event_flag = events[i].events;
76
- if (event_flag & EPOLLIN) {
77
- obj_io = (VALUE) events[i].data.ptr;
78
- rb_funcall(readables, id_push, 1, obj_io);
79
- } else if (event_flag & EPOLLOUT) {
80
- obj_io = (VALUE) events[i].data.ptr;
81
- rb_funcall(writables, id_push, 1, obj_io);
82
- }
83
- }
84
-
85
- result = rb_ary_new2(2);
86
- rb_ary_store(result, 0, readables);
87
- rb_ary_store(result, 1, writables);
88
-
89
- xfree(events);
90
- return result;
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
17
 
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
- }
18
+ #if HAVELIBURING_H
19
+ rb_define_method(Scheduler, "io_read", method_scheduler_io_read, 4);
20
+ rb_define_method(Scheduler, "io_write", method_scheduler_io_read, 4);
21
+ #endif
22
+ }
23
+
24
+ #if HAVE_LIBURING_H
25
+ #include "uring.h"
26
+ #elif HAVE_SYS_EPOLL_H
27
+ #include "epoll.h"
28
+ #elif HAVE_SYS_EVENT_H
29
+ #include "kqueue.h"
30
+ #elif HAVE_WINDOWS_H
31
+ #include "select.h"
32
+ // #include "iocp.h"
184
33
  #else
185
- // Fallback to IO.select
186
- VALUE method_scheduler_init(VALUE self) {
187
- return Qnil;
188
- }
189
-
190
- VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
191
- return Qnil;
192
- }
193
-
194
- VALUE method_scheduler_deregister(VALUE self, VALUE io) {
195
- return Qnil;
196
- }
197
-
198
- VALUE method_scheduler_wait(VALUE self) {
199
- // return IO.select(@readable.keys, @writable.keys, [], next_timeout)
200
- VALUE readable, writable, readable_keys, writable_keys, next_timeout;
201
- ID id_select = rb_intern("select");
202
- ID id_keys = rb_intern("keys");
203
- ID id_next_timeout = rb_intern("next_timeout");
204
-
205
- readable = rb_iv_get(self, "@readable");
206
- writable = rb_iv_get(self, "@writable");
207
-
208
- readable_keys = rb_funcall(readable, id_keys, 0);
209
- writable_keys = rb_funcall(writable, id_keys, 0);
210
- next_timeout = rb_funcall(self, id_next_timeout, 0);
211
-
212
- return rb_funcall(rb_cIO, id_select, 4, readable_keys, writable_keys, rb_ary_new(), next_timeout);
213
- }
214
-
215
- VALUE method_scheduler_backend() {
216
- return rb_str_new_cstr("ruby");
217
- }
218
- #endif
34
+ #include "select.h"
35
+ #endif
36
+ #endif