evt 0.2.3 → 0.3.0

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: bcbae12e6f731dbbd29a341c60bbb41ef1fabb6095b0bc581f1bce0a51d6a729
4
- data.tar.gz: 46497ea730a052a6c8e0a390fa031e8b81dd7b2eb84f05152e85df286db9f20a
3
+ metadata.gz: f49f37d3240b1878b00acaeea453e00c9dae2a96c8c7f12889836d653d6b1835
4
+ data.tar.gz: 2af4b0238768dcb2b4112eaff379cad3899633eab69017517f36d5d51ba1fae7
5
5
  SHA512:
6
- metadata.gz: 782544e4c98db9600fbacdeef1fdf85748494ed75e7ca4edbc154d483bf09da6ce4ff764600695368e6b7e47c26c0415523d303f74f1174b8c6c41e5130ac6b7
7
- data.tar.gz: e763ec572a4289c1c14ed83503502ffa4ffd346a239e30510fe699eb46e72cbf5fad07e67caa3f8b416a5a42c2851bc483cc81d418e9dd02550afac39d8751a2
6
+ metadata.gz: 58222d04ff91f47667697fd63d6d235d85c8204e09a82ac18dbd05b27d37eb79d6bcda034117684e2d49cd0cd67d2818df5264a4f73751851f1ebefdbe3a9de2
7
+ data.tar.gz: 17c54e28beb805e30a3883446a807c5114287ba3c37b402a85dcba013f2910e8aca7d29c17efe527dd050a971f224ff0e8f4f2aacd36ef8cbe7970ff8aa06d56
@@ -0,0 +1,21 @@
1
+ name: Build
2
+ on:
3
+ pull_request:
4
+ push:
5
+ branches:
6
+ - master
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-20.04
10
+ steps:
11
+ - uses: actions/checkout@v2
12
+ - uses: ruby/setup-ruby@master
13
+ with:
14
+ ruby-version: '3.0'
15
+ bundler-cache: false
16
+ - name: Install Dependencies
17
+ run: |
18
+ gem install bundler
19
+ bundle install --jobs 4 --retry 3
20
+ - name: Build
21
+ run: rake build
@@ -37,18 +37,4 @@ jobs:
37
37
  DISABLE_KQUEUE: ${{ matrix.disable-kqueue }}
38
38
  run: rake compile
39
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
40
+ run: rake test
data/README.md CHANGED
@@ -4,7 +4,8 @@ The Event Library that designed for Ruby 3.0 Fiber Scheduler.
4
4
 
5
5
  **This gem is still under development, APIs and features are not stable. Advices and PRs are highly welcome.**
6
6
 
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)
7
+ [![CI Tests](https://github.com/dsh0416/evt/workflows/CI%20Tests/badge.svg)](https://github.com/dsh0416/evt/actions?query=workflow%3A%22Build%22)
8
+ [![Build](https://github.com/dsh0416/evt/workflows/Build/badge.svg)](https://github.com/dsh0416/evt/actions?query=workflow%3A%22CI+Tests%22)
8
9
  [![Gem Version](https://badge.fury.io/rb/evt.svg)](https://rubygems.org/gems/evt)
9
10
  [![Downloads](https://ruby-gem-downloads-badge.herokuapp.com/evt?type=total)](https://rubygems.org/gems/evt)
10
11
 
@@ -69,8 +70,6 @@ Fiber.schedule do
69
70
  wr.close
70
71
  end
71
72
 
72
- scheduler.run
73
-
74
73
  # "Hello World"
75
74
  ```
76
75
 
data/Rakefile CHANGED
@@ -11,6 +11,7 @@ Rake::TestTask.new(:test) do |t|
11
11
  t.libs << "test"
12
12
  t.libs << "lib"
13
13
  t.test_files = FileList["test/**/*_test.rb"]
14
+ t.verbose = true
14
15
  end
15
16
 
16
17
  task :default => :test
@@ -18,10 +18,11 @@ 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|.vscode)/}) }
21
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|examples|.vscode)/}) }
22
22
  end
23
23
  spec.require_paths = ["lib"]
24
24
  spec.extensions = ['ext/evt/extconf.rb']
25
25
 
26
26
  spec.add_development_dependency 'rake-compiler', '~> 1.0'
27
+ spec.add_development_dependency 'simplecov', '~> 0.20.0'
27
28
  end
@@ -1,14 +1,14 @@
1
1
  #ifndef EPOLL_H
2
- #define EPOLL_G
2
+ #define EPOLL_H
3
3
  #include "evt.h"
4
4
 
5
5
  #if HAVE_SYS_EPOLL_H
6
- VALUE method_scheduler_init(VALUE self) {
6
+ VALUE method_scheduler_epoll_init(VALUE self) {
7
7
  rb_iv_set(self, "@epfd", INT2NUM(epoll_create(1))); // Size of epoll is ignored after Linux 2.6.8.
8
8
  return Qnil;
9
9
  }
10
10
 
11
- VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
11
+ VALUE method_scheduler_epoll_register(VALUE self, VALUE io, VALUE interest) {
12
12
  struct epoll_event event;
13
13
  ID id_fileno = rb_intern("fileno");
14
14
  int epfd = NUM2INT(rb_iv_get(self, "@epfd"));
@@ -31,7 +31,7 @@ VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
31
31
  return Qnil;
32
32
  }
33
33
 
34
- VALUE method_scheduler_deregister(VALUE self, VALUE io) {
34
+ VALUE method_scheduler_epoll_deregister(VALUE self, VALUE io) {
35
35
  ID id_fileno = rb_intern("fileno");
36
36
  int epfd = NUM2INT(rb_iv_get(self, "@epfd"));
37
37
  int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
@@ -39,7 +39,7 @@ VALUE method_scheduler_deregister(VALUE self, VALUE io) {
39
39
  return Qnil;
40
40
  }
41
41
 
42
- VALUE method_scheduler_wait(VALUE self) {
42
+ VALUE method_scheduler_epoll_wait(VALUE self) {
43
43
  int n, epfd, i, event_flag, timeout;
44
44
  VALUE next_timeout, obj_io, readables, writables, result;
45
45
  ID id_next_timeout = rb_intern("next_timeout");
@@ -82,7 +82,7 @@ VALUE method_scheduler_wait(VALUE self) {
82
82
  return result;
83
83
  }
84
84
 
85
- VALUE method_scheduler_backend(VALUE klass) {
85
+ VALUE method_scheduler_epoll_backend(VALUE klass) {
86
86
  return rb_str_new_cstr("epoll");
87
87
  }
88
88
  #endif
@@ -9,31 +9,37 @@ void Init_evt_ext()
9
9
  rb_ext_ractor_safe(true);
10
10
  #endif
11
11
  Evt = rb_define_module("Evt");
12
- Scheduler = rb_define_class_under(Evt, "Scheduler", rb_cObject);
13
- Payload = rb_define_class_under(Scheduler, "Payload", rb_cObject);
12
+ Bundled = rb_define_class_under(Evt, "Bundled", rb_cObject);
13
+ Payload = rb_define_class_under(Bundled, "Payload", rb_cObject);
14
14
  Fiber = rb_define_class("Fiber", rb_cObject);
15
- rb_define_singleton_method(Scheduler, "backend", method_scheduler_backend, 0);
16
- rb_define_method(Scheduler, "init_selector", method_scheduler_init, 0);
17
- rb_define_method(Scheduler, "register", method_scheduler_register, 2);
18
- rb_define_method(Scheduler, "deregister", method_scheduler_deregister, 1);
19
- rb_define_method(Scheduler, "wait", method_scheduler_wait, 0);
20
-
21
- #if HAVELIBURING_H
22
- rb_define_method(Scheduler, "io_read", method_scheduler_io_read, 4);
23
- rb_define_method(Scheduler, "io_write", method_scheduler_io_write, 4);
15
+ #if HAVE_LIBURING_H
16
+ rb_define_singleton_method(Bundled, "uring_backend", method_scheduler_uring_backend, 0);
17
+ rb_define_method(Bundled, "uring_init_selector", method_scheduler_uring_init, 0);
18
+ rb_define_method(Bundled, "uring_register", method_scheduler_uring_register, 2);
19
+ rb_define_method(Bundled, "uring_wait", method_scheduler_uring_wait, 0);
20
+ rb_define_method(Bundled, "uring_io_read", method_scheduler_uring_io_read, 4);
21
+ rb_define_method(Bundled, "uring_io_write", method_scheduler_uring_io_write, 4);
22
+ #endif
23
+ #if HAVE_SYS_EPOLL_H
24
+ rb_define_singleton_method(Bundled, "epoll_backend", method_scheduler_epoll_backend, 0);
25
+ rb_define_method(Bundled, "epoll_init_selector", method_scheduler_epoll_init, 0);
26
+ rb_define_method(Bundled, "epoll_register", method_scheduler_epoll_register, 2);
27
+ rb_define_method(Bundled, "epoll_deregister", method_scheduler_epoll_deregister, 1);
28
+ rb_define_method(Bundled, "epoll_wait", method_scheduler_epoll_wait, 0);
24
29
  #endif
30
+ #if HAVE_SYS_EVENT_H
31
+ rb_define_singleton_method(Bundled, "kqueue_backend", method_scheduler_kqueue_backend, 0);
32
+ rb_define_method(Bundled, "kqueue_init_selector", method_scheduler_kqueue_init, 0);
33
+ rb_define_method(Bundled, "kqueue_register", method_scheduler_kqueue_register, 2);
34
+ rb_define_method(Bundled, "kqueue_wait", method_scheduler_kqueue_wait, 0);
35
+ #endif
36
+ rb_define_singleton_method(Bundled, "select_backend", method_scheduler_select_backend, 0);
37
+ rb_define_method(Bundled, "select_wait", method_scheduler_select_wait, 0);
25
38
  }
26
39
 
27
- #if HAVE_LIBURING_H
28
- #include "uring.h"
29
- #elif HAVE_SYS_EPOLL_H
30
- #include "epoll.h"
31
- #elif HAVE_SYS_EVENT_H
32
- #include "kqueue.h"
33
- #elif HAVE_WINDOWS_H
34
- #include "select.h"
35
- // #include "iocp.h"
36
- #else
37
- #include "select.h"
38
- #endif
40
+ #include "uring.h"
41
+ #include "epoll.h"
42
+ #include "kqueue.h"
43
+ // #include "iocp.h"
44
+ #include "select.h"
39
45
  #endif
@@ -4,29 +4,22 @@
4
4
  #include <ruby.h>
5
5
 
6
6
  VALUE Evt = Qnil;
7
- VALUE Scheduler = Qnil;
7
+ VALUE Bundled = Qnil;
8
8
  VALUE Payload = Qnil;
9
9
  VALUE Fiber = Qnil;
10
10
 
11
11
  void Init_evt_ext();
12
- VALUE method_scheduler_init(VALUE self);
13
- VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest);
14
- VALUE method_scheduler_deregister(VALUE self, VALUE io);
15
- VALUE method_scheduler_wait(VALUE self);
16
- VALUE method_scheduler_backend(VALUE klass);
17
- #if HAVE_LIBURING_H
18
- VALUE method_scheduler_io_read(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length);
19
- VALUE method_scheduler_io_write(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length);
20
- #endif
21
-
22
- #if HAV_WINDOWS_H
23
- VALUE method_scheduler_io_read(VALUE io, VALUE buffer, VALUE offset, VALUE length);
24
- VALUE method_scheduler_io_write(VALUE io, VALUE buffer, VALUE offset, VALUE length);
25
- #endif
26
12
 
27
13
  #if HAVE_LIBURING_H
28
- #include <liburing.h>
14
+ VALUE method_scheduler_uring_init(VALUE self);
15
+ VALUE method_scheduler_uring_register(VALUE self, VALUE io, VALUE interest);
16
+ VALUE method_scheduler_uring_deregister(VALUE self, VALUE io);
17
+ VALUE method_scheduler_uring_wait(VALUE self);
18
+ VALUE method_scheduler_uring_backend(VALUE klass);
19
+ VALUE method_scheduler_uring_io_read(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length);
20
+ VALUE method_scheduler_uring_io_write(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length);
29
21
 
22
+ #include <liburing.h>
30
23
  #define URING_ENTRIES 64
31
24
  #define URING_MAX_EVENTS 64
32
25
 
@@ -49,13 +42,26 @@ VALUE method_scheduler_io_write(VALUE io, VALUE buffer, VALUE offset, VALUE leng
49
42
  .data = NULL,
50
43
  .flags = RUBY_TYPED_FREE_IMMEDIATELY,
51
44
  };
52
- #elif HAVE_SYS_EPOLL_H
45
+ #endif
46
+ #if HAVE_SYS_EPOLL_H
47
+ VALUE method_scheduler_epoll_init(VALUE self);
48
+ VALUE method_scheduler_epoll_register(VALUE self, VALUE io, VALUE interest);
49
+ VALUE method_scheduler_epoll_deregister(VALUE self, VALUE io);
50
+ VALUE method_scheduler_epoll_wait(VALUE self);
51
+ VALUE method_scheduler_epoll_backend(VALUE klass);
53
52
  #include <sys/epoll.h>
54
53
  #define EPOLL_MAX_EVENTS 64
55
- #elif HAVE_SYS_EVENT_H
54
+ #endif
55
+ #if HAVE_SYS_EVENT_H
56
+ VALUE method_scheduler_kqueue_init(VALUE self);
57
+ VALUE method_scheduler_kqueue_register(VALUE self, VALUE io, VALUE interest);
58
+ VALUE method_scheduler_kqueue_deregister(VALUE self, VALUE io);
59
+ VALUE method_scheduler_kqueue_wait(VALUE self);
60
+ VALUE method_scheduler_kqueue_backend(VALUE klass);
56
61
  #include <sys/event.h>
57
62
  #define KQUEUE_MAX_EVENTS 64
58
- #elif HAVE_WINDOWS_H
63
+ #endif
64
+ #if HAVE_WINDOWS_H
59
65
  // #include <Windows.h>
60
66
  // #define IOCP_MAX_EVENTS 64
61
67
 
@@ -79,4 +85,6 @@ VALUE method_scheduler_io_write(VALUE io, VALUE buffer, VALUE offset, VALUE leng
79
85
  // .flags = RUBY_TYPED_FREE_IMMEDIATELY,
80
86
  // };
81
87
  #endif
88
+ VALUE method_scheduler_select_wait(VALUE self);
89
+ VALUE method_scheduler_select_backend(VALUE klass);
82
90
  #endif
@@ -11,13 +11,13 @@ size_t iocp_payload_size(const void* data) {
11
11
  return sizeof(HANDLE);
12
12
  }
13
13
 
14
- VALUE method_scheduler_init(VALUE self) {
14
+ VALUE method_scheduler_iocp_init(VALUE self) {
15
15
  HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
16
16
  rb_iv_set(self, "@iocp", TypedData_Wrap_Struct(Payload, &type_iocp_payload, iocp));
17
17
  return Qnil;
18
18
  }
19
19
 
20
- VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
20
+ VALUE method_scheduler_iocp_register(VALUE self, VALUE io, VALUE interest) {
21
21
  HANDLE iocp;
22
22
  VALUE iocp_obj = rb_iv_get(self, "@iocp");
23
23
  struct iocp_data* data;
@@ -47,11 +47,7 @@ VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
47
47
  return Qnil;
48
48
  }
49
49
 
50
- VALUE method_scheduler_deregister(VALUE self, VALUE io) {
51
- return Qnil;
52
- }
53
-
54
- VALUE method_scheduler_wait(VALUE self) {
50
+ VALUE method_scheduler_iocp_wait(VALUE self) {
55
51
  ID id_next_timeout = rb_intern("next_timeout");
56
52
  ID id_push = rb_intern("push");
57
53
  VALUE iocp_obj = rb_iv_get(self, "@iocp");
@@ -119,7 +115,7 @@ VALUE method_scheduler_wait(VALUE self) {
119
115
  return result;
120
116
  }
121
117
 
122
- VALUE method_scheduler_backend(VALUE klass) {
118
+ VALUE method_scheduler_iocp_backend(VALUE klass) {
123
119
  return rb_str_new_cstr("iocp");
124
120
  }
125
121
  #endif
@@ -4,12 +4,12 @@
4
4
 
5
5
  #if HAVE_SYS_EVENT_H
6
6
 
7
- VALUE method_scheduler_init(VALUE self) {
7
+ VALUE method_scheduler_kqueue_init(VALUE self) {
8
8
  rb_iv_set(self, "@kq", INT2NUM(kqueue()));
9
9
  return Qnil;
10
10
  }
11
11
 
12
- VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
12
+ VALUE method_scheduler_kqueue_register(VALUE self, VALUE io, VALUE interest) {
13
13
  struct kevent event;
14
14
  u_short event_flags = 0;
15
15
  ID id_fileno = rb_intern("fileno");
@@ -32,12 +32,7 @@ VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
32
32
  return Qnil;
33
33
  }
34
34
 
35
- VALUE method_scheduler_deregister(VALUE self, VALUE io) {
36
- // One-shot mode
37
- return Qnil;
38
- }
39
-
40
- VALUE method_scheduler_wait(VALUE self) {
35
+ VALUE method_scheduler_kqueue_wait(VALUE self) {
41
36
  int n, kq, i;
42
37
  u_short event_flags = 0;
43
38
  struct kevent events[KQUEUE_MAX_EVENTS];
@@ -80,7 +75,7 @@ VALUE method_scheduler_wait(VALUE self) {
80
75
  return result;
81
76
  }
82
77
 
83
- VALUE method_scheduler_backend(VALUE klass) {
78
+ VALUE method_scheduler_kqueue_backend(VALUE klass) {
84
79
  return rb_str_new_cstr("kqueue");
85
80
  }
86
81
  #endif
@@ -2,19 +2,7 @@
2
2
  #define SELECT_H
3
3
  #include "evt.h"
4
4
 
5
- VALUE method_scheduler_init(VALUE self) {
6
- return Qnil;
7
- }
8
-
9
- VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
10
- return Qnil;
11
- }
12
-
13
- VALUE method_scheduler_deregister(VALUE self, VALUE io) {
14
- return Qnil;
15
- }
16
-
17
- VALUE method_scheduler_wait(VALUE self) {
5
+ VALUE method_scheduler_select_wait(VALUE self) {
18
6
  // return IO.select(@readable.keys, @writable.keys, [], next_timeout)
19
7
  VALUE readable, writable, readable_keys, writable_keys, next_timeout;
20
8
  ID id_select = rb_intern("select");
@@ -30,7 +18,7 @@ VALUE method_scheduler_wait(VALUE self) {
30
18
  return rb_funcall(rb_cIO, id_select, 4, readable_keys, writable_keys, rb_ary_new(), next_timeout);
31
19
  }
32
20
 
33
- VALUE method_scheduler_backend(VALUE klass) {
21
+ VALUE method_scheduler_select_backend(VALUE klass) {
34
22
  return rb_str_new_cstr("ruby");
35
23
  }
36
24
  #endif
@@ -12,7 +12,7 @@ size_t uring_payload_size(const void* data) {
12
12
  return sizeof(struct io_uring);
13
13
  }
14
14
 
15
- VALUE method_scheduler_init(VALUE self) {
15
+ VALUE method_scheduler_uring_init(VALUE self) {
16
16
  int ret;
17
17
  struct io_uring* ring;
18
18
  ring = xmalloc(sizeof(struct io_uring));
@@ -24,7 +24,7 @@ VALUE method_scheduler_init(VALUE self) {
24
24
  return Qnil;
25
25
  }
26
26
 
27
- VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
27
+ VALUE method_scheduler_uring_register(VALUE self, VALUE io, VALUE interest) {
28
28
  VALUE ring_obj;
29
29
  struct io_uring* ring;
30
30
  struct io_uring_sqe *sqe;
@@ -60,12 +60,7 @@ VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
60
60
  return Qnil;
61
61
  }
62
62
 
63
- VALUE method_scheduler_deregister(VALUE self, VALUE io) {
64
- // io_uring runs under oneshot mode. No need to deregister.
65
- return Qnil;
66
- }
67
-
68
- VALUE method_scheduler_wait(VALUE self) {
63
+ VALUE method_scheduler_uring_wait(VALUE self) {
69
64
  struct io_uring* ring;
70
65
  struct io_uring_cqe *cqes[URING_MAX_EVENTS];
71
66
  struct uring_data *data;
@@ -122,7 +117,7 @@ VALUE method_scheduler_wait(VALUE self) {
122
117
  return result;
123
118
  }
124
119
 
125
- VALUE method_scheduler_io_read(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length) {
120
+ VALUE method_scheduler_uring_io_read(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length) {
126
121
  struct io_uring* ring;
127
122
  struct uring_data *data;
128
123
  char* read_buffer;
@@ -160,7 +155,7 @@ VALUE method_scheduler_io_read(VALUE self, VALUE io, VALUE buffer, VALUE offset,
160
155
  return result;
161
156
  }
162
157
 
163
- VALUE method_scheduler_io_write(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length) {
158
+ VALUE method_scheduler_uring_io_write(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length) {
164
159
  struct io_uring* ring;
165
160
  struct uring_data *data;
166
161
  char* write_buffer;
@@ -192,7 +187,7 @@ VALUE method_scheduler_io_write(VALUE self, VALUE io, VALUE buffer, VALUE offset
192
187
  return length;
193
188
  }
194
189
 
195
- VALUE method_scheduler_backend(VALUE klass) {
190
+ VALUE method_scheduler_uring_backend(VALUE klass) {
196
191
  return rb_str_new_cstr("liburing");
197
192
  }
198
193
 
data/lib/evt.rb CHANGED
@@ -1,6 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'fiber'
4
+ require 'socket'
5
+ require 'io/nonblock'
3
6
  require_relative 'evt/version'
7
+ require_relative 'evt/backends/bundled'
8
+ require_relative 'evt/backends/epoll'
9
+ require_relative 'evt/backends/iocp'
10
+ require_relative 'evt/backends/kqueue'
11
+ require_relative 'evt/backends/select'
12
+ require_relative 'evt/backends/uring'
4
13
  require_relative 'evt/scheduler'
5
14
  require_relative 'evt_ext'
6
15
 
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Evt::Bundled
4
+ MAXIMUM_TIMEOUT = 5
5
+
6
+ def initialize
7
+ @readable = {}
8
+ @writable = {}
9
+ @waiting = {}
10
+ @iovs = {}
11
+
12
+ @lock = Mutex.new
13
+ @blocking = 0
14
+ @ready = []
15
+
16
+ init_selector
17
+ end
18
+
19
+ attr_reader :readable
20
+ attr_reader :writable
21
+ attr_reader :waiting
22
+
23
+ def next_timeout
24
+ _fiber, timeout = @waiting.min_by{|key, value| value}
25
+
26
+ if timeout
27
+ offset = timeout - current_time
28
+ return 0 if offset < 0
29
+ return offset if offset < MAXIMUM_TIMEOUT
30
+ end
31
+
32
+ MAXIMUM_TIMEOUT
33
+ end
34
+
35
+ def run
36
+ while @readable.any? or @writable.any? or @waiting.any? or @iovs.any? or @blocking.positive?
37
+ readable, writable, iovs = self.wait
38
+
39
+ readable&.each do |io|
40
+ fiber = @readable.delete(io)
41
+ fiber&.resume
42
+ end
43
+
44
+ writable&.each do |io|
45
+ fiber = @writable.delete(io)
46
+ fiber&.resume
47
+ end
48
+
49
+ unless iovs.nil?
50
+ iovs&.each do |io|
51
+ fiber = @iovs.delete(io)
52
+ fiber&.resume
53
+ end
54
+ end
55
+
56
+ if @waiting.any?
57
+ time = current_time
58
+ waiting, @waiting = @waiting, {}
59
+
60
+ waiting.each do |fiber, timeout|
61
+ if timeout <= time
62
+ fiber.resume
63
+ else
64
+ @waiting[fiber] = timeout
65
+ end
66
+ end
67
+ end
68
+
69
+ if @ready.any?
70
+ ready = nil
71
+
72
+ @lock.synchronize do
73
+ ready, @ready = @ready, []
74
+ end
75
+
76
+ ready.each do |fiber|
77
+ fiber.resume
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ def current_time
84
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
85
+ end
86
+
87
+ # Wait for the given file descriptor to match the specified events within
88
+ # the specified timeout.
89
+ # @parameter event [Integer] A bit mask of `IO::READABLE`,
90
+ # `IO::WRITABLE` and `IO::PRIORITY`.
91
+ # @parameter timeout [Numeric] The amount of time to wait for the event in seconds.
92
+ # @returns [Integer] The subset of events that are ready.
93
+ def io_wait(io, events, duration)
94
+ # TODO: IO::PRIORITY
95
+ @readable[io] = Fiber.current unless (events & IO::READABLE).zero?
96
+ @writable[io] = Fiber.current unless (events & IO::WRITABLE).zero?
97
+ self.register(io, events)
98
+ Fiber.yield
99
+ self.deregister(io)
100
+ true
101
+ end
102
+
103
+ # Sleep the current task for the specified duration, or forever if not
104
+ # specified.
105
+ # @param duration [Numeric] The amount of time to sleep in seconds.
106
+ def kernel_sleep(duration = nil)
107
+ self.block(:sleep, duration)
108
+ true
109
+ end
110
+
111
+ # Block the calling fiber.
112
+ # @parameter blocker [Object] What we are waiting on, informational only.
113
+ # @parameter timeout [Numeric | Nil] The amount of time to wait for in seconds.
114
+ # @returns [Boolean] Whether the blocking operation was successful or not.
115
+ def block(blocker, timeout = nil)
116
+ if timeout
117
+ @waiting[Fiber.current] = current_time + timeout
118
+ begin
119
+ Fiber.yield
120
+ ensure
121
+ @waiting.delete(Fiber.current)
122
+ end
123
+ else
124
+ @blocking += 1
125
+ begin
126
+ Fiber.yield
127
+ ensure
128
+ @blocking -= 1
129
+ end
130
+ end
131
+ end
132
+
133
+ # Unblock the specified fiber.
134
+ # @parameter blocker [Object] What we are waiting on, informational only.
135
+ # @parameter fiber [Fiber] The fiber to unblock.
136
+ # @reentrant Thread safe.
137
+ def unblock(blocker, fiber)
138
+ @lock.synchronize do
139
+ @ready << fiber
140
+ end
141
+ end
142
+
143
+ # Invoked when the thread exits.
144
+ def close
145
+ self.run
146
+ end
147
+
148
+ # Intercept the creation of a non-blocking fiber.
149
+ # @returns [Fiber]
150
+ def fiber(&block)
151
+ fiber = Fiber.new(blocking: false, &block)
152
+ fiber.resume
153
+ fiber
154
+ end
155
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Evt::Epoll < Evt::Bundled
4
+ def self.available?
5
+ self.respond_to?(:epoll_backend)
6
+ end
7
+
8
+ def self.backend
9
+ self.epoll_backend
10
+ end
11
+
12
+ def init_selector
13
+ epoll_init_selector
14
+ end
15
+
16
+ def register(io, interest)
17
+ epoll_register(io, interest)
18
+ end
19
+
20
+ def deregister(io)
21
+ epoll_deregister(io)
22
+ end
23
+
24
+ def wait
25
+ epoll_wait
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Evt::Iocp < Evt::Bundled
4
+ ##
5
+ # IOCP is totally disabled for now
6
+ def self.available?
7
+ false
8
+ end
9
+
10
+ def init_selector
11
+ # Placeholder
12
+ end
13
+
14
+ def register(io, interest)
15
+ # Placeholder
16
+ end
17
+
18
+ def deregister(io)
19
+ # Placeholder
20
+ end
21
+
22
+ def io_read(io, buffer, offset, length)
23
+ # Placeholder
24
+ end
25
+
26
+ def io_write(io, buffer, offset, length)
27
+ # Placeholder
28
+ end
29
+
30
+ def wait
31
+ # Placeholder
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Evt::Kqueue < Evt::Bundled
4
+ def self.available?
5
+ self.respond_to?(:kqueue_backend)
6
+ end
7
+
8
+ def self.backend
9
+ self.kqueue_backend
10
+ end
11
+
12
+ def init_selector
13
+ kqueue_init_selector
14
+ end
15
+
16
+ def register(io, interest)
17
+ kqueue_register(io, interest)
18
+ end
19
+
20
+ def deregister(io)
21
+ # Kqueue running under one-shot mode, no need to deregister
22
+ end
23
+
24
+ def wait
25
+ kqueue_wait
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Evt::Select < Evt::Bundled
4
+ def self.available?
5
+ self.respond_to?(:select_backend)
6
+ end
7
+
8
+ def self.backend
9
+ self.select_backend
10
+ end
11
+
12
+ def init_selector
13
+ # Select is stateless
14
+ end
15
+
16
+ def register(io, interest)
17
+ # Select is stateless
18
+ end
19
+
20
+ def deregister(io)
21
+ # Select is stateless
22
+ end
23
+
24
+ def wait
25
+ select_wait
26
+ end
27
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Evt::Uring < Evt::Bundled
4
+ def self.available?
5
+ self.respond_to?(:uring_backend)
6
+ end
7
+
8
+ def self.backend
9
+ self.uring_backend
10
+ end
11
+
12
+ def init_selector
13
+ uring_init_selector
14
+ end
15
+
16
+ def register(io, interest)
17
+ uring_register(io, register)
18
+ end
19
+
20
+ def deregister(io)
21
+ # io_uring running under one-shot mode, no need to deregister
22
+ end
23
+
24
+ def io_read(io, buffer, offset, length)
25
+ uring_io_read(io, buffer, offset, length)
26
+ end
27
+
28
+ def io_write(io, buffer, offset, length)
29
+ uring_io_write(io, buffer, offset, length)
30
+ end
31
+
32
+ def wait
33
+ uring_wait
34
+ end
35
+ end
@@ -1,121 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'fiber'
4
- require 'socket'
5
- require 'io/nonblock'
6
-
3
+ ##
4
+ # The major class for Ruby Fiber Scheduler
5
+ # @example
6
+ # scheduler = Evt::Scheduler.new
7
+ # Fiber.set_scheduler scheduler
8
+ # scheduler.run
7
9
  class Evt::Scheduler
8
- def initialize
9
- @readable = {}
10
- @writable = {}
11
- @iovs = {}
12
- @waiting = {}
13
-
14
- @lock = Mutex.new
15
- @locking = 0
16
- @ready = []
17
-
18
- @ios = ObjectSpace::WeakMap.new
19
- init_selector
20
- end
21
-
22
- attr_reader :readable
23
- attr_reader :writable
24
- attr_reader :waiting
25
-
26
- def next_timeout
27
- _fiber, timeout = @waiting.min_by{|key, value| value}
28
-
29
- if timeout
30
- offset = timeout - current_time
31
- offset < 0 ? 0 : offset
32
- end
33
- end
34
-
35
- def run
36
- while @readable.any? or @writable.any? or @waiting.any? or @iovs.any? or @locking.positive?
37
- readable, writable, iovs = self.wait
38
-
39
- readable&.each do |io|
40
- fiber = @readable.delete(io)
41
- fiber&.resume
42
- end
43
-
44
- writable&.each do |io|
45
- fiber = @writable.delete(io)
46
- fiber&.resume
47
- end
48
-
49
- unless iovs.nil?
50
- iovs&.each do |io|
51
- fiber = @iovs.delete(io)
52
- fiber&.resume
53
- end
54
- end
55
-
56
- if @waiting.any?
57
- time = current_time
58
- waiting = @waiting
59
- @waiting = {}
60
-
61
- waiting.each do |fiber, timeout|
62
- if timeout <= time
63
- fiber.resume
64
- else
65
- @waiting[fiber] = timeout
66
- end
67
- end
68
- end
69
-
70
- if @ready.any?
71
- ready = nil
72
-
73
- @lock.synchronize do
74
- ready, @ready = @ready, []
75
- end
76
-
77
- ready.each do |fiber|
78
- fiber.resume
79
- end
10
+ class << self
11
+ BACKENDS = [
12
+ Evt::Uring,
13
+ Evt::Epoll,
14
+ Evt::Kqueue,
15
+ Evt::Iocp,
16
+ Evt::Select,
17
+ ].freeze
18
+
19
+ ##
20
+ # Returns the fastest possible scheduler
21
+ # Use the backend scheduler directly if you want to choose it yourself
22
+ def new
23
+ BACKENDS.each do |backend|
24
+ return backend.new if backend.available?
80
25
  end
81
26
  end
82
- end
83
-
84
- def current_time
85
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
86
- end
87
-
88
- def io_wait(io, events, duration)
89
- @readable[io] = Fiber.current unless (events & IO::READABLE).zero?
90
- @writable[io] = Fiber.current unless (events & IO::WRITABLE).zero?
91
- self.register(io, events)
92
- Fiber.yield
93
- self.deregister(io)
94
- true
95
- end
96
-
97
- def kernel_sleep(duration = nil)
98
- @waiting[Fiber.current] = current_time + duration if duration.nil?
99
- Fiber.yield
100
- true
101
- end
102
-
103
- def mutex_lock(mutex)
104
- @locking += 1
105
- Fiber.yield
106
- ensure
107
- @locking -= 1
108
- end
109
27
 
110
- def mutex_unlock(mutex, fiber)
111
- @lock.synchronize do
112
- @ready << fiber
28
+ ##
29
+ # Returns all available backends on this machine
30
+ def availables
31
+ BACKENDS.filter do |backend|
32
+ backend.available?
33
+ end
113
34
  end
114
35
  end
115
-
116
- def fiber(&block)
117
- fiber = Fiber.new(blocking: false, &block)
118
- fiber.resume
119
- fiber
120
- end
121
36
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Evt
4
- VERSION = "0.2.3"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delton Ding
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: simplecov
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.20.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.20.0
27
41
  description: A low-level Event Handler designed for Ruby 3 Scheduler for better performance
28
42
  email:
29
43
  - dsh0416@gmail.com
@@ -32,6 +46,7 @@ extensions:
32
46
  - ext/evt/extconf.rb
33
47
  extra_rdoc_files: []
34
48
  files:
49
+ - ".github/workflows/build.yml"
35
50
  - ".github/workflows/test.yml"
36
51
  - ".gitignore"
37
52
  - CODE_OF_CONDUCT.md
@@ -49,6 +64,12 @@ files:
49
64
  - ext/evt/select.h
50
65
  - ext/evt/uring.h
51
66
  - lib/evt.rb
67
+ - lib/evt/backends/bundled.rb
68
+ - lib/evt/backends/epoll.rb
69
+ - lib/evt/backends/iocp.rb
70
+ - lib/evt/backends/kqueue.rb
71
+ - lib/evt/backends/select.rb
72
+ - lib/evt/backends/uring.rb
52
73
  - lib/evt/scheduler.rb
53
74
  - lib/evt/version.rb
54
75
  homepage: https://github.com/dsh0416/evt