evt 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +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
|
-
[![CI Tests](https://github.com/dsh0416/evt/workflows/CI%20Tests/badge.svg)](https://github.com/dsh0416/evt/actions?query=workflow%3A%
|
7
|
+
[![CI Tests](https://github.com/dsh0416/evt/workflows/CI%20Tests/badge.svg)](https://github.com/dsh0416/evt/actions?query=workflow%3A%22Build%22)
|
8
|
+
[![Build](https://github.com/dsh0416/evt/workflows/Build/badge.svg)](https://github.com/dsh0416/evt/actions?query=workflow%3A%22CI+Tests%22)
|
8
9
|
[![Gem Version](https://badge.fury.io/rb/evt.svg)](https://rubygems.org/gems/evt)
|
9
10
|
[![Downloads](https://ruby-gem-downloads-badge.herokuapp.com/evt?type=total)](https://rubygems.org/gems/evt)
|
10
11
|
|
@@ -69,8 +70,6 @@ Fiber.schedule do
|
|
69
70
|
wr.close
|
70
71
|
end
|
71
72
|
|
72
|
-
scheduler.run
|
73
|
-
|
74
73
|
# "Hello World"
|
75
74
|
```
|
76
75
|
|
data/Rakefile
CHANGED
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
|