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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 03ec6525946e2b8674a3f8401082b6eef8536130
4
- data.tar.gz: 957b3894fc8259592b9ec4728727ae95bcdec284
3
+ metadata.gz: d1573340ee3ea2395903700d513da753aafc2195
4
+ data.tar.gz: a59c67818ca6a04791149dbf708d76db176be04b
5
5
  SHA512:
6
- metadata.gz: a2f31354371be81f9d5f507f7ca955fdca10b5156d2da1a6583925a562488f50679f3df148282fd5c2d9af56b39c9ddc9a400e5933140f5e47b6fda1de8d3b71
7
- data.tar.gz: 0811e83c8d916e4c7a47656a54263b6bda5407d7e01c07265cf099607ac52af4f03faf01727fa4ae3f0566fb840b2cff6210e6996e98a20731f58bfaeaa49a42
6
+ metadata.gz: 797a50a8523875f1f411706125f2585316cf916f82bc254e545e7dbc727e2a395893a2164a4db6eb6909796bd9b44fce77491feac8f2bd9aaa9ec774e608b357
7
+ data.tar.gz: f5ef83dea7d7760a681dac4f2160fd8ab5da183054f43d7dbc25bba552800e73d2fba771e4af8d970b616aa8a71baedd8e01f627f13499f92346e424065d4fb7
data/lib/ztimer/slot.rb CHANGED
@@ -10,6 +10,15 @@ module Ztimer
10
10
  @callback = callback
11
11
  @started_at = nil
12
12
  @executed_at = nil
13
+ @canceled = false
14
+ end
15
+
16
+ def canceled?
17
+ return @canceled
18
+ end
19
+
20
+ def cancel!
21
+ @canceled = true
13
22
  end
14
23
 
15
24
  def <=>(other)
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Ztimer
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -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 = 20
8
- @slots = SortedSet.new
9
- @metric = Hitimes::Metric.new("Notifier")
10
- @monitor = nil
11
- @running = 0
12
- @lock = Mutex.new
13
- @mutex = Mutex.new
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 @slots.count
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
- @mutex.synchronize do
41
- @slots << slot
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.push slot
51
+ @queue << slot
71
52
 
72
- @lock.synchronize do
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? && (slot = @queue.pop(true))
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
- @lock.synchronize { @running -= 1 }
68
+ @workers_lock.synchronize { @running -= 1 }
85
69
  end
86
70
  worker.abort_on_exception = true
87
71
  end
data/ztimer.gemspec CHANGED
@@ -23,4 +23,5 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency "rake", "~> 10.0"
24
24
  spec.add_development_dependency "rspec", "~> 3.0"
25
25
  spec.add_dependency "hitimes", "~> 1.2"
26
+ spec.add_dependency "lounger", "~> 0.2"
26
27
  end
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.2.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-04 00:00:00.000000000 Z
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.6
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: