ztimer 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -1
- data/Gemfile +4 -0
- data/README.md +20 -5
- data/lib/ztimer.rb +97 -89
- data/lib/ztimer/slot.rb +2 -2
- data/lib/ztimer/sorted_store.rb +2 -2
- data/lib/ztimer/version.rb +2 -2
- data/lib/ztimer/watcher.rb +2 -2
- metadata +2 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba1fe46353cca7319d61150f6fe52622d3668db2
|
4
|
+
data.tar.gz: ac6dc6b45e0001d39e63a6ead7950b5db75ac7d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1eac7a0992c9d806a76c35cc31805bb716e7ca8b39e4b15b0609ac8c9415464c816b6b57a74821745b213ad57a9e21843f641b16663c19cf3e46d267b6216ce9
|
7
|
+
data.tar.gz: f81c9081bad5354bb6e0f056596b9c8db7ce5878d6ff6d1da5e82536c218e143c59ef60389bda4b27bed205d09881c35e6c7dccf28f8f15d7395cf487142fe00
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# Ztimer
|
2
2
|
|
3
|
-
**Ztimer** is a Ruby
|
4
|
-
|
3
|
+
**Ztimer** is a simple Ruby implementation of an asynchronous timer, that allows to enqueue the execution of Ruby
|
4
|
+
code, so that it will be asynchronously executed on timeout. It's very useful when you need a simple way to execute
|
5
|
+
some code asynchronously or with a certain delay.
|
6
|
+
|
5
7
|
|
6
8
|
## Installation
|
7
9
|
|
@@ -46,14 +48,27 @@ end
|
|
46
48
|
sleep 10 # wait for 10 seconds (10 executions)
|
47
49
|
job.cancel! # cancel the recurrent job
|
48
50
|
|
51
|
+
# Custom Ztimer instance
|
52
|
+
my_timer = Ztimer.new(concurrency: 5) # create a new Ztimer instance
|
53
|
+
10.times do
|
54
|
+
# Use the custom ztimer to execute jobs asynchronously
|
55
|
+
my_timer.async do
|
56
|
+
puts "Doing async job..."
|
57
|
+
end
|
58
|
+
end
|
49
59
|
```
|
50
60
|
|
51
61
|
By default **Ztimer** will run at maximum 20 jobs concurrently, so that if you have 100 jobs to be
|
52
|
-
executed at the same time, at
|
62
|
+
executed at the same time, at most 20 of them will run concurrently. This is necessary in order to prevent uncontrolled threads spawn when many jobs have to be run at the same time.
|
53
63
|
|
54
|
-
Anyway, you can change the concurrency by calling `Ztimer.concurrency = <concurrency>`, where `<concurrency>` is the maximum number of `Ztimer` workers allowed to run in parallel (ex: `Ztimer.concurrency = 50`).
|
64
|
+
Anyway, you can change the concurrency level by calling `Ztimer.concurrency = <concurrency>`, where `<concurrency>` is the maximum number of `Ztimer` workers allowed to run in parallel (ex: `Ztimer.concurrency = 50`).
|
65
|
+
|
66
|
+
If you're using custom **Ztimer** instance, you can specify the concurrency while creating the new instance:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
my_timer = Ztimer.new(concurrency: 42) # create a ztimer with concurrency set to 42
|
70
|
+
```
|
55
71
|
|
56
72
|
## Contributing
|
57
73
|
|
58
74
|
Bug reports and pull requests are welcome on GitHub at https://github.com/serioja90/ztimer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
59
|
-
|
data/lib/ztimer.rb
CHANGED
@@ -3,116 +3,124 @@ require "ztimer/slot"
|
|
3
3
|
require "ztimer/sorted_store"
|
4
4
|
require "ztimer/watcher"
|
5
5
|
|
6
|
-
|
7
|
-
@
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
slot = Slot.new(enqueued_at, enqueued_at, -1, &callback)
|
21
|
-
|
22
|
-
incr_counter!
|
23
|
-
execute(slot)
|
24
|
-
|
25
|
-
return slot
|
26
|
-
end
|
6
|
+
class Ztimer
|
7
|
+
@default_instance = nil
|
8
|
+
|
9
|
+
attr_reader :concurrency, :running, :count, :watcher, :queue
|
10
|
+
|
11
|
+
def initialize(concurrency: 20)
|
12
|
+
@concurrency = concurrency
|
13
|
+
@watcher = Ztimer::Watcher.new(){|slot| execute(slot) }
|
14
|
+
@workers_lock = Mutex.new
|
15
|
+
@count_lock = Mutex.new
|
16
|
+
@queue = Queue.new
|
17
|
+
@running = 0
|
18
|
+
@count = 0
|
19
|
+
end
|
27
20
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
slot = Slot.new(enqueued_at, expires_at, -1, &callback)
|
21
|
+
def async(&callback)
|
22
|
+
enqueued_at = utc_microseconds
|
23
|
+
slot = Slot.new(enqueued_at, enqueued_at, -1, &callback)
|
32
24
|
|
33
|
-
|
25
|
+
incr_counter!
|
26
|
+
execute(slot)
|
34
27
|
|
35
|
-
|
36
|
-
|
28
|
+
return slot
|
29
|
+
end
|
37
30
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
31
|
+
def after(milliseconds, &callback)
|
32
|
+
enqueued_at = utc_microseconds
|
33
|
+
expires_at = enqueued_at + milliseconds * 1000
|
34
|
+
slot = Slot.new(enqueued_at, expires_at, -1, &callback)
|
42
35
|
|
43
|
-
|
36
|
+
add(slot)
|
44
37
|
|
45
|
-
|
46
|
-
|
38
|
+
return slot
|
39
|
+
end
|
47
40
|
|
48
|
-
|
49
|
-
|
50
|
-
|
41
|
+
def every(milliseconds, &callback)
|
42
|
+
enqueued_at = utc_microseconds
|
43
|
+
expires_at = enqueued_at + milliseconds * 1000
|
44
|
+
slot = Slot.new(enqueued_at, expires_at, milliseconds * 1000, &callback)
|
51
45
|
|
52
|
-
|
53
|
-
raise ArgumentError.new("Invalid concurrency value: #{new_value}") unless new_value.is_a?(Fixnum) && new_value >= 1
|
54
|
-
@concurrency = new_value
|
55
|
-
end
|
46
|
+
add(slot)
|
56
47
|
|
48
|
+
return slot
|
49
|
+
end
|
57
50
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
scheduled: @watcher.jobs,
|
62
|
-
executing: @queue.size,
|
63
|
-
total: @count
|
64
|
-
}
|
65
|
-
end
|
51
|
+
def jobs_count
|
52
|
+
return @watcher.jobs
|
53
|
+
end
|
66
54
|
|
67
|
-
|
55
|
+
def concurrency=(new_value)
|
56
|
+
raise ArgumentError.new("Invalid concurrency value: #{new_value}") unless new_value.is_a?(Fixnum) && new_value >= 1
|
57
|
+
@concurrency = new_value
|
58
|
+
end
|
68
59
|
|
69
|
-
def add(slot)
|
70
|
-
incr_counter!
|
71
|
-
@watcher << slot
|
72
|
-
end
|
73
60
|
|
74
|
-
|
75
|
-
|
76
|
-
|
61
|
+
def stats
|
62
|
+
{
|
63
|
+
running: @running,
|
64
|
+
scheduled: @watcher.jobs,
|
65
|
+
executing: @queue.size,
|
66
|
+
total: @count
|
67
|
+
}
|
68
|
+
end
|
77
69
|
|
78
|
-
def execute(slot)
|
79
|
-
@queue << slot
|
80
70
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
71
|
+
def self.method_missing(name, *args, &block)
|
72
|
+
@default_instance ||= Ztimer.new(concurrency: 20)
|
73
|
+
@default_instance.send(name, *args, &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
def add(slot)
|
79
|
+
incr_counter!
|
80
|
+
@watcher << slot
|
81
|
+
end
|
82
|
+
|
83
|
+
def incr_counter!
|
84
|
+
@count_lock.synchronize{ @count += 1 }
|
85
|
+
end
|
86
|
+
|
87
|
+
def execute(slot)
|
88
|
+
@queue << slot
|
89
|
+
|
90
|
+
@workers_lock.synchronize do
|
91
|
+
[@concurrency - @running, @queue.size].min.times do
|
92
|
+
@running += 1
|
93
|
+
start_new_thread!
|
86
94
|
end
|
87
95
|
end
|
96
|
+
end
|
88
97
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end
|
98
|
+
def start_new_thread!
|
99
|
+
worker = Thread.new do
|
100
|
+
begin
|
101
|
+
loop do
|
102
|
+
current_slot = nil
|
103
|
+
@workers_lock.synchronize do
|
104
|
+
current_slot = @queue.pop(true) unless @queue.empty?
|
105
|
+
end
|
106
|
+
break if current_slot.nil?
|
107
|
+
|
108
|
+
begin
|
109
|
+
current_slot.executed_at = utc_microseconds
|
110
|
+
current_slot.callback.call(current_slot) unless current_slot.callback.nil? || current_slot.canceled?
|
111
|
+
rescue => e
|
112
|
+
STDERR.puts e.inspect + (e.backtrace ? "\n" + e.backtrace.join("\n") : "")
|
105
113
|
end
|
106
|
-
rescue ThreadError
|
107
|
-
puts "queue is empty"
|
108
114
|
end
|
109
|
-
|
115
|
+
rescue ThreadError
|
116
|
+
puts "queue is empty"
|
110
117
|
end
|
111
|
-
|
118
|
+
@workers_lock.synchronize { @running -= 1 }
|
112
119
|
end
|
120
|
+
worker.abort_on_exception = true
|
121
|
+
end
|
113
122
|
|
114
|
-
|
115
|
-
|
116
|
-
end
|
123
|
+
def utc_microseconds
|
124
|
+
return Time.now.to_f * 1_000_000
|
117
125
|
end
|
118
|
-
end
|
126
|
+
end
|
data/lib/ztimer/slot.rb
CHANGED
data/lib/ztimer/sorted_store.rb
CHANGED
data/lib/ztimer/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "0.
|
1
|
+
class Ztimer
|
2
|
+
VERSION = "0.6.0"
|
3
3
|
end
|
data/lib/ztimer/watcher.rb
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.6.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:
|
11
|
+
date: 2017-02-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -100,4 +100,3 @@ signing_key:
|
|
100
100
|
specification_version: 4
|
101
101
|
summary: An asyncrhonous timer
|
102
102
|
test_files: []
|
103
|
-
has_rdoc:
|