ztimer 0.2.0 → 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/lib/ztimer/slot.rb +9 -0
- data/lib/ztimer/sorted_store.rb +97 -0
- data/lib/ztimer/version.rb +1 -1
- data/lib/ztimer/watcher.rb +80 -0
- data/lib/ztimer.rb +24 -40
- data/ztimer.gemspec +1 -0
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1573340ee3ea2395903700d513da753aafc2195
|
4
|
+
data.tar.gz: a59c67818ca6a04791149dbf708d76db176be04b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 797a50a8523875f1f411706125f2585316cf916f82bc254e545e7dbc727e2a395893a2164a4db6eb6909796bd9b44fce77491feac8f2bd9aaa9ec774e608b357
|
7
|
+
data.tar.gz: f5ef83dea7d7760a681dac4f2160fd8ab5da183054f43d7dbc25bba552800e73d2fba771e4af8d970b616aa8a71baedd8e01f627f13499f92346e424065d4fb7
|
data/lib/ztimer/slot.rb
CHANGED
@@ -0,0 +1,97 @@
|
|
1
|
+
|
2
|
+
module Ztimer
|
3
|
+
class SortedStore
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@store = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def <<(value)
|
10
|
+
@store.insert(position_for(value), value)
|
11
|
+
return self
|
12
|
+
end
|
13
|
+
|
14
|
+
def delete(value)
|
15
|
+
index = index_of(value)
|
16
|
+
if index
|
17
|
+
@store.delete_at(index)
|
18
|
+
else
|
19
|
+
return nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def [](index)
|
24
|
+
return @store[index]
|
25
|
+
end
|
26
|
+
|
27
|
+
def first
|
28
|
+
return @store.first
|
29
|
+
end
|
30
|
+
|
31
|
+
def last
|
32
|
+
return @store.last
|
33
|
+
end
|
34
|
+
|
35
|
+
def shift
|
36
|
+
return @store.shift
|
37
|
+
end
|
38
|
+
|
39
|
+
def pop
|
40
|
+
return @store.pop
|
41
|
+
end
|
42
|
+
|
43
|
+
def index_of(value, start = 0, stop = [@store.count - 1, 0].max)
|
44
|
+
if start > stop
|
45
|
+
return nil
|
46
|
+
elsif start == stop
|
47
|
+
return value == @store[start] ? start : nil
|
48
|
+
else
|
49
|
+
position = ((stop + start)/ 2).to_i
|
50
|
+
case value <=> @store[position]
|
51
|
+
when -1 then return index_of(value, start, position)
|
52
|
+
when 0 then return position
|
53
|
+
when 1 then return index_of(value, position + 1, stop)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def count
|
59
|
+
return @store.count
|
60
|
+
end
|
61
|
+
|
62
|
+
def size
|
63
|
+
return @store.size
|
64
|
+
end
|
65
|
+
|
66
|
+
def empty?
|
67
|
+
return @store.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
def clear
|
71
|
+
return @store.clear
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_a
|
75
|
+
return @store.dup
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
def position_for(item, start = 0, stop = [@store.count - 1, 0].max)
|
82
|
+
if start > stop
|
83
|
+
raise "Invalid range (#{start}, #{stop})"
|
84
|
+
elsif start == stop
|
85
|
+
element = @store[start]
|
86
|
+
return element.nil? || ((item <=> element) <= 0) ? start : start + 1
|
87
|
+
else
|
88
|
+
position = ((stop + start)/ 2).to_i
|
89
|
+
case item <=> @store[position]
|
90
|
+
when -1 then return position_for(item, start, position)
|
91
|
+
when 0 then return position
|
92
|
+
when 1 then return position_for(item, position + 1, stop)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/ztimer/version.rb
CHANGED
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
module Ztimer
|
3
|
+
class Watcher
|
4
|
+
|
5
|
+
def initialize(&callback)
|
6
|
+
@thread = nil
|
7
|
+
@idler = Lounger.new
|
8
|
+
@slots = Ztimer::SortedStore.new
|
9
|
+
@callback = callback
|
10
|
+
@lock = Mutex.new
|
11
|
+
@metric = Hitimes::Metric.new("Notifier")
|
12
|
+
@mutex = Mutex.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def << (slot)
|
16
|
+
@mutex.synchronize do
|
17
|
+
@slots << slot
|
18
|
+
if @slots.first == slot
|
19
|
+
run
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def jobs
|
25
|
+
return @slots.size
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def run
|
31
|
+
if @thread
|
32
|
+
@idler.signal && @thread.run
|
33
|
+
else
|
34
|
+
start
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def start
|
39
|
+
@lock.synchronize do
|
40
|
+
return if @thread
|
41
|
+
@thread = Thread.new do
|
42
|
+
loop do
|
43
|
+
delay = get_delay
|
44
|
+
if delay.nil?
|
45
|
+
@idler.wait
|
46
|
+
next
|
47
|
+
end
|
48
|
+
|
49
|
+
select(nil, nil, nil, delay / 1_000_000.to_f) if delay > 1 # 1 microsecond of cranularity
|
50
|
+
|
51
|
+
while get_first_expired do
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
@thread.abort_on_exception = true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_delay
|
60
|
+
return @mutex.synchronize { @slots.empty? ? nil : @slots.first.expires_at - @metric.utc_microseconds }
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_first_expired
|
64
|
+
@mutex.synchronize do
|
65
|
+
slot = @slots.first
|
66
|
+
if slot && (slot.expires_at < @metric.utc_microseconds)
|
67
|
+
@slots.shift
|
68
|
+
slot.started_at = @metric.utc_microseconds
|
69
|
+
execute(slot) unless slot.canceled?
|
70
|
+
end
|
71
|
+
|
72
|
+
slot
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def execute(slot)
|
77
|
+
@callback.call(slot)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/ztimer.rb
CHANGED
@@ -1,32 +1,37 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'hitimes'
|
3
|
+
require 'pqueue'
|
4
|
+
require 'lounger'
|
5
|
+
|
3
6
|
require "ztimer/version"
|
4
7
|
require "ztimer/slot"
|
8
|
+
require "ztimer/sorted_store"
|
9
|
+
require "ztimer/watcher"
|
5
10
|
|
6
11
|
module Ztimer
|
7
|
-
@concurrency
|
8
|
-
@
|
9
|
-
@metric
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@
|
14
|
-
@queue = Queue.new
|
12
|
+
@concurrency = 20
|
13
|
+
@watcher = Ztimer::Watcher.new(){|slot| execute(slot) }
|
14
|
+
@metric = Hitimes::Metric.new("Notifier")
|
15
|
+
@workers_lock = Mutex.new
|
16
|
+
@queue = Queue.new
|
17
|
+
@running = 0
|
18
|
+
@count = 0
|
15
19
|
|
16
20
|
class << self
|
17
|
-
attr_reader :concurrency, :running
|
21
|
+
attr_reader :concurrency, :running, :count
|
18
22
|
|
19
23
|
def after(milliseconds, &callback)
|
20
24
|
enqueued_at = @metric.utc_microseconds
|
21
25
|
expires_at = enqueued_at + milliseconds * 1000
|
22
26
|
slot = Slot.new(enqueued_at, expires_at, &callback)
|
27
|
+
|
23
28
|
add(slot)
|
24
29
|
|
25
30
|
return slot
|
26
31
|
end
|
27
32
|
|
28
33
|
def jobs_count
|
29
|
-
return @
|
34
|
+
return @watcher.jobs
|
30
35
|
end
|
31
36
|
|
32
37
|
def concurrency=(new_value)
|
@@ -37,51 +42,30 @@ module Ztimer
|
|
37
42
|
protected
|
38
43
|
|
39
44
|
def add(slot)
|
40
|
-
@
|
41
|
-
|
42
|
-
restart_monitor if @slots.first == slot || @monitor.nil? || !@monitor.alive?
|
43
|
-
end
|
45
|
+
@count += 1
|
46
|
+
@watcher << slot
|
44
47
|
end
|
45
48
|
|
46
|
-
def restart_monitor
|
47
|
-
@monitor.kill if @monitor
|
48
|
-
|
49
|
-
@monitor = Thread.new do
|
50
|
-
loop do
|
51
|
-
break if @slots.empty?
|
52
|
-
|
53
|
-
delay = @slots.first.expires_at - @metric.utc_microseconds
|
54
|
-
select(nil, nil, nil, delay / 1_000_000.to_f) if delay > 1 # 1 microsecond of cranularity
|
55
|
-
|
56
|
-
while @slots.first && (@slots.first.expires_at < @metric.utc_microseconds) do
|
57
|
-
@mutex.synchronize do
|
58
|
-
slot = @slots.first
|
59
|
-
slot.started_at = @metric.utc_microseconds
|
60
|
-
execute(slot)
|
61
|
-
@slots.delete slot
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
@monitor.abort_on_exception = true
|
67
|
-
end
|
68
49
|
|
69
50
|
def execute(slot)
|
70
|
-
@queue
|
51
|
+
@queue << slot
|
71
52
|
|
72
|
-
@
|
53
|
+
@workers_lock.synchronize do
|
73
54
|
[@concurrency - @running, @queue.size].min.times do
|
74
55
|
@running += 1
|
75
56
|
worker = Thread.new do
|
76
57
|
begin
|
77
|
-
while !@queue.empty? &&
|
58
|
+
while !@queue.empty? && @queue.pop(true) do
|
78
59
|
slot.executed_at = @metric.utc_microseconds
|
79
60
|
slot.callback.call(slot) unless slot.callback.nil?
|
80
61
|
end
|
62
|
+
rescue ThreadError
|
63
|
+
# queue is empty
|
64
|
+
puts "queue is empty"
|
81
65
|
rescue => e
|
82
66
|
STDERR.puts e.inspect + (e.backtrace ? "\n" + e.backtrace.join("\n") : "")
|
83
67
|
end
|
84
|
-
@
|
68
|
+
@workers_lock.synchronize { @running -= 1 }
|
85
69
|
end
|
86
70
|
worker.abort_on_exception = true
|
87
71
|
end
|
data/ztimer.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ztimer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Groza Sergiu
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: lounger
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.2'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.2'
|
69
83
|
description: Ruby asyncrhonous timer that allows you to enqueue tasks to be executed
|
70
84
|
asyncrhonously after a delay
|
71
85
|
email:
|
@@ -85,7 +99,9 @@ files:
|
|
85
99
|
- bin/setup
|
86
100
|
- lib/ztimer.rb
|
87
101
|
- lib/ztimer/slot.rb
|
102
|
+
- lib/ztimer/sorted_store.rb
|
88
103
|
- lib/ztimer/version.rb
|
104
|
+
- lib/ztimer/watcher.rb
|
89
105
|
- ztimer.gemspec
|
90
106
|
homepage: https://github.com/serioja90/ztimer
|
91
107
|
licenses:
|
@@ -107,9 +123,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
123
|
version: '0'
|
108
124
|
requirements: []
|
109
125
|
rubyforge_project:
|
110
|
-
rubygems_version: 2.4.
|
126
|
+
rubygems_version: 2.4.8
|
111
127
|
signing_key:
|
112
128
|
specification_version: 4
|
113
129
|
summary: An asyncrhonous timer
|
114
130
|
test_files: []
|
115
|
-
has_rdoc:
|