ztimer 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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: