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 +4 -4
- data/.github/workflows/build.yml +21 -0
- data/.github/workflows/test.yml +1 -15
- data/README.md +2 -3
- data/Rakefile +1 -0
- data/evt.gemspec +2 -1
- data/ext/evt/epoll.h +6 -6
- data/ext/evt/evt.c +29 -23
- data/ext/evt/evt.h +27 -19
- data/ext/evt/iocp.h +4 -8
- data/ext/evt/kqueue.h +4 -9
- data/ext/evt/select.h +2 -14
- data/ext/evt/uring.h +6 -11
- data/lib/evt.rb +9 -0
- data/lib/evt/backends/bundled.rb +155 -0
- data/lib/evt/backends/epoll.rb +27 -0
- data/lib/evt/backends/iocp.rb +33 -0
- data/lib/evt/backends/kqueue.rb +27 -0
- data/lib/evt/backends/select.rb +27 -0
- data/lib/evt/backends/uring.rb +35 -0
- data/lib/evt/scheduler.rb +27 -112
- data/lib/evt/version.rb +1 -1
- metadata +22 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f49f37d3240b1878b00acaeea453e00c9dae2a96c8c7f12889836d653d6b1835
|
4
|
+
data.tar.gz: 2af4b0238768dcb2b4112eaff379cad3899633eab69017517f36d5d51ba1fae7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/.github/workflows/test.yml
CHANGED
@@ -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
|
-
[](https://github.com/dsh0416/evt/actions?query=workflow%3A%
|
7
|
+
[](https://github.com/dsh0416/evt/actions?query=workflow%3A%22Build%22)
|
8
|
+
[](https://github.com/dsh0416/evt/actions?query=workflow%3A%22CI+Tests%22)
|
8
9
|
[](https://rubygems.org/gems/evt)
|
9
10
|
[](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
data/evt.gemspec
CHANGED
@@ -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|
|
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
|
data/ext/evt/epoll.h
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
#ifndef EPOLL_H
|
2
|
-
#define
|
2
|
+
#define EPOLL_H
|
3
3
|
#include "evt.h"
|
4
4
|
|
5
5
|
#if HAVE_SYS_EPOLL_H
|
6
|
-
VALUE
|
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
|
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
|
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
|
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
|
85
|
+
VALUE method_scheduler_epoll_backend(VALUE klass) {
|
86
86
|
return rb_str_new_cstr("epoll");
|
87
87
|
}
|
88
88
|
#endif
|
data/ext/evt/evt.c
CHANGED
@@ -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
|
-
|
13
|
-
Payload = rb_define_class_under(
|
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
|
-
|
16
|
-
|
17
|
-
rb_define_method(
|
18
|
-
rb_define_method(
|
19
|
-
rb_define_method(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
#
|
28
|
-
|
29
|
-
#
|
30
|
-
|
31
|
-
#
|
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
|
data/ext/evt/evt.h
CHANGED
@@ -4,29 +4,22 @@
|
|
4
4
|
#include <ruby.h>
|
5
5
|
|
6
6
|
VALUE Evt = Qnil;
|
7
|
-
VALUE
|
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
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
data/ext/evt/iocp.h
CHANGED
@@ -11,13 +11,13 @@ size_t iocp_payload_size(const void* data) {
|
|
11
11
|
return sizeof(HANDLE);
|
12
12
|
}
|
13
13
|
|
14
|
-
VALUE
|
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
|
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
|
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
|
118
|
+
VALUE method_scheduler_iocp_backend(VALUE klass) {
|
123
119
|
return rb_str_new_cstr("iocp");
|
124
120
|
}
|
125
121
|
#endif
|
data/ext/evt/kqueue.h
CHANGED
@@ -4,12 +4,12 @@
|
|
4
4
|
|
5
5
|
#if HAVE_SYS_EVENT_H
|
6
6
|
|
7
|
-
VALUE
|
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
|
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
|
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
|
78
|
+
VALUE method_scheduler_kqueue_backend(VALUE klass) {
|
84
79
|
return rb_str_new_cstr("kqueue");
|
85
80
|
}
|
86
81
|
#endif
|
data/ext/evt/select.h
CHANGED
@@ -2,19 +2,7 @@
|
|
2
2
|
#define SELECT_H
|
3
3
|
#include "evt.h"
|
4
4
|
|
5
|
-
VALUE
|
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
|
21
|
+
VALUE method_scheduler_select_backend(VALUE klass) {
|
34
22
|
return rb_str_new_cstr("ruby");
|
35
23
|
}
|
36
24
|
#endif
|
data/ext/evt/uring.h
CHANGED
@@ -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
|
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
|
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
|
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
|
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
|
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
|
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
|
data/lib/evt/scheduler.rb
CHANGED
@@ -1,121 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
111
|
-
|
112
|
-
|
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
|
data/lib/evt/version.rb
CHANGED
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.
|
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
|