evt 0.2.1 → 0.3.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: 9466e3fd37da9807dc59525feebfe9252489a460a83111febf95973ff77fee56
4
- data.tar.gz: 531808277a49049dc156144294346730a457d717699e091348125086dca529de
3
+ metadata.gz: 945c1a802be8b4e03b88c44138a139367288409b27c83a6f2904d0ecc94d2a40
4
+ data.tar.gz: 7b88e16f4a082f536c5b0eb9a7f68e67e6e1f5ab4586dbe771f7c57b9004447a
5
5
  SHA512:
6
- metadata.gz: c12a241295309fd31bf21bc4957374a636ff046b6aa1c5f670113f0cdeedb3df74f974145ba6f19f9e442d5f8d9ae373d70bfee6ae417dc09e9b620d57bcc78f
7
- data.tar.gz: b3a369c7640d4770a89e7d37166930a757ca25266132fbd0544f2f0a065670de1c13578ea4cf7fc43ad1fb2c2020757b20cc972e85c23cab720bb229103fb070
6
+ metadata.gz: f042da98ff5f85de3b4fce59a2726f2f4c46a1da8817abb524d700cd0413fc4118cb6c0a766a69abdc7e4ae5f0acdd35bc653b0c33116239c1e776f4c3218f23
7
+ data.tar.gz: 1824a4935d712e62268b9cad01f52c445ccecfcaa859b11fd93749f28ef3f259370b011af395ae269c9c03544b7ab5c1bb6ad3afd18cc8eea4a6d576e151ff76
@@ -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
@@ -12,13 +12,13 @@ jobs:
12
12
  fail-fast: false
13
13
  matrix:
14
14
  include:
15
- - { os: ubuntu-20.04, ruby: '3.0' }
16
- - { os: ubuntu-20.04, ruby: ruby-head }
17
- - { os: macos-11.0, ruby: '3.0' }
18
- - { os: macos-11.0, ruby: ruby-head }
19
- - { os: windows-2019, ruby: mingw }
20
- - { os: windows-2019, ruby: mswin }
21
- name: ${{ matrix.os }} ${{ matrix.ruby }}
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
22
  runs-on: ${{ matrix.os }}
23
23
  timeout-minutes: 5
24
24
  steps:
@@ -32,20 +32,9 @@ jobs:
32
32
  gem install bundler
33
33
  bundle install --jobs 4 --retry 3
34
34
  - name: Compile
35
+ env:
36
+ DISABLE_EPOLL: ${{ matrix.disable-epoll }}
37
+ DISABLE_KQUEUE: ${{ matrix.disable-kqueue }}
35
38
  run: rake compile
36
39
  - name: Test
37
- run: rake
38
- build:
39
- runs-on: ubuntu-20.04
40
- steps:
41
- - uses: actions/checkout@v2
42
- - uses: ruby/setup-ruby@master
43
- with:
44
- ruby-version: '3.0'
45
- bundler-cache: false
46
- - name: Install Dependencies
47
- run: |
48
- gem install bundler
49
- bundle install --jobs 4 --retry 3
50
- - name: Build
51
- run: gem build evt.gemspec
40
+ run: rake test
data/README.md CHANGED
@@ -1,33 +1,49 @@
1
1
  # Evt
2
2
 
3
- The Event Library that designed for Ruby 3.0.
3
+ 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
 
11
12
  ## Features
12
13
 
13
-
14
-
15
14
  ### IO Backend Support
16
15
 
17
16
  | | Linux | Windows | macOS | FreeBSD |
18
17
  | --------------- | ----------- | ------------| ----------- | ----------- |
19
- | io_uring | (See 1) | ❌ | ❌ | ❌ |
18
+ | io_uring | ⚠️ (See 1) | ❌ | ❌ | ❌ |
20
19
  | epoll | ✅ (See 2) | ❌ | ❌ | ❌ |
21
20
  | kqueue | ❌ | ❌ | ✅ (⚠️ See 5) | ✅ |
22
21
  | IOCP | ❌ | ❌ (⚠️See 3) | ❌ | ❌ |
23
- | Ruby (`select`) | ✅ Fallback | ✅ (⚠️See 4) | ✅ Fallback | ✅ Fallback |
22
+ | Ruby (`IO.select`) | ✅ Fallback | ✅ (⚠️See 4) | ✅ Fallback | ✅ Fallback |
24
23
 
25
- 1. when liburing is installed
26
- 2. when kernel version >= 2.6.8
24
+ 1. when liburing is installed. (Currently fixing)
25
+ 2. when kernel version >= 2.6.9
27
26
  3. WOULD NOT WORK until `FILE_FLAG_OVERLAPPED` is included in I/O initialization process.
28
27
  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).
29
28
  5. `kqueue` performance in Darwin is very poor. **MAY BE DISABLED IN THE FUTURE.**
30
29
 
30
+ ### Benchmark
31
+
32
+ The benchmark is running under `v0.3.1` version. See `example.rb` in [midori](https://github.com/midori-rb/midori.rb) for test code, the test is running under a single-thread server.
33
+
34
+ The test command is `wrk -t4 -c8192 -d30s http://localhost:3001`.
35
+
36
+ All of the systems have set their file descriptor limit to maximum.
37
+ On systems raising "Fiber unable to allocate memory", `sudo sysctl -w vm.max_map_count=1000000` is set.
38
+
39
+ | OS | CPU | Memory | Backend | req/s |
40
+ | ----- | ----------- | ------ | ---------------------- | --------------|
41
+ | Linux | Ryzen 2700x | 64GB | epoll | 1853259.47 |
42
+ | Linux | Ryzen 2700x | 64GB | io_uring | require fixes |
43
+ | Linux | Ryzen 2700x | 64GB | IO.select (using poll) | 1636849.15 |
44
+ | macOS | i7-6820HQ | 16GB | kqueue | 247370.37 |
45
+ | macOS | i7-6820HQ | 16GB | IO.select (using poll) | 323391.38 |
46
+
31
47
  ## Install
32
48
 
33
49
  ```bash
@@ -55,8 +71,6 @@ Fiber.schedule do
55
71
  wr.close
56
72
  end
57
73
 
58
- scheduler.run
59
-
60
74
  # "Hello World"
61
75
  ```
62
76
 
@@ -68,6 +82,7 @@ scheduler.run
68
82
  - [x] Support iov features of io_uring
69
83
  - [x] Support IOCP (**NOT ENABLED YET**)
70
84
  - [x] Setup tests with Ruby 3
85
+ - [x] Selectable backend compilation by environment variable
71
86
  - [ ] Support IOCP with iov features
72
87
  - [ ] Setup more tests for production purpose
73
88
  - [ ] Documentation for usages
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
@@ -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.8.0.dev'
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,10 +18,12 @@ 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'
28
+ spec.add_development_dependency 'minitest-reporters', '~> 1.4'
27
29
  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"));
@@ -25,21 +25,15 @@ VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
25
25
  event.events |= EPOLLOUT;
26
26
  }
27
27
 
28
+ event.events |=EPOLLONESHOT;
29
+
28
30
  event.data.ptr = (void*) io;
29
31
 
30
32
  epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
31
33
  return Qnil;
32
34
  }
33
35
 
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) {
36
+ VALUE method_scheduler_epoll_wait(VALUE self) {
43
37
  int n, epfd, i, event_flag, timeout;
44
38
  VALUE next_timeout, obj_io, readables, writables, result;
45
39
  ID id_next_timeout = rb_intern("next_timeout");
@@ -56,7 +50,7 @@ VALUE method_scheduler_wait(VALUE self) {
56
50
  timeout = NUM2INT(next_timeout);
57
51
  }
58
52
 
59
- struct epoll_event* events = (struct epoll_event*) xmalloc(sizeof(struct epoll_event) * EPOLL_MAX_EVENTS);
53
+ struct epoll_event events[EPOLL_MAX_EVENTS];
60
54
 
61
55
  n = epoll_wait(epfd, events, EPOLL_MAX_EVENTS, timeout);
62
56
  if (n < 0) {
@@ -79,12 +73,10 @@ VALUE method_scheduler_wait(VALUE self) {
79
73
  result = rb_ary_new2(2);
80
74
  rb_ary_store(result, 0, readables);
81
75
  rb_ary_store(result, 1, writables);
82
-
83
- xfree(events);
84
76
  return result;
85
77
  }
86
78
 
87
- VALUE method_scheduler_backend(VALUE klass) {
79
+ VALUE method_scheduler_epoll_backend(VALUE klass) {
88
80
  return rb_str_new_cstr("epoll");
89
81
  }
90
82
  #endif
@@ -5,30 +5,40 @@
5
5
 
6
6
  void Init_evt_ext()
7
7
  {
8
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
9
+ rb_ext_ractor_safe(true);
10
+ #endif
8
11
  Evt = rb_define_module("Evt");
9
- Scheduler = rb_define_class_under(Evt, "Scheduler", rb_cObject);
10
- 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);
11
14
  Fiber = rb_define_class("Fiber", rb_cObject);
12
- rb_define_singleton_method(Scheduler, "backend", method_scheduler_backend, 0);
13
- rb_define_method(Scheduler, "init_selector", method_scheduler_init, 0);
14
- rb_define_method(Scheduler, "register", method_scheduler_register, 2);
15
- rb_define_method(Scheduler, "deregister", method_scheduler_deregister, 1);
16
- rb_define_method(Scheduler, "wait", method_scheduler_wait, 0);
17
-
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);
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_wait", method_scheduler_epoll_wait, 0);
21
28
  #endif
29
+ #if HAVE_SYS_EVENT_H
30
+ rb_define_singleton_method(Bundled, "kqueue_backend", method_scheduler_kqueue_backend, 0);
31
+ rb_define_method(Bundled, "kqueue_init_selector", method_scheduler_kqueue_init, 0);
32
+ rb_define_method(Bundled, "kqueue_register", method_scheduler_kqueue_register, 2);
33
+ rb_define_method(Bundled, "kqueue_wait", method_scheduler_kqueue_wait, 0);
34
+ #endif
35
+ rb_define_singleton_method(Bundled, "select_backend", method_scheduler_select_backend, 0);
36
+ rb_define_method(Bundled, "select_wait", method_scheduler_select_wait, 0);
22
37
  }
23
38
 
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"
33
- #endif
39
+ #include "uring.h"
40
+ #include "epoll.h"
41
+ #include "kqueue.h"
42
+ // #include "iocp.h"
43
+ #include "select.h"
34
44
  #endif
@@ -4,29 +4,21 @@
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_wait(VALUE self);
17
+ VALUE method_scheduler_uring_backend(VALUE klass);
18
+ VALUE method_scheduler_uring_io_read(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length);
19
+ VALUE method_scheduler_uring_io_write(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length);
29
20
 
21
+ #include <liburing.h>
30
22
  #define URING_ENTRIES 64
31
23
  #define URING_MAX_EVENTS 64
32
24
 
@@ -49,13 +41,24 @@ VALUE method_scheduler_io_write(VALUE io, VALUE buffer, VALUE offset, VALUE leng
49
41
  .data = NULL,
50
42
  .flags = RUBY_TYPED_FREE_IMMEDIATELY,
51
43
  };
52
- #elif HAVE_SYS_EPOLL_H
44
+ #endif
45
+ #if HAVE_SYS_EPOLL_H
46
+ VALUE method_scheduler_epoll_init(VALUE self);
47
+ VALUE method_scheduler_epoll_register(VALUE self, VALUE io, VALUE interest);
48
+ VALUE method_scheduler_epoll_wait(VALUE self);
49
+ VALUE method_scheduler_epoll_backend(VALUE klass);
53
50
  #include <sys/epoll.h>
54
51
  #define EPOLL_MAX_EVENTS 64
55
- #elif HAVE_SYS_EVENT_H
52
+ #endif
53
+ #if HAVE_SYS_EVENT_H
54
+ VALUE method_scheduler_kqueue_init(VALUE self);
55
+ VALUE method_scheduler_kqueue_register(VALUE self, VALUE io, VALUE interest);
56
+ VALUE method_scheduler_kqueue_wait(VALUE self);
57
+ VALUE method_scheduler_kqueue_backend(VALUE klass);
56
58
  #include <sys/event.h>
57
59
  #define KQUEUE_MAX_EVENTS 64
58
- #elif HAVE_WINDOWS_H
60
+ #endif
61
+ #if HAVE_WINDOWS_H
59
62
  // #include <Windows.h>
60
63
  // #define IOCP_MAX_EVENTS 64
61
64
 
@@ -79,4 +82,6 @@ VALUE method_scheduler_io_write(VALUE io, VALUE buffer, VALUE offset, VALUE leng
79
82
  // .flags = RUBY_TYPED_FREE_IMMEDIATELY,
80
83
  // };
81
84
  #endif
85
+ VALUE method_scheduler_select_wait(VALUE self);
86
+ VALUE method_scheduler_select_backend(VALUE klass);
82
87
  #endif
@@ -2,11 +2,22 @@ require 'mkmf'
2
2
  extension_name = 'evt_ext'
3
3
  dir_config(extension_name)
4
4
 
5
- have_library('uring')
6
- have_header('liburing.h')
7
- have_header('sys/epoll.h')
8
- have_header('sys/event.h')
9
- have_header('Windows.h')
5
+ def env_defined?(v)
6
+ return false if ENV[v].nil?
7
+ return false if ENV[v].empty?
8
+ return false if ENV[v].upcase == 'FALSE'
9
+ return false if ENV[v] == '0'
10
+ true
11
+ end
12
+
13
+ unless env_defined?('DISABLE_URING')
14
+ have_library('uring')
15
+ have_header('liburing.h')
16
+ end
17
+
18
+ have_header('sys/epoll.h') unless env_defined?('DISABLE_EPOLL')
19
+ have_header('sys/event.h') unless env_defined?('DISABLE_KQUEUE')
20
+ have_header('Windows.h') unless env_defined?('DISABLE_IOCP')
10
21
 
11
22
  create_header
12
23
  create_makefile(extension_name)
@@ -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");
@@ -27,26 +27,15 @@ VALUE method_scheduler_register(VALUE self, VALUE io, VALUE interest) {
27
27
  event_flags |= EVFILT_WRITE;
28
28
  }
29
29
 
30
- EV_SET(&event, fd, event_flags, EV_ADD|EV_ENABLE, 0, 0, (void*) io);
30
+ EV_SET(&event, fd, event_flags, EV_ADD|EV_ENABLE|EV_ONESHOT, 0, 0, (void*) io);
31
31
  kevent(kq, &event, 1, NULL, 0, NULL); // TODO: Check the return value
32
32
  return Qnil;
33
33
  }
34
34
 
35
- VALUE method_scheduler_deregister(VALUE self, VALUE io) {
36
- struct kevent event;
37
- ID id_fileno = rb_intern("fileno");
38
- int kq = NUM2INT(rb_iv_get(self, "@kq"));
39
- int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
40
- EV_SET(&event, fd, 0, EV_DELETE, 0, 0, (void*) io);
41
- kevent(kq, &event, 1, NULL, 0, (void*) io); // TODO: Check the return value
42
- return Qnil;
43
- }
44
-
45
- VALUE method_scheduler_wait(VALUE self) {
35
+ VALUE method_scheduler_kqueue_wait(VALUE self) {
46
36
  int n, kq, i;
47
37
  u_short event_flags = 0;
48
-
49
- struct kevent* events; // Event Triggered
38
+ struct kevent events[KQUEUE_MAX_EVENTS];
50
39
  struct timespec timeout;
51
40
  VALUE next_timeout, obj_io, readables, writables, result;
52
41
  ID id_next_timeout = rb_intern("next_timeout");
@@ -57,8 +46,6 @@ VALUE method_scheduler_wait(VALUE self) {
57
46
  readables = rb_ary_new();
58
47
  writables = rb_ary_new();
59
48
 
60
- events = (struct kevent*) xmalloc(sizeof(struct kevent) * KQUEUE_MAX_EVENTS);
61
-
62
49
  if (next_timeout == Qnil || NUM2INT(next_timeout) == -1) {
63
50
  n = kevent(kq, NULL, 0, events, KQUEUE_MAX_EVENTS, NULL);
64
51
  } else {
@@ -85,11 +72,10 @@ VALUE method_scheduler_wait(VALUE self) {
85
72
  rb_ary_store(result, 0, readables);
86
73
  rb_ary_store(result, 1, writables);
87
74
 
88
- xfree(events);
89
75
  return result;
90
76
  }
91
77
 
92
- VALUE method_scheduler_backend(VALUE klass) {
78
+ VALUE method_scheduler_kqueue_backend(VALUE klass) {
93
79
  return rb_str_new_cstr("kqueue");
94
80
  }
95
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;
@@ -84,6 +79,12 @@ VALUE method_scheduler_wait(VALUE self) {
84
79
  iovs = rb_ary_new();
85
80
 
86
81
  TypedData_Get_Struct(rb_iv_get(self, "@ring"), struct io_uring, &type_uring_payload, ring);
82
+
83
+ struct __kernel_timespec ts;
84
+ ts.tv_sec = NUM2INT(next_timeout);
85
+ ts.tv_nsec = 0;
86
+
87
+ io_uring_wait_cqe_timeout(ring, cqes, &ts);
87
88
  ret = io_uring_peek_batch_cqe(ring, cqes, URING_MAX_EVENTS);
88
89
 
89
90
  for (i = 0; i < ret; i++) {
@@ -99,18 +100,12 @@ VALUE method_scheduler_wait(VALUE self) {
99
100
  rb_funcall(writables, id_push, 1, obj_io);
100
101
  }
101
102
  } else {
102
- rb_funcall(iovs, id_push, 1, obj_io);
103
+ VALUE v = rb_ary_new2(2);
104
+ rb_ary_store(v, 0, obj_io);
105
+ rb_ary_store(v, 1, obj_io);
106
+ rb_funcall(iovs, id_push, 1, SIZET2NUM(cqes[i]->res));
103
107
  }
104
- }
105
-
106
- if (ret == 0) {
107
- if (next_timeout != Qnil && NUM2INT(next_timeout) != -1) {
108
- // sleep
109
- time = next_timeout / 1000;
110
- rb_funcall(rb_mKernel, id_sleep, 1, rb_float_new(time));
111
- } else {
112
- rb_funcall(rb_mKernel, id_sleep, 1, rb_float_new(0.001)); // To avoid infinite loop
113
- }
108
+ io_uring_cqe_seen(ring, cqes[i]);
114
109
  }
115
110
 
116
111
  result = rb_ary_new2(3);
@@ -121,7 +116,7 @@ VALUE method_scheduler_wait(VALUE self) {
121
116
  return result;
122
117
  }
123
118
 
124
- VALUE method_scheduler_io_read(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length) {
119
+ VALUE method_scheduler_uring_io_read(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length) {
125
120
  struct io_uring* ring;
126
121
  struct uring_data *data;
127
122
  char* read_buffer;
@@ -136,30 +131,26 @@ VALUE method_scheduler_io_read(VALUE self, VALUE io, VALUE buffer, VALUE offset,
136
131
  int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
137
132
 
138
133
  read_buffer = (char*) xmalloc(NUM2SIZET(length));
139
- struct iovec iov = {
140
- .iov_base = read_buffer,
141
- .iov_len = NUM2SIZET(length),
142
- };
143
134
 
144
135
  data = (struct uring_data*) xmalloc(sizeof(struct uring_data));
145
136
  data->is_poll = false;
146
137
  data->io = io;
147
138
  data->poll_mask = 0;
148
139
 
149
- io_uring_prep_readv(sqe, fd, &iov, 1, NUM2SIZET(offset));
140
+ io_uring_prep_read(sqe, fd, read_buffer, 1, NUM2SIZET(length), NUM2SIZET(offset));
150
141
  io_uring_sqe_set_data(sqe, data);
151
142
  io_uring_submit(ring);
152
143
 
144
+ VALUE ret = rb_funcall(Fiber, rb_intern("yield"), 0); // Fiber.yield
145
+
153
146
  VALUE result = rb_str_new(read_buffer, strlen(read_buffer));
154
147
  if (buffer != Qnil) {
155
148
  rb_str_append(buffer, result);
156
149
  }
157
-
158
- rb_funcall(Fiber, rb_intern("yield"), 0); // Fiber.yield
159
- return result;
150
+ return ret;
160
151
  }
161
152
 
162
- VALUE method_scheduler_io_write(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length) {
153
+ VALUE method_scheduler_uring_io_write(VALUE self, VALUE io, VALUE buffer, VALUE offset, VALUE length) {
163
154
  struct io_uring* ring;
164
155
  struct uring_data *data;
165
156
  char* write_buffer;
@@ -174,24 +165,19 @@ VALUE method_scheduler_io_write(VALUE self, VALUE io, VALUE buffer, VALUE offset
174
165
  int fd = NUM2INT(rb_funcall(io, id_fileno, 0));
175
166
 
176
167
  write_buffer = StringValueCStr(buffer);
177
- struct iovec iov = {
178
- .iov_base = write_buffer,
179
- .iov_len = NUM2SIZET(length),
180
- };
181
168
 
182
169
  data = (struct uring_data*) xmalloc(sizeof(struct uring_data));
183
170
  data->is_poll = false;
184
171
  data->io = io;
185
172
  data->poll_mask = 0;
186
173
 
187
- io_uring_prep_writev(sqe, fd, &iov, 1, NUM2SIZET(offset));
174
+ io_uring_prep_write(sqe, fd, write_buffer, NUM2SIZET(length), NUM2SIZET(offset));
188
175
  io_uring_sqe_set_data(sqe, data);
189
176
  io_uring_submit(ring);
190
- rb_funcall(Fiber, rb_intern("yield"), 0); // Fiber.yield
191
- return length;
177
+ return rb_funcall(Fiber, rb_intern("yield"), 0);
192
178
  }
193
179
 
194
- VALUE method_scheduler_backend(VALUE klass) {
180
+ VALUE method_scheduler_uring_backend(VALUE klass) {
195
181
  return rb_str_new_cstr("liburing");
196
182
  }
197
183
 
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,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Evt::Bundled
4
+ MAXIMUM_TIMEOUT = 5
5
+ COLLECT_COUNTER_MAX = 16384
6
+
7
+ def initialize
8
+ @readable = {}
9
+ @writable = {}
10
+ @waiting = {}
11
+ @iovs = {}
12
+
13
+ @lock = Mutex.new
14
+ @blocking = 0
15
+ @ready = []
16
+ @collect_counter = 0
17
+
18
+ init_selector
19
+ end
20
+
21
+ attr_reader :readable
22
+ attr_reader :writable
23
+ attr_reader :waiting
24
+
25
+ def next_timeout
26
+ _fiber, timeout = @waiting.min_by{ |key, value| value }
27
+
28
+ if timeout
29
+ offset = timeout - current_time
30
+ return 0 if offset < 0
31
+ return offset if offset < MAXIMUM_TIMEOUT
32
+ end
33
+
34
+ MAXIMUM_TIMEOUT
35
+ end
36
+
37
+ def run
38
+ while @readable.any? or @writable.any? or @waiting.any? or @iovs.any? or @blocking.positive?
39
+ readable, writable, iovs = self.wait
40
+
41
+ readable&.each do |io|
42
+ fiber = @readable.delete(io)
43
+ fiber&.resume
44
+ end
45
+
46
+ writable&.each do |io|
47
+ fiber = @writable.delete(io)
48
+ fiber&.resume
49
+ end
50
+
51
+ unless iovs.nil?
52
+ iovs&.each do |v|
53
+ io, ret = v
54
+ fiber = @iovs.delete(io)
55
+ fiber&.resume(ret)
56
+ end
57
+ end
58
+
59
+ collect
60
+
61
+ if @waiting.any?
62
+ time = current_time
63
+ waiting, @waiting = @waiting, {}
64
+
65
+ waiting.each do |fiber, timeout|
66
+ if timeout <= time
67
+ fiber.resume if fiber.is_a? Fiber and fiber.alive?
68
+ else
69
+ @waiting[fiber] = timeout
70
+ end
71
+ end
72
+ end
73
+
74
+ if @ready.any?
75
+ ready = nil
76
+
77
+ @lock.synchronize do
78
+ ready, @ready = @ready, []
79
+ end
80
+
81
+ ready.each do |fiber|
82
+ fiber.resume if fiber.is_a? Fiber and fiber.alive?
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ def current_time
89
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
90
+ end
91
+
92
+ # Wait for the given file descriptor to match the specified events within
93
+ # the specified timeout.
94
+ # @parameter event [Integer] A bit mask of `IO::READABLE`,
95
+ # `IO::WRITABLE` and `IO::PRIORITY`.
96
+ # @parameter timeout [Numeric] The amount of time to wait for the event in seconds.
97
+ # @returns [Integer] The subset of events that are ready.
98
+ def io_wait(io, events, duration)
99
+ # TODO: IO::PRIORITY
100
+ @readable[io] = Fiber.current unless (events & IO::READABLE).zero?
101
+ @writable[io] = Fiber.current unless (events & IO::WRITABLE).zero?
102
+ self.register(io, events)
103
+ Fiber.yield
104
+ true
105
+ end
106
+
107
+ # Sleep the current task for the specified duration, or forever if not
108
+ # specified.
109
+ # @param duration [Numeric] The amount of time to sleep in seconds.
110
+ def kernel_sleep(duration = nil)
111
+ self.block(:sleep, duration)
112
+ true
113
+ end
114
+
115
+ # Block the calling fiber.
116
+ # @parameter blocker [Object] What we are waiting on, informational only.
117
+ # @parameter timeout [Numeric | Nil] The amount of time to wait for in seconds.
118
+ # @returns [Boolean] Whether the blocking operation was successful or not.
119
+ def block(blocker, timeout = nil)
120
+ if timeout
121
+ @waiting[Fiber.current] = current_time + timeout
122
+ begin
123
+ Fiber.yield
124
+ ensure
125
+ @waiting.delete(Fiber.current)
126
+ end
127
+ else
128
+ @blocking += 1
129
+ begin
130
+ Fiber.yield
131
+ ensure
132
+ @blocking -= 1
133
+ end
134
+ end
135
+ end
136
+
137
+ # Unblock the specified fiber.
138
+ # @parameter blocker [Object] What we are waiting on, informational only.
139
+ # @parameter fiber [Fiber] The fiber to unblock.
140
+ # @reentrant Thread safe.
141
+ def unblock(blocker, fiber)
142
+ @lock.synchronize do
143
+ @ready << fiber
144
+ end
145
+ end
146
+
147
+ # Invoked when the thread exits.
148
+ def close
149
+ self.run
150
+ end
151
+
152
+ # Collect closed streams in readables and writables
153
+ def collect(force=false)
154
+ if @collect_counter < COLLECT_COUNTER_MAX and !force
155
+ @collect_counter += 1
156
+ return
157
+ end
158
+
159
+ @collect_counter = 0
160
+
161
+ @readable.keys.each do |io|
162
+ @readable.delete(io) if io.closed?
163
+ end
164
+
165
+ @writable.keys.each do |io|
166
+ @writable.delete(io) if io.closed?
167
+ end
168
+
169
+ @iovs.keys.each do |io|
170
+ @iovs.delete(io) if io.closed?
171
+ end
172
+ end
173
+
174
+ # Intercept the creation of a non-blocking fiber.
175
+ # @returns [Fiber]
176
+ def fiber(&block)
177
+ fiber = Fiber.new(blocking: false, &block)
178
+ fiber.resume
179
+ fiber
180
+ end
181
+ end
@@ -0,0 +1,23 @@
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 wait
21
+ epoll_wait
22
+ end
23
+ end
@@ -0,0 +1,29 @@
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 io_read(io, buffer, offset, length)
19
+ # Placeholder
20
+ end
21
+
22
+ def io_write(io, buffer, offset, length)
23
+ # Placeholder
24
+ end
25
+
26
+ def wait
27
+ # Placeholder
28
+ end
29
+ end
@@ -0,0 +1,23 @@
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 wait
21
+ kqueue_wait
22
+ end
23
+ end
@@ -0,0 +1,26 @@
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 wait
21
+ select_wait
22
+ rescue Errno::EBADF => _
23
+ collect(true)
24
+ return [], []
25
+ end
26
+ end
@@ -0,0 +1,31 @@
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, interest)
18
+ end
19
+
20
+ def io_read(io, buffer, offset, length)
21
+ uring_io_read(io, buffer, offset, length)
22
+ end
23
+
24
+ def io_write(io, buffer, offset, length)
25
+ uring_io_write(io, buffer, offset, length)
26
+ end
27
+
28
+ def wait
29
+ uring_wait
30
+ end
31
+ 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.1"
4
+ VERSION = "0.3.2"
5
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.2.1
4
+ version: 0.3.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-12-21 00:00:00.000000000 Z
11
+ date: 2020-12-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -24,6 +24,34 @@ 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
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest-reporters
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.4'
27
55
  description: A low-level Event Handler designed for Ruby 3 Scheduler for better performance
28
56
  email:
29
57
  - dsh0416@gmail.com
@@ -32,6 +60,7 @@ extensions:
32
60
  - ext/evt/extconf.rb
33
61
  extra_rdoc_files: []
34
62
  files:
63
+ - ".github/workflows/build.yml"
35
64
  - ".github/workflows/test.yml"
36
65
  - ".gitignore"
37
66
  - CODE_OF_CONDUCT.md
@@ -49,6 +78,12 @@ files:
49
78
  - ext/evt/select.h
50
79
  - ext/evt/uring.h
51
80
  - lib/evt.rb
81
+ - lib/evt/backends/bundled.rb
82
+ - lib/evt/backends/epoll.rb
83
+ - lib/evt/backends/iocp.rb
84
+ - lib/evt/backends/kqueue.rb
85
+ - lib/evt/backends/select.rb
86
+ - lib/evt/backends/uring.rb
52
87
  - lib/evt/scheduler.rb
53
88
  - lib/evt/version.rb
54
89
  homepage: https://github.com/dsh0416/evt
@@ -65,7 +100,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
65
100
  requirements:
66
101
  - - ">="
67
102
  - !ruby/object:Gem::Version
68
- version: 2.8.0.dev
103
+ version: 3.0.0.rc1
69
104
  required_rubygems_version: !ruby/object:Gem::Requirement
70
105
  requirements:
71
106
  - - ">="
@@ -75,5 +110,5 @@ requirements: []
75
110
  rubygems_version: 3.2.2
76
111
  signing_key:
77
112
  specification_version: 4
78
- summary: A low-level Event Handler designed for Ruby 3 Scheduler
113
+ summary: The Event Library that designed for Ruby 3.0 Fiber Scheluer.
79
114
  test_files: []