itsi 0.2.25 → 0.2.27.rc1
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/CHANGELOG.md +6 -3
- data/Cargo.lock +940 -988
- data/Cargo.toml +0 -1
- data/Dockerfile +2 -2
- data/Rakefile +24 -7
- data/crates/itsi_acme/Cargo.toml +2 -1
- data/crates/itsi_acme/src/acceptor.rs +1 -1
- data/crates/itsi_acme/src/acme.rs +31 -3
- data/crates/itsi_acme/src/http_challenge.rs +81 -0
- data/crates/itsi_acme/src/https_helper.rs +3 -1
- data/crates/itsi_acme/src/jose.rs +6 -2
- data/crates/itsi_acme/src/lib.rs +2 -0
- data/crates/itsi_acme/src/resolver.rs +27 -4
- data/crates/itsi_acme/src/state.rs +183 -22
- data/crates/itsi_scheduler/Cargo.toml +1 -1
- data/crates/itsi_scheduler/src/itsi_scheduler.rs +115 -64
- data/crates/itsi_scheduler/src/lib.rs +2 -1
- data/crates/itsi_server/Cargo.lock +2 -2
- data/crates/itsi_server/Cargo.toml +2 -1
- data/crates/itsi_server/src/lib.rs +15 -0
- data/crates/itsi_server/src/ruby_types/itsi_http_request.rs +9 -0
- data/crates/itsi_server/src/ruby_types/itsi_http_response.rs +95 -0
- data/crates/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +22 -1
- data/crates/itsi_server/src/ruby_types/itsi_server.rs +100 -0
- data/crates/itsi_server/src/server/binds/listener.rs +9 -24
- data/crates/itsi_server/src/server/binds/tls.rs +372 -67
- data/crates/itsi_server/src/services/itsi_http_service.rs +46 -2
- data/gems/scheduler/Cargo.lock +940 -988
- data/gems/scheduler/Cargo.toml +0 -1
- data/gems/scheduler/Gemfile +8 -2
- data/gems/scheduler/Gemfile.lock +107 -0
- data/gems/scheduler/Rakefile +33 -9
- data/gems/scheduler/itsi-scheduler.gemspec +0 -2
- data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
- data/gems/scheduler/lib/itsi/scheduler.rb +121 -6
- data/gems/scheduler/test/helpers/test_helper.rb +2 -0
- data/gems/scheduler/test/test_address_resolve.rb +8 -2
- data/gems/scheduler/test/test_itsi_scheduler.rb +80 -0
- data/gems/scheduler/test/test_timeout_after.rb +102 -0
- data/gems/server/Cargo.lock +940 -988
- data/gems/server/Cargo.toml +0 -1
- data/gems/server/Gemfile +2 -0
- data/gems/server/Gemfile.lock +123 -0
- data/gems/server/Rakefile +18 -5
- data/gems/server/itsi-server.gemspec +0 -2
- data/gems/server/lib/itsi/http_request.rb +10 -0
- data/gems/server/lib/itsi/server/rack_interface.rb +45 -2
- data/gems/server/lib/itsi/server/version.rb +1 -1
- data/gems/server/lib/itsi/server.rb +24 -0
- data/gems/server/test/acme/local_acme_challenges.rb +190 -0
- data/gems/server/test/helpers/local_acme.rb +218 -0
- data/gems/server/test/helpers/test_helper.rb +7 -11
- data/gems/server/test/middleware/endpoint.rb +9 -6
- data/gems/server/test/rack/test_rack_server.rb +79 -0
- data/lib/itsi/version.rb +1 -1
- metadata +11 -59
- data/gems/scheduler/vendor/rb-sys-build/.cargo-ok +0 -1
- data/gems/scheduler/vendor/rb-sys-build/.cargo_vcs_info.json +0 -6
- data/gems/scheduler/vendor/rb-sys-build/Cargo.lock +0 -294
- data/gems/scheduler/vendor/rb-sys-build/Cargo.toml +0 -71
- data/gems/scheduler/vendor/rb-sys-build/Cargo.toml.orig +0 -32
- data/gems/scheduler/vendor/rb-sys-build/LICENSE-APACHE +0 -190
- data/gems/scheduler/vendor/rb-sys-build/LICENSE-MIT +0 -21
- data/gems/scheduler/vendor/rb-sys-build/src/bindings/sanitizer.rs +0 -185
- data/gems/scheduler/vendor/rb-sys-build/src/bindings/stable_api.rs +0 -247
- data/gems/scheduler/vendor/rb-sys-build/src/bindings/wrapper.h +0 -71
- data/gems/scheduler/vendor/rb-sys-build/src/bindings.rs +0 -280
- data/gems/scheduler/vendor/rb-sys-build/src/cc.rs +0 -421
- data/gems/scheduler/vendor/rb-sys-build/src/lib.rs +0 -12
- data/gems/scheduler/vendor/rb-sys-build/src/rb_config/flags.rs +0 -101
- data/gems/scheduler/vendor/rb-sys-build/src/rb_config/library.rs +0 -132
- data/gems/scheduler/vendor/rb-sys-build/src/rb_config/search_path.rs +0 -57
- data/gems/scheduler/vendor/rb-sys-build/src/rb_config.rs +0 -906
- data/gems/scheduler/vendor/rb-sys-build/src/utils.rs +0 -53
- data/gems/server/vendor/rb-sys-build/.cargo-ok +0 -1
- data/gems/server/vendor/rb-sys-build/.cargo_vcs_info.json +0 -6
- data/gems/server/vendor/rb-sys-build/Cargo.lock +0 -294
- data/gems/server/vendor/rb-sys-build/Cargo.toml +0 -71
- data/gems/server/vendor/rb-sys-build/Cargo.toml.orig +0 -32
- data/gems/server/vendor/rb-sys-build/LICENSE-APACHE +0 -190
- data/gems/server/vendor/rb-sys-build/LICENSE-MIT +0 -21
- data/gems/server/vendor/rb-sys-build/src/bindings/sanitizer.rs +0 -185
- data/gems/server/vendor/rb-sys-build/src/bindings/stable_api.rs +0 -247
- data/gems/server/vendor/rb-sys-build/src/bindings/wrapper.h +0 -71
- data/gems/server/vendor/rb-sys-build/src/bindings.rs +0 -280
- data/gems/server/vendor/rb-sys-build/src/cc.rs +0 -421
- data/gems/server/vendor/rb-sys-build/src/lib.rs +0 -12
- data/gems/server/vendor/rb-sys-build/src/rb_config/flags.rs +0 -101
- data/gems/server/vendor/rb-sys-build/src/rb_config/library.rs +0 -132
- data/gems/server/vendor/rb-sys-build/src/rb_config/search_path.rs +0 -57
- data/gems/server/vendor/rb-sys-build/src/rb_config.rs +0 -906
- data/gems/server/vendor/rb-sys-build/src/utils.rs +0 -53
- data/vendor/rb-sys-build/.cargo-ok +0 -1
- data/vendor/rb-sys-build/.cargo_vcs_info.json +0 -6
- data/vendor/rb-sys-build/Cargo.lock +0 -294
- data/vendor/rb-sys-build/Cargo.toml +0 -71
- data/vendor/rb-sys-build/Cargo.toml.orig +0 -32
- data/vendor/rb-sys-build/LICENSE-APACHE +0 -190
- data/vendor/rb-sys-build/LICENSE-MIT +0 -21
- data/vendor/rb-sys-build/src/bindings/sanitizer.rs +0 -185
- data/vendor/rb-sys-build/src/bindings/stable_api.rs +0 -247
- data/vendor/rb-sys-build/src/bindings/wrapper.h +0 -71
- data/vendor/rb-sys-build/src/bindings.rs +0 -280
- data/vendor/rb-sys-build/src/cc.rs +0 -421
- data/vendor/rb-sys-build/src/lib.rs +0 -12
- data/vendor/rb-sys-build/src/rb_config/flags.rs +0 -101
- data/vendor/rb-sys-build/src/rb_config/library.rs +0 -132
- data/vendor/rb-sys-build/src/rb_config/search_path.rs +0 -57
- data/vendor/rb-sys-build/src/rb_config.rs +0 -906
- data/vendor/rb-sys-build/src/utils.rs +0 -53
data/gems/scheduler/Cargo.toml
CHANGED
data/gems/scheduler/Gemfile
CHANGED
|
@@ -2,10 +2,16 @@ source "https://rubygems.org"
|
|
|
2
2
|
|
|
3
3
|
gemspec
|
|
4
4
|
|
|
5
|
-
group :development
|
|
5
|
+
group :development do
|
|
6
6
|
gem "bundler"
|
|
7
|
-
gem "minitest", "~> 5.16"
|
|
8
7
|
gem "rake", "~> 13.0"
|
|
9
8
|
gem "rake-compiler"
|
|
10
9
|
gem "rb_sys", "~> 0.9.91"
|
|
11
10
|
end
|
|
11
|
+
|
|
12
|
+
group :test do
|
|
13
|
+
gem "activerecord"
|
|
14
|
+
gem "minitest", "~> 5.16"
|
|
15
|
+
gem "minitest-reporters"
|
|
16
|
+
gem "pg"
|
|
17
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
itsi-scheduler (0.2.27.rc1)
|
|
5
|
+
rb_sys (~> 0.9.91)
|
|
6
|
+
|
|
7
|
+
GEM
|
|
8
|
+
remote: https://rubygems.org/
|
|
9
|
+
specs:
|
|
10
|
+
activemodel (8.1.3)
|
|
11
|
+
activesupport (= 8.1.3)
|
|
12
|
+
activerecord (8.1.3)
|
|
13
|
+
activemodel (= 8.1.3)
|
|
14
|
+
activesupport (= 8.1.3)
|
|
15
|
+
timeout (>= 0.4.0)
|
|
16
|
+
activesupport (8.1.3)
|
|
17
|
+
base64
|
|
18
|
+
bigdecimal
|
|
19
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
20
|
+
connection_pool (>= 2.2.5)
|
|
21
|
+
drb
|
|
22
|
+
i18n (>= 1.6, < 2)
|
|
23
|
+
json
|
|
24
|
+
logger (>= 1.4.2)
|
|
25
|
+
minitest (>= 5.1)
|
|
26
|
+
securerandom (>= 0.3)
|
|
27
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
28
|
+
uri (>= 0.13.1)
|
|
29
|
+
ansi (1.6.0)
|
|
30
|
+
base64 (0.3.0)
|
|
31
|
+
bigdecimal (4.1.2)
|
|
32
|
+
builder (3.3.0)
|
|
33
|
+
concurrent-ruby (1.3.6)
|
|
34
|
+
connection_pool (3.0.2)
|
|
35
|
+
drb (2.2.3)
|
|
36
|
+
i18n (1.14.8)
|
|
37
|
+
concurrent-ruby (~> 1.0)
|
|
38
|
+
json (2.19.4)
|
|
39
|
+
logger (1.7.0)
|
|
40
|
+
minitest (5.27.0)
|
|
41
|
+
minitest-reporters (1.8.0)
|
|
42
|
+
ansi
|
|
43
|
+
builder
|
|
44
|
+
minitest (>= 5.0, < 7)
|
|
45
|
+
ruby-progressbar
|
|
46
|
+
pg (1.6.3)
|
|
47
|
+
pg (1.6.3-arm64-darwin)
|
|
48
|
+
rake (13.3.1)
|
|
49
|
+
rake-compiler (1.3.0)
|
|
50
|
+
rake
|
|
51
|
+
rake-compiler-dock (1.11.0)
|
|
52
|
+
rb_sys (0.9.126)
|
|
53
|
+
json (>= 2)
|
|
54
|
+
rake-compiler-dock (= 1.11.0)
|
|
55
|
+
ruby-progressbar (1.13.0)
|
|
56
|
+
securerandom (0.4.1)
|
|
57
|
+
timeout (0.6.1)
|
|
58
|
+
tzinfo (2.0.6)
|
|
59
|
+
concurrent-ruby (~> 1.0)
|
|
60
|
+
uri (1.1.1)
|
|
61
|
+
|
|
62
|
+
PLATFORMS
|
|
63
|
+
arm64-darwin-24
|
|
64
|
+
ruby
|
|
65
|
+
|
|
66
|
+
DEPENDENCIES
|
|
67
|
+
activerecord
|
|
68
|
+
bundler
|
|
69
|
+
itsi-scheduler!
|
|
70
|
+
minitest (~> 5.16)
|
|
71
|
+
minitest-reporters
|
|
72
|
+
pg
|
|
73
|
+
rake (~> 13.0)
|
|
74
|
+
rake-compiler
|
|
75
|
+
rb_sys (~> 0.9.91)
|
|
76
|
+
|
|
77
|
+
CHECKSUMS
|
|
78
|
+
activemodel (8.1.3) sha256=90c05cbe4cef3649b8f79f13016191ea94c4525ce4a5c0fb7ef909c4b91c8219
|
|
79
|
+
activerecord (8.1.3) sha256=8003be7b2466ba0a2a670e603eeb0a61dd66058fccecfc49901e775260ac70ab
|
|
80
|
+
activesupport (8.1.3) sha256=21a5e0dfbd4c3ddd9e1317ec6a4d782fa226e7867dc70b0743acda81a1dca20e
|
|
81
|
+
ansi (1.6.0) sha256=ac9ea0c0ea8d32fb4e271348e609963ac78882f34b73836c2a02b3622e666658
|
|
82
|
+
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
83
|
+
bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
|
|
84
|
+
builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
|
|
85
|
+
concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
|
|
86
|
+
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
|
|
87
|
+
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
|
|
88
|
+
i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
|
|
89
|
+
itsi-scheduler (0.2.27.rc1)
|
|
90
|
+
json (2.19.4) sha256=670a7d333fb3b18ca5b29cb255eb7bef099e40d88c02c80bd42a3f30fe5239ac
|
|
91
|
+
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
92
|
+
minitest (5.27.0) sha256=2d3b17f8a36fe7801c1adcffdbc38233b938eb0b4966e97a6739055a45fa77d5
|
|
93
|
+
minitest-reporters (1.8.0) sha256=8ce5280fb73ad3178ae525454df169b6f28c1b38b1d088ea91815d3a370ba384
|
|
94
|
+
pg (1.6.3) sha256=1388d0563e13d2758c1089e35e973a3249e955c659592d10e5b77c468f628a99
|
|
95
|
+
pg (1.6.3-arm64-darwin) sha256=7240330b572e6355d7c75a7de535edb5dfcbd6295d9c7777df4d9dddfb8c0e5f
|
|
96
|
+
rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
|
|
97
|
+
rake-compiler (1.3.0) sha256=eec272ef6d4dad27b36f5cdcf5b9ee4df2193751f4082b095f981ebf9cdf4127
|
|
98
|
+
rake-compiler-dock (1.11.0) sha256=eab51f2cd533eb35cea6b624a75281f047123e70a64c58b607471bb49428f8c2
|
|
99
|
+
rb_sys (0.9.126) sha256=ba958e0b8b4b89eeae0b3d24b64c809eb2c37e0ab0773a49e9b1c2e22c95aef8
|
|
100
|
+
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
|
101
|
+
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
|
102
|
+
timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb
|
|
103
|
+
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
|
|
104
|
+
uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
|
|
105
|
+
|
|
106
|
+
BUNDLED WITH
|
|
107
|
+
2.6.9
|
data/gems/scheduler/Rakefile
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "bundler/gem_tasks"
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
def test_tasks_requested?
|
|
6
|
+
requested = Rake.application.top_level_tasks
|
|
7
|
+
return true if requested.empty?
|
|
8
|
+
|
|
9
|
+
requested.any? do |task_name|
|
|
10
|
+
task_name == "test" || task_name.start_with?("test:")
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
require "minitest/test_task" if test_tasks_requested?
|
|
5
15
|
|
|
6
16
|
SMOKE_TEST_GLOBS = %w[
|
|
7
17
|
test/test_itsi_scheduler.rb
|
|
@@ -11,6 +21,16 @@ SMOKE_TEST_GLOBS = %w[
|
|
|
11
21
|
test/test_file_io.rb
|
|
12
22
|
].freeze
|
|
13
23
|
|
|
24
|
+
def native_build_tasks_requested?
|
|
25
|
+
requested = Rake.application.top_level_tasks
|
|
26
|
+
return true if requested.empty?
|
|
27
|
+
|
|
28
|
+
requested.any? do |task_name|
|
|
29
|
+
task_name == "default" ||
|
|
30
|
+
task_name.start_with?("build", "compile", "cross", "clobber")
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
14
34
|
def configure_test_task(task_name, test_globs)
|
|
15
35
|
Minitest::TestTask.create(task_name) do |t|
|
|
16
36
|
t.libs << "test"
|
|
@@ -21,19 +41,23 @@ def configure_test_task(task_name, test_globs)
|
|
|
21
41
|
end
|
|
22
42
|
end
|
|
23
43
|
|
|
24
|
-
|
|
25
|
-
configure_test_task("test
|
|
44
|
+
if test_tasks_requested?
|
|
45
|
+
configure_test_task(:test, ["test/**/*.rb"])
|
|
46
|
+
configure_test_task("test:smoke", SMOKE_TEST_GLOBS)
|
|
26
47
|
|
|
27
|
-
task "test:full" => :test
|
|
48
|
+
task "test:full" => :test
|
|
49
|
+
end
|
|
28
50
|
|
|
29
|
-
|
|
51
|
+
if native_build_tasks_requested?
|
|
52
|
+
require "rb_sys/extensiontask"
|
|
30
53
|
|
|
31
|
-
task build: :compile
|
|
54
|
+
task build: :compile
|
|
32
55
|
|
|
33
|
-
GEMSPEC = Gem::Specification.load("itsi-scheduler.gemspec")
|
|
56
|
+
GEMSPEC = Gem::Specification.load("itsi-scheduler.gemspec")
|
|
34
57
|
|
|
35
|
-
RbSys::ExtensionTask.new("itsi-scheduler", GEMSPEC) do |ext|
|
|
36
|
-
|
|
58
|
+
RbSys::ExtensionTask.new("itsi-scheduler", GEMSPEC) do |ext|
|
|
59
|
+
ext.lib_dir = "lib/itsi/scheduler"
|
|
60
|
+
end
|
|
37
61
|
end
|
|
38
62
|
|
|
39
63
|
task default: %i[compile test rubocop]
|
|
@@ -30,8 +30,6 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
end + Dir["../../crates/**/*.{toml,rs,lock}"].map do |ext_file|
|
|
31
31
|
"ext/#{ext_file[%r{.*crates/(.*?)$}, 1]}"
|
|
32
32
|
end.compact +
|
|
33
|
-
Dir["vendor/rb-sys-build/**/*.{toml,rs,h,lock}"] +
|
|
34
|
-
Dir["vendor/rb-sys-build/LICENSE-*"] +
|
|
35
33
|
Dir.glob("lib/**/*.{rb,bundle,so,dylib,dll}")
|
|
36
34
|
spec.files.uniq!
|
|
37
35
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "etc"
|
|
4
|
+
|
|
3
5
|
require_relative "scheduler/version"
|
|
4
6
|
require_relative "scheduler/native_extension"
|
|
5
7
|
require_relative "schedule_refinement"
|
|
@@ -7,6 +9,7 @@ require_relative "schedule_refinement"
|
|
|
7
9
|
module Itsi
|
|
8
10
|
class Scheduler
|
|
9
11
|
class Error < StandardError; end
|
|
12
|
+
WorkRequest = Struct.new(:fiber, :work, :result, :error, keyword_init: true)
|
|
10
13
|
|
|
11
14
|
def self.resume_token
|
|
12
15
|
@resume_token ||= 0
|
|
@@ -17,12 +20,14 @@ module Itsi
|
|
|
17
20
|
@join_waiters = {}.compare_by_identity
|
|
18
21
|
@token_map = {}.compare_by_identity
|
|
19
22
|
@resume_tokens = {}.compare_by_identity
|
|
23
|
+
@timeout_requests = {}
|
|
20
24
|
@unblocked = [[], []]
|
|
21
25
|
@unblock_idx = 0
|
|
22
26
|
@unblocked_mux = Mutex.new
|
|
23
27
|
@resume_fiber = method(:resume_fiber).to_proc
|
|
24
28
|
@resume_fiber_with_readiness = method(:resume_fiber_with_readiness).to_proc
|
|
25
29
|
@resume_blocked = method(:resume_blocked).to_proc
|
|
30
|
+
setup_worker_pool
|
|
26
31
|
end
|
|
27
32
|
|
|
28
33
|
def block(_, timeout, fiber = Fiber.current, token = Scheduler.resume_token)
|
|
@@ -33,6 +38,7 @@ module Itsi
|
|
|
33
38
|
@token_map[fiber] = token
|
|
34
39
|
Fiber.yield
|
|
35
40
|
ensure
|
|
41
|
+
cancel_wait(token)
|
|
36
42
|
@resume_tokens.delete(token)
|
|
37
43
|
@token_map.delete(fiber)
|
|
38
44
|
@join_waiters.delete(fiber)
|
|
@@ -61,6 +67,60 @@ module Itsi
|
|
|
61
67
|
block nil, duration
|
|
62
68
|
end
|
|
63
69
|
|
|
70
|
+
def timeout_after(duration, klass = Timeout::Error, message = "execution expired")
|
|
71
|
+
fiber = Fiber.current
|
|
72
|
+
token = Scheduler.resume_token
|
|
73
|
+
exception = klass.is_a?(Class) ? klass.new(message) : klass
|
|
74
|
+
@timeout_requests[token] = [fiber, exception]
|
|
75
|
+
start_timer(duration, token)
|
|
76
|
+
yield duration
|
|
77
|
+
ensure
|
|
78
|
+
clear_timer(token) if token
|
|
79
|
+
@timeout_requests.delete(token) if token
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def fiber_interrupt(fiber, exception)
|
|
83
|
+
cancel_wait(@token_map[fiber]) if @token_map.key?(fiber)
|
|
84
|
+
fiber.raise(exception)
|
|
85
|
+
true
|
|
86
|
+
rescue FiberError
|
|
87
|
+
false
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def blocking_operation_wait(work)
|
|
91
|
+
request = WorkRequest.new(fiber: Fiber.current, work: work)
|
|
92
|
+
@worker_queue << request
|
|
93
|
+
block(nil, nil, request.fiber)
|
|
94
|
+
raise request.error if request.error
|
|
95
|
+
|
|
96
|
+
request.result
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def io_select(readables, writables, exceptables, timeout)
|
|
100
|
+
readables = Array(readables).compact
|
|
101
|
+
writables = Array(writables).compact
|
|
102
|
+
exceptables = Array(exceptables).compact
|
|
103
|
+
ios = (readables + writables + exceptables).uniq
|
|
104
|
+
|
|
105
|
+
if ios.length == 1
|
|
106
|
+
io = ios.first
|
|
107
|
+
events = 0
|
|
108
|
+
events |= IO::READABLE if readables.include?(io)
|
|
109
|
+
events |= IO::WRITABLE if writables.include?(io)
|
|
110
|
+
events |= IO::PRIORITY if exceptables.include?(io)
|
|
111
|
+
readiness = io_wait(io, events, timeout)
|
|
112
|
+
return nil unless readiness
|
|
113
|
+
|
|
114
|
+
return [
|
|
115
|
+
(readiness & IO::READABLE).zero? ? [] : readables.select { |entry| entry == io },
|
|
116
|
+
(readiness & IO::WRITABLE).zero? ? [] : writables.select { |entry| entry == io },
|
|
117
|
+
(readiness & IO::PRIORITY).zero? ? [] : exceptables.select { |entry| entry == io }
|
|
118
|
+
]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
blocking_operation_wait(-> { IO.select(readables, writables, exceptables, timeout) })
|
|
122
|
+
end
|
|
123
|
+
|
|
64
124
|
def tick
|
|
65
125
|
events = fetch_due_events
|
|
66
126
|
timers = fetch_due_timers
|
|
@@ -72,6 +132,12 @@ module Itsi
|
|
|
72
132
|
end
|
|
73
133
|
|
|
74
134
|
def resume_fiber(token)
|
|
135
|
+
if (request = @timeout_requests.delete(token))
|
|
136
|
+
fiber, exception = request
|
|
137
|
+
fiber_interrupt(fiber, exception)
|
|
138
|
+
return
|
|
139
|
+
end
|
|
140
|
+
|
|
75
141
|
if (fiber = @resume_tokens.delete(token))
|
|
76
142
|
fiber.resume
|
|
77
143
|
end
|
|
@@ -126,6 +192,7 @@ module Itsi
|
|
|
126
192
|
def close
|
|
127
193
|
run
|
|
128
194
|
ensure
|
|
195
|
+
shutdown_worker_pool
|
|
129
196
|
@closed ||= true
|
|
130
197
|
freeze
|
|
131
198
|
end
|
|
@@ -133,12 +200,17 @@ module Itsi
|
|
|
133
200
|
# Need to defer to Process::Status rather than our extension
|
|
134
201
|
# as we don't have a means of creating our own Process::Status.
|
|
135
202
|
def process_wait(pid, flags)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
203
|
+
blocking_operation_wait(-> { Process::Status.wait(pid, flags) })
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def address_resolve(hostname)
|
|
207
|
+
blocking_operation_wait(-> { native_address_resolve(hostname) })
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def process_fork
|
|
211
|
+
shutdown_worker_pool
|
|
212
|
+
setup_worker_pool
|
|
213
|
+
nil
|
|
142
214
|
end
|
|
143
215
|
|
|
144
216
|
def closed?
|
|
@@ -149,5 +221,48 @@ module Itsi
|
|
|
149
221
|
def fiber(&blk)
|
|
150
222
|
Fiber.new(blocking: false, &blk).tap(&:resume)
|
|
151
223
|
end
|
|
224
|
+
|
|
225
|
+
private
|
|
226
|
+
|
|
227
|
+
def setup_worker_pool
|
|
228
|
+
@worker_stop_token = Object.new
|
|
229
|
+
@worker_queue = Queue.new
|
|
230
|
+
@worker_threads = Array.new(worker_pool_size) { start_worker_thread }
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def start_worker_thread
|
|
234
|
+
Thread.new do
|
|
235
|
+
Thread.current.report_on_exception = false
|
|
236
|
+
Thread.current.thread_variable_set(:fork_safe, true)
|
|
237
|
+
|
|
238
|
+
loop do
|
|
239
|
+
request = @worker_queue.pop
|
|
240
|
+
break if request.equal?(@worker_stop_token)
|
|
241
|
+
|
|
242
|
+
begin
|
|
243
|
+
request.result = request.work.call
|
|
244
|
+
rescue Exception => exception
|
|
245
|
+
request.error = exception
|
|
246
|
+
ensure
|
|
247
|
+
unblock(nil, request.fiber)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def shutdown_worker_pool
|
|
254
|
+
return unless @worker_threads
|
|
255
|
+
|
|
256
|
+
@worker_threads.size.times { @worker_queue << @worker_stop_token }
|
|
257
|
+
@worker_threads.each(&:join)
|
|
258
|
+
@worker_threads.clear
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def worker_pool_size
|
|
262
|
+
size = ENV.fetch("ITSI_WORKER_POOL_SIZE", Etc.nprocessors.to_s).to_i
|
|
263
|
+
size.positive? ? size : 1
|
|
264
|
+
rescue StandardError
|
|
265
|
+
1
|
|
266
|
+
end
|
|
152
267
|
end
|
|
153
268
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "ipaddr"
|
|
4
|
+
|
|
3
5
|
class TestAddressResolve < Minitest::Test
|
|
4
6
|
include Itsi::Scheduler::TestHelper
|
|
5
7
|
|
|
@@ -15,7 +17,11 @@ class TestAddressResolve < Minitest::Test
|
|
|
15
17
|
end
|
|
16
18
|
end
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
assert results.all?
|
|
20
|
+
assert_equal 2, results.length
|
|
21
|
+
assert results.all?(&:any?)
|
|
22
|
+
results.flatten.each do |addrinfo|
|
|
23
|
+
assert_instance_of Addrinfo, addrinfo
|
|
24
|
+
assert IPAddr.new(addrinfo.ip_address)
|
|
25
|
+
end
|
|
20
26
|
end
|
|
21
27
|
end
|
|
@@ -29,4 +29,84 @@ class TestItsiScheduler < Minitest::Test
|
|
|
29
29
|
assert_equal total, 6
|
|
30
30
|
assert_match /terminated on exception/, out
|
|
31
31
|
end
|
|
32
|
+
|
|
33
|
+
def test_blocking_operation_wait_returns_result_without_stalling_scheduler
|
|
34
|
+
result = nil
|
|
35
|
+
marker = nil
|
|
36
|
+
|
|
37
|
+
with_scheduler do |scheduler|
|
|
38
|
+
Fiber.schedule do
|
|
39
|
+
result = scheduler.blocking_operation_wait(-> do
|
|
40
|
+
sleep 0.05
|
|
41
|
+
:done
|
|
42
|
+
end)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Fiber.schedule do
|
|
46
|
+
sleep 0.01
|
|
47
|
+
marker = :progressed
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
assert_equal :done, result
|
|
52
|
+
assert_equal :progressed, marker
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_blocking_operation_wait_propagates_exceptions
|
|
56
|
+
error = nil
|
|
57
|
+
|
|
58
|
+
with_scheduler do |scheduler|
|
|
59
|
+
Fiber.schedule do
|
|
60
|
+
begin
|
|
61
|
+
scheduler.blocking_operation_wait(-> { raise ArgumentError, "boom" })
|
|
62
|
+
rescue => exception
|
|
63
|
+
error = exception
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
refute_nil error
|
|
69
|
+
assert_equal ArgumentError, error.class
|
|
70
|
+
assert_equal "boom", error.message
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def test_io_select_returns_ready_descriptors_without_stalling_scheduler
|
|
74
|
+
ready = nil
|
|
75
|
+
marker = nil
|
|
76
|
+
reader, writer = IO.pipe
|
|
77
|
+
|
|
78
|
+
with_scheduler do |scheduler|
|
|
79
|
+
Fiber.schedule do
|
|
80
|
+
ready = scheduler.io_select([reader], nil, nil, 0.2)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
Fiber.schedule do
|
|
84
|
+
sleep 0.01
|
|
85
|
+
marker = :progressed
|
|
86
|
+
writer.write("x")
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
assert_equal :progressed, marker
|
|
91
|
+
assert_equal [[reader], [], []], ready
|
|
92
|
+
ensure
|
|
93
|
+
reader&.close
|
|
94
|
+
writer&.close
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def test_process_fork_reinitializes_worker_pool
|
|
98
|
+
worker_threads = nil
|
|
99
|
+
refreshed_threads = nil
|
|
100
|
+
|
|
101
|
+
with_scheduler do |scheduler|
|
|
102
|
+
worker_threads = scheduler.instance_variable_get(:@worker_threads)
|
|
103
|
+
scheduler.process_fork
|
|
104
|
+
refreshed_threads = scheduler.instance_variable_get(:@worker_threads)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
refute_nil worker_threads
|
|
108
|
+
refute_nil refreshed_threads
|
|
109
|
+
refute_same worker_threads, refreshed_threads
|
|
110
|
+
assert_equal worker_threads.length, refreshed_threads.length
|
|
111
|
+
end
|
|
32
112
|
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "timeout"
|
|
4
|
+
|
|
5
|
+
class TestTimeoutAfter < Minitest::Test
|
|
6
|
+
include Itsi::Scheduler::TestHelper
|
|
7
|
+
|
|
8
|
+
def test_timeout_after_raises_timeout_error
|
|
9
|
+
result = nil
|
|
10
|
+
|
|
11
|
+
with_scheduler do
|
|
12
|
+
Fiber.schedule do
|
|
13
|
+
begin
|
|
14
|
+
Timeout.timeout(0.01, Timeout::Error, "benchmark timeout") do
|
|
15
|
+
sleep 0.05
|
|
16
|
+
end
|
|
17
|
+
rescue Timeout::Error => error
|
|
18
|
+
result = error.message
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
assert_equal "benchmark timeout", result
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_timeout_after_cancels_fast_work_without_waiting_for_timeout
|
|
27
|
+
result = nil
|
|
28
|
+
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
29
|
+
|
|
30
|
+
with_scheduler do
|
|
31
|
+
Fiber.schedule do
|
|
32
|
+
Timeout.timeout(0.05, Timeout::Error, "benchmark timeout") do
|
|
33
|
+
sleep 0.001
|
|
34
|
+
result = :completed
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
|
|
40
|
+
|
|
41
|
+
assert_equal :completed, result
|
|
42
|
+
assert_operator elapsed, :<, 0.03
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_timeout_after_interrupts_io_wait_without_leaking_waiters
|
|
46
|
+
result = nil
|
|
47
|
+
reader, writer = IO.pipe
|
|
48
|
+
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
49
|
+
|
|
50
|
+
with_scheduler do
|
|
51
|
+
Fiber.schedule do
|
|
52
|
+
begin
|
|
53
|
+
Timeout.timeout(0.01, Timeout::Error, "benchmark timeout") do
|
|
54
|
+
reader.readpartial(1)
|
|
55
|
+
end
|
|
56
|
+
rescue Timeout::Error
|
|
57
|
+
result = :timed_out
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
|
|
63
|
+
|
|
64
|
+
assert_equal :timed_out, result
|
|
65
|
+
assert_operator elapsed, :<, 0.05
|
|
66
|
+
ensure
|
|
67
|
+
reader&.close
|
|
68
|
+
writer&.close
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_cascading_timeout_workload_completes
|
|
72
|
+
completed = 0
|
|
73
|
+
timed_out = 0
|
|
74
|
+
total = 0
|
|
75
|
+
|
|
76
|
+
with_scheduler do
|
|
77
|
+
20.times do |index|
|
|
78
|
+
Fiber.schedule do
|
|
79
|
+
50.times do |iteration|
|
|
80
|
+
slow = ((iteration * 7 + index * 3) % 10) < 3
|
|
81
|
+
|
|
82
|
+
begin
|
|
83
|
+
Timeout.timeout(0.01, Timeout::Error, "benchmark timeout") do
|
|
84
|
+
sleep(slow ? 0.05 : 0.002)
|
|
85
|
+
completed += 1
|
|
86
|
+
end
|
|
87
|
+
rescue Timeout::Error
|
|
88
|
+
timed_out += 1
|
|
89
|
+
ensure
|
|
90
|
+
total += 1
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
assert_equal 1000, total
|
|
98
|
+
assert_operator completed, :>, 0
|
|
99
|
+
assert_operator timed_out, :>, 0
|
|
100
|
+
assert_equal total, completed + timed_out
|
|
101
|
+
end
|
|
102
|
+
end
|