evt 0.2.3 → 0.3.0

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