fiber_scheduler 0.0.1
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 +7 -0
- data/lib/fiber/scheduler.rb +1 -0
- data/lib/fiber_scheduler/trigger.rb +40 -0
- data/lib/fiber_scheduler/triggers.rb +72 -0
- data/lib/fiber_scheduler/version.rb +3 -0
- data/lib/fiber_scheduler.rb +150 -0
- metadata +117 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fd5761664b48ce8937367c38ae5424c2aad1ffcbac56fac2c002de020f08b6fc
|
4
|
+
data.tar.gz: 426a54cbbd96a1b5eb54794b2a6b295be2af98413cbfb7978780ca57ff8347f7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9cdf31791771ecd2fec6fee05a7849954e71a2d6596ea429ec99300a558bd8c129a8e402e7bcae6c1c093765ac9738b4944e70de26a423ae62c440f4c9d10e18
|
7
|
+
data.tar.gz: 4cf8d6bd02b7a51ef03e902da0bb25bbd5fae16ebf82c1b5aa7cf68d84b723b458be1b9106258fff527e17704ffba3850a0b377b735bf21c0638b62c7930d20d
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "../fiber_scheduler"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class FiberScheduler
|
2
|
+
class Trigger
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
attr_reader :time
|
6
|
+
|
7
|
+
def initialize(duration, &block)
|
8
|
+
@time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + duration
|
9
|
+
@block = block
|
10
|
+
|
11
|
+
@disabled = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def <=>(other)
|
15
|
+
raise unless other.is_a?(self.class)
|
16
|
+
|
17
|
+
@time <=> other.time
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
@block.call
|
22
|
+
end
|
23
|
+
|
24
|
+
def interval
|
25
|
+
@time - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
26
|
+
end
|
27
|
+
|
28
|
+
def disable
|
29
|
+
@disabled = true
|
30
|
+
end
|
31
|
+
|
32
|
+
def disabled?
|
33
|
+
@disabled
|
34
|
+
end
|
35
|
+
|
36
|
+
def inspect
|
37
|
+
"#<#{self.class} time=#{@time}>"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require_relative "trigger"
|
2
|
+
|
3
|
+
class FiberScheduler
|
4
|
+
class Triggers
|
5
|
+
def initialize
|
6
|
+
# Array is sorted by Trigger#time
|
7
|
+
@triggers = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
12
|
+
|
13
|
+
while @triggers.any? && @triggers.first.time <= now
|
14
|
+
trigger = @triggers.shift
|
15
|
+
unless trigger.disabled?
|
16
|
+
trigger.call
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def add(duration, &block)
|
22
|
+
trigger = Trigger.new(duration, &block)
|
23
|
+
|
24
|
+
if @triggers.empty?
|
25
|
+
@triggers << trigger
|
26
|
+
return trigger
|
27
|
+
end
|
28
|
+
|
29
|
+
# binary search
|
30
|
+
min = 0
|
31
|
+
max = @triggers.size - 1
|
32
|
+
while min <= max
|
33
|
+
index = (min + max) / 2
|
34
|
+
t = @triggers[index]
|
35
|
+
|
36
|
+
if t > trigger
|
37
|
+
if index.zero? || @triggers[index - 1] <= trigger
|
38
|
+
# found it
|
39
|
+
break
|
40
|
+
else
|
41
|
+
# @triggers[index - 1] > trigger
|
42
|
+
max = index - 1
|
43
|
+
end
|
44
|
+
else
|
45
|
+
# t <= trigger
|
46
|
+
index += 1
|
47
|
+
min = index
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
@triggers.insert(index, trigger)
|
52
|
+
trigger
|
53
|
+
end
|
54
|
+
|
55
|
+
def interval
|
56
|
+
# Prune disabled triggers
|
57
|
+
while @triggers.first&.disabled?
|
58
|
+
@triggers.shift
|
59
|
+
end
|
60
|
+
|
61
|
+
return if @triggers.empty?
|
62
|
+
|
63
|
+
interval = @triggers.first.interval
|
64
|
+
|
65
|
+
interval >= 0 ? interval : 0
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
@triggers.inspect
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require "io/event"
|
2
|
+
require "resolv"
|
3
|
+
require_relative "fiber_scheduler/triggers"
|
4
|
+
|
5
|
+
module Kernel
|
6
|
+
def FiberScheduler(&block)
|
7
|
+
if Fiber.scheduler.nil?
|
8
|
+
scheduler = FiberScheduler.new
|
9
|
+
Fiber.set_scheduler(scheduler)
|
10
|
+
|
11
|
+
begin
|
12
|
+
yield
|
13
|
+
scheduler.close
|
14
|
+
ensure
|
15
|
+
Fiber.set_scheduler(nil)
|
16
|
+
end
|
17
|
+
else
|
18
|
+
# Fiber.scheduler already set, just schedule a task.
|
19
|
+
Fiber.schedule(&block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class FiberScheduler
|
25
|
+
TimeoutError = Class.new(RuntimeError)
|
26
|
+
IOWaitTimeout = Class.new(TimeoutError)
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@selector = IO::Event::Selector.new(Fiber.current)
|
30
|
+
@triggers = Triggers.new
|
31
|
+
|
32
|
+
@count = 0
|
33
|
+
@nested = []
|
34
|
+
end
|
35
|
+
|
36
|
+
def run
|
37
|
+
while @count > 0
|
38
|
+
if @nested.empty?
|
39
|
+
@selector.select(@triggers.interval)
|
40
|
+
@triggers.call
|
41
|
+
else
|
42
|
+
while @nested.any?
|
43
|
+
fiber = @nested.pop
|
44
|
+
fiber.transfer
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Fiber::SchedulerInterface methods below
|
51
|
+
|
52
|
+
def close
|
53
|
+
return unless @selector
|
54
|
+
|
55
|
+
begin
|
56
|
+
run
|
57
|
+
ensure
|
58
|
+
@selector.close
|
59
|
+
@selector = nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def block(blocker, timeout)
|
64
|
+
return @selector.transfer unless timeout
|
65
|
+
|
66
|
+
fiber = Fiber.current
|
67
|
+
trigger = @triggers.add(timeout) do
|
68
|
+
fiber.transfer if fiber.alive?
|
69
|
+
end
|
70
|
+
|
71
|
+
begin
|
72
|
+
@selector.transfer
|
73
|
+
ensure
|
74
|
+
trigger.disable
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def unblock(blocker, fiber)
|
79
|
+
@selector.push(fiber)
|
80
|
+
end
|
81
|
+
|
82
|
+
def kernel_sleep(duration = nil)
|
83
|
+
return @selector.transfer unless duration
|
84
|
+
|
85
|
+
block(:sleep, duration)
|
86
|
+
end
|
87
|
+
|
88
|
+
def address_resolve(hostname)
|
89
|
+
Resolv.getaddresses(hostname)
|
90
|
+
end
|
91
|
+
|
92
|
+
def io_wait(io, events, timeout = nil)
|
93
|
+
fiber = Fiber.current
|
94
|
+
return @selector.io_wait(fiber, io, events) unless timeout
|
95
|
+
|
96
|
+
trigger = @triggers.raise_in(timeout, IOWaitTimeout)
|
97
|
+
# trigger = @triggers.add(timeout) do
|
98
|
+
# fiber.raise(IOWaitTimeout) if fiber.alive?
|
99
|
+
# end
|
100
|
+
|
101
|
+
begin
|
102
|
+
@selector.io_wait(fiber, io, events)
|
103
|
+
rescue IOWaitTimeout
|
104
|
+
false
|
105
|
+
ensure
|
106
|
+
trigger.disable
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def io_read(io, buffer, length)
|
111
|
+
@selector.io_read(Fiber.current, io, buffer, length)
|
112
|
+
end
|
113
|
+
|
114
|
+
def io_write(io, buffer, length)
|
115
|
+
@selector.io_write(Fiber.current, io, buffer, length)
|
116
|
+
end
|
117
|
+
|
118
|
+
def process_wait(pid, flags)
|
119
|
+
@selector.process_wait(Fiber.current, pid, flags)
|
120
|
+
end
|
121
|
+
|
122
|
+
def timeout_after(duration, exception = TimeoutError, message = "timeout")
|
123
|
+
fiber = Fiber.current
|
124
|
+
trigger = @triggers.add(duration) do
|
125
|
+
fiber.raise(exception, message) if fiber.alive?
|
126
|
+
end
|
127
|
+
|
128
|
+
begin
|
129
|
+
yield duration
|
130
|
+
ensure
|
131
|
+
trigger.disable
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def fiber(&block)
|
136
|
+
unless Fiber.blocking?
|
137
|
+
# nested Fiber.schedule
|
138
|
+
@nested << Fiber.current
|
139
|
+
end
|
140
|
+
|
141
|
+
fiber = Fiber.new(blocking: false) do
|
142
|
+
@count += 1
|
143
|
+
block.call
|
144
|
+
ensure
|
145
|
+
@count -= 1
|
146
|
+
end
|
147
|
+
|
148
|
+
fiber.tap(&:transfer)
|
149
|
+
end
|
150
|
+
end
|
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fiber_scheduler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bruno Sutic
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-02-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: io-event
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: async
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.11'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.11'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop-rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.8'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: standard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.7'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.7'
|
83
|
+
description:
|
84
|
+
email: code@brunosutic.com
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- lib/fiber/scheduler.rb
|
90
|
+
- lib/fiber_scheduler.rb
|
91
|
+
- lib/fiber_scheduler/trigger.rb
|
92
|
+
- lib/fiber_scheduler/triggers.rb
|
93
|
+
- lib/fiber_scheduler/version.rb
|
94
|
+
homepage: https://github.com/bruno-/fiber_scheduler
|
95
|
+
licenses:
|
96
|
+
- MIT
|
97
|
+
metadata: {}
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: 3.1.0
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubygems_version: 3.3.3
|
114
|
+
signing_key:
|
115
|
+
specification_version: 4
|
116
|
+
summary: Fiber scheduler
|
117
|
+
test_files: []
|