evt 0.2.0 → 0.3.1

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: 792dc614f6216687c600f101b8fac515a71fc0249c4ca6af3f880cb71ee17eff
4
- data.tar.gz: c64b75645bd2adb22a3947c7b239c9cde05a8fb1ac42a6e08f2cfe653bc20a84
3
+ metadata.gz: 40b96a960c2027db56a915a8c4d6e20f8d29fd9f195003ef1a93cf9fad13a151
4
+ data.tar.gz: d6f99c7c134954a47b68c56f9a68f1227ed9ac90cefedefbef33a9c8978730aa
5
5
  SHA512:
6
- metadata.gz: '016090b4ba576eb10896099d3879445d3c65d3b8f6d9475731786639282c5e7f37ba1029e6d1201b165f9bfbf20193fb13017b5ea6af90792b60989f33691899'
7
- data.tar.gz: 83d15ffca0e28b5c96f241197bbd97eabe1a935e01f71c6e4bb94a2eb9ad413148b2318d1b55a4df4b11444e989508b32cd7da9f1b19b839512d13b92b7034b9
6
+ metadata.gz: 75dbf1e519db25b8a0f63958cb2578fe852f12374eaed0a5a3b9595b30315ad92971477db2b27ed1bd2db6bd051553fa93eaa41263ed45c08c57ace0b43117fd
7
+ data.tar.gz: 905988d2a76fed454cd42b3acc014d5ddce044ecdf918352d8a6ede58213b3006dda7588a7d5889c6a4b16b23cce0a14308d71e4ba02430efcfe0fcdfa8f38c6
@@ -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,15 +1,16 @@
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)
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)
9
+ [![Gem Version](https://badge.fury.io/rb/evt.svg)](https://rubygems.org/gems/evt)
10
+ [![Downloads](https://ruby-gem-downloads-badge.herokuapp.com/evt?type=total)](https://rubygems.org/gems/evt)
8
11
 
9
12
  ## Features
10
13
 
11
-
12
-
13
14
  ### IO Backend Support
14
15
 
15
16
  | | Linux | Windows | macOS | FreeBSD |
@@ -18,14 +19,33 @@ The Event Library that designed for Ruby 3.0.
18
19
  | epoll | ✅ (See 2) | ❌ | ❌ | ❌ |
19
20
  | kqueue | ❌ | ❌ | ✅ (⚠️ See 5) | ✅ |
20
21
  | IOCP | ❌ | ❌ (⚠️See 3) | ❌ | ❌ |
21
- | Ruby (`select`) | ✅ Fallback | ✅ (⚠️See 4) | ✅ Fallback | ✅ Fallback |
22
+ | Ruby (`IO.select`) | ✅ Fallback | ✅ (⚠️See 4) | ✅ Fallback | ✅ Fallback |
22
23
 
23
24
  1. when liburing is installed
24
- 2. when kernel version >= 2.6.8
25
+ 2. when kernel version >= 2.6.9
25
26
  3. WOULD NOT WORK until `FILE_FLAG_OVERLAPPED` is included in I/O initialization process.
26
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).
27
28
  5. `kqueue` performance in Darwin is very poor. **MAY BE DISABLED IN THE FUTURE.**
28
29
 
30
+ ### Benchmark
31
+
32
+ The benchmark is running under `v0.2.2` version. See [evt-server-benchmark](https://github.com/dsh0416/evt-server-benchmark) for test code, the test is running under a single-thread server.
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
+
38
+ | OS | CPU | Memory | Backend | req/s |
39
+ | ----- | ----------- | ------ | ---------------------- | -------- |
40
+ | Linux | Ryzen 2700x | 64GB | epoll | 54680.08 |
41
+ | Linux | Ryzen 2700x | 64GB | io_uring | 50245.53 |
42
+ | Linux | Ryzen 2700x | 64GB | IO.select (using poll) | 44159.23 |
43
+ | macOS | i7-6820HQ | 16GB | kqueue | 37855.53 |
44
+ | macOS | i7-6820HQ | 16GB | IO.select (using poll) | 28293.36 |
45
+
46
+ The benchmark uses an invalid parser, and `wrk` is very error-sensitive. The benchmark can't close the connection properly.
47
+ Use a valid parser, recent updates to my [midori](https://github.com/midori-rb/midori.rb) is able to use Ruby scheduler, which could achives 247k+ req/s on single thread with `kqueue` and 647k+ req/s with `epoll`.
48
+
29
49
  ## Install
30
50
 
31
51
  ```bash
@@ -53,8 +73,6 @@ Fiber.schedule do
53
73
  wr.close
54
74
  end
55
75
 
56
- scheduler.run
57
-
58
76
  # "Hello World"
59
77
  ```
60
78
 
@@ -66,6 +84,7 @@ scheduler.run
66
84
  - [x] Support iov features of io_uring
67
85
  - [x] Support IOCP (**NOT ENABLED YET**)
68
86
  - [x] Setup tests with Ruby 3
87
+ - [x] Selectable backend compilation by environment variable
69
88
  - [ ] Support IOCP with iov features
70
89
  - [ ] Setup more tests for production purpose
71
90
  - [ ] 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,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");
@@ -56,7 +56,7 @@ VALUE method_scheduler_wait(VALUE self) {
56
56
  timeout = NUM2INT(next_timeout);
57
57
  }
58
58
 
59
- struct epoll_event* events = (struct epoll_event*) xmalloc(sizeof(struct epoll_event) * EPOLL_MAX_EVENTS);
59
+ struct epoll_event events[EPOLL_MAX_EVENTS];
60
60
 
61
61
  n = epoll_wait(epfd, events, EPOLL_MAX_EVENTS, timeout);
62
62
  if (n < 0) {
@@ -79,12 +79,10 @@ VALUE method_scheduler_wait(VALUE self) {
79
79
  result = rb_ary_new2(2);
80
80
  rb_ary_store(result, 0, readables);
81
81
  rb_ary_store(result, 1, writables);
82
-
83
- xfree(events);
84
82
  return result;
85
83
  }
86
84
 
87
- VALUE method_scheduler_backend(VALUE klass) {
85
+ VALUE method_scheduler_epoll_backend(VALUE klass) {
88
86
  return rb_str_new_cstr("epoll");
89
87
  }
90
88
  #endif
@@ -5,30 +5,41 @@
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_deregister", method_scheduler_epoll_deregister, 1);
28
+ rb_define_method(Bundled, "epoll_wait", method_scheduler_epoll_wait, 0);
21
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);
22
38
  }
23
39
 
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
40
+ #include "uring.h"
41
+ #include "epoll.h"
42
+ #include "kqueue.h"
43
+ // #include "iocp.h"
44
+ #include "select.h"
34
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
@@ -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 {
@@ -70,7 +57,6 @@ VALUE method_scheduler_wait(VALUE self) {
70
57
  // TODO: Check if n >= 0
71
58
  for (i = 0; i < n; i++) {
72
59
  event_flags = events[i].filter;
73
- printf("event flags: %d\n", event_flags);
74
60
  if (event_flags & EVFILT_READ) {
75
61
  obj_io = (VALUE) events[i].udata;
76
62
  rb_funcall(readables, id_push, 1, obj_io);
@@ -86,11 +72,10 @@ VALUE method_scheduler_wait(VALUE self) {
86
72
  rb_ary_store(result, 0, readables);
87
73
  rb_ary_store(result, 1, writables);
88
74
 
89
- xfree(events);
90
75
  return result;
91
76
  }
92
77
 
93
- VALUE method_scheduler_backend(VALUE klass) {
78
+ VALUE method_scheduler_kqueue_backend(VALUE klass) {
94
79
  return rb_str_new_cstr("kqueue");
95
80
  }
96
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;
@@ -90,27 +85,27 @@ VALUE method_scheduler_wait(VALUE self) {
90
85
  data = (struct uring_data*) io_uring_cqe_get_data(cqes[i]);
91
86
  poll_events = data->poll_mask;
92
87
  obj_io = data->io;
93
- if (!data->is_poll) {
88
+ if (data->is_poll) {
89
+ if (poll_events & POLL_IN) {
90
+ rb_funcall(readables, id_push, 1, obj_io);
91
+ }
92
+
93
+ if (poll_events & POLL_OUT) {
94
+ rb_funcall(writables, id_push, 1, obj_io);
95
+ }
96
+ } else {
94
97
  rb_funcall(iovs, id_push, 1, obj_io);
95
98
  }
96
-
97
- if (poll_events & POLL_IN) {
98
- rb_funcall(readables, id_push, 1, obj_io);
99
- }
100
-
101
- if (poll_events & POLL_OUT) {
102
- rb_funcall(writables, id_push, 1, obj_io);
103
- }
104
- xfree(data);
99
+ io_uring_cqe_seen(ring, cqes[i]);
105
100
  }
106
101
 
107
102
  if (ret == 0) {
108
103
  if (next_timeout != Qnil && NUM2INT(next_timeout) != -1) {
109
104
  // sleep
110
105
  time = next_timeout / 1000;
111
- rb_funcall(rb_mKernel, id_sleep, 1, RFLOAT_VALUE(time));
106
+ rb_funcall(rb_mKernel, id_sleep, 1, rb_float_new(time));
112
107
  } else {
113
- rb_funcall(rb_mKernel, id_sleep, 1, RFLOAT_VALUE(0.001)); // To avoid infinite loop
108
+ rb_funcall(rb_mKernel, id_sleep, 1, rb_float_new(0.001)); // To avoid infinite loop
114
109
  }
115
110
  }
116
111
 
@@ -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;
@@ -152,7 +147,6 @@ VALUE method_scheduler_io_read(VALUE self, VALUE io, VALUE buffer, VALUE offset,
152
147
  io_uring_submit(ring);
153
148
 
154
149
  VALUE result = rb_str_new(read_buffer, strlen(read_buffer));
155
- xfree(read_buffer);
156
150
  if (buffer != Qnil) {
157
151
  rb_str_append(buffer, result);
158
152
  }
@@ -161,7 +155,7 @@ VALUE method_scheduler_io_read(VALUE self, VALUE io, VALUE buffer, VALUE offset,
161
155
  return result;
162
156
  }
163
157
 
164
- 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) {
165
159
  struct io_uring* ring;
166
160
  struct uring_data *data;
167
161
  char* write_buffer;
@@ -193,7 +187,7 @@ VALUE method_scheduler_io_write(VALUE self, VALUE io, VALUE buffer, VALUE offset
193
187
  return length;
194
188
  }
195
189
 
196
- VALUE method_scheduler_backend(VALUE klass) {
190
+ VALUE method_scheduler_uring_backend(VALUE klass) {
197
191
  return rb_str_new_cstr("liburing");
198
192
  }
199
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,177 @@
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 |io|
53
+ fiber = @iovs.delete(io)
54
+ fiber&.resume
55
+ end
56
+ end
57
+
58
+ collect
59
+
60
+ if @waiting.any?
61
+ time = current_time
62
+ waiting, @waiting = @waiting, {}
63
+
64
+ waiting.each do |fiber, timeout|
65
+ if timeout <= time
66
+ fiber.resume
67
+ else
68
+ @waiting[fiber] = timeout
69
+ end
70
+ end
71
+ end
72
+
73
+ if @ready.any?
74
+ ready = nil
75
+
76
+ @lock.synchronize do
77
+ ready, @ready = @ready, []
78
+ end
79
+
80
+ ready.each do |fiber|
81
+ fiber.resume
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ def current_time
88
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
89
+ end
90
+
91
+ # Wait for the given file descriptor to match the specified events within
92
+ # the specified timeout.
93
+ # @parameter event [Integer] A bit mask of `IO::READABLE`,
94
+ # `IO::WRITABLE` and `IO::PRIORITY`.
95
+ # @parameter timeout [Numeric] The amount of time to wait for the event in seconds.
96
+ # @returns [Integer] The subset of events that are ready.
97
+ def io_wait(io, events, duration)
98
+ # TODO: IO::PRIORITY
99
+ @readable[io] = Fiber.current unless (events & IO::READABLE).zero?
100
+ @writable[io] = Fiber.current unless (events & IO::WRITABLE).zero?
101
+ self.register(io, events)
102
+ Fiber.yield
103
+ self.deregister(io)
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
154
+ if @collect_counter < COLLECT_COUNTER_MAX
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
+ end
169
+
170
+ # Intercept the creation of a non-blocking fiber.
171
+ # @returns [Fiber]
172
+ def fiber(&block)
173
+ fiber = Fiber.new(blocking: false, &block)
174
+ fiber.resume
175
+ fiber
176
+ end
177
+ 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.0"
4
+ VERSION = "0.3.1"
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.0
4
+ version: 0.3.1
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-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -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
@@ -65,7 +86,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
65
86
  requirements:
66
87
  - - ">="
67
88
  - !ruby/object:Gem::Version
68
- version: 2.8.0.dev
89
+ version: 3.0.0.rc1
69
90
  required_rubygems_version: !ruby/object:Gem::Requirement
70
91
  requirements:
71
92
  - - ">="
@@ -75,5 +96,5 @@ requirements: []
75
96
  rubygems_version: 3.2.2
76
97
  signing_key:
77
98
  specification_version: 4
78
- summary: A low-level Event Handler designed for Ruby 3 Scheduler
99
+ summary: The Event Library that designed for Ruby 3.0 Fiber Scheluer.
79
100
  test_files: []