vinted-thread 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5135a4b3c6ef3c2396b06b34c7040d39b52fbe3f
4
+ data.tar.gz: 445e9bec68c67f6597c037844b90b5c41cc29f8c
5
+ SHA512:
6
+ metadata.gz: 3822c43a01c8f8d29652b46917be0827a4c83f061e7b159222d202c16fe86d293468991e7342cd092f8ba0065fddbc7d62796d9c31ac6b0a49c83adfc3a58942
7
+ data.tar.gz: f257a9d93c9e63b8e8708b5cfffb37be3a7abd929342d9f16cfbbde5b6ed2b493cfe63212f8f8dd37e14c8710a6e7a3321b024717090007c7bdad9dd700b99dd
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.2"
4
+ - "1.9.3"
5
+ - "2.0.0"
6
+ - jruby-19mode # JRuby in 1.9 mode
7
+ - rbx-19mode
data/README.md ADDED
@@ -0,0 +1,195 @@
1
+ thread - various extensions to the thread library
2
+ =================================================
3
+
4
+ Pool
5
+ ====
6
+ All the implementations I looked at were either buggy or wasted CPU resources
7
+ for no apparent reason, for example used a sleep of 0.01 seconds to then check for
8
+ readiness and stuff like this.
9
+
10
+ This implementation uses standard locking functions to work properly across multiple Ruby
11
+ implementations.
12
+
13
+ Example
14
+ -------
15
+
16
+ ```ruby
17
+ require 'thread/pool'
18
+
19
+ pool = Thread.pool(4)
20
+
21
+ 10.times {
22
+ pool.process {
23
+ sleep 2
24
+
25
+ puts 'lol'
26
+ }
27
+ }
28
+
29
+ pool.shutdown
30
+ ```
31
+
32
+ You should get 4 lols every 2 seconds and it should exit after 10 of them.
33
+
34
+ Channel
35
+ =======
36
+ This implements a channel where you can write messages and receive messages.
37
+
38
+ Example
39
+ -------
40
+
41
+ ```ruby
42
+ require 'thread/channel'
43
+
44
+ channel = Thread.channel
45
+ channel.send 'wat'
46
+ channel.receive # => 'wat'
47
+
48
+ channel = Thread.channel { |o| o.is_a?(Integer) }
49
+ channel.send 'wat' # => ArgumentError: guard mismatch
50
+
51
+ Thread.new {
52
+ while num = channel.receive(&:even?)
53
+ puts 'Aye!'
54
+ end
55
+ }
56
+
57
+ Thread.new {
58
+ while num = channel.receive(&:odd?)
59
+ puts 'Arrr!'
60
+ end
61
+ }
62
+
63
+ loop {
64
+ channel.send rand(1_000_000_000)
65
+
66
+ sleep 0.5
67
+ }
68
+ ```
69
+
70
+ Pipe
71
+ ====
72
+ A pipe allows you to execute various tasks on a set of data in parallel,
73
+ each datum inserted in the pipe is passed along through queues to the various
74
+ functions composing the pipe, the final result is inserted in the final queue.
75
+
76
+ Example
77
+ -------
78
+
79
+ ```ruby
80
+ require 'thread/pipe'
81
+
82
+ p = Thread |-> d { d * 2 } |-> d { d * 4 }
83
+ p << 2
84
+
85
+ puts ~p # => 16
86
+ ```
87
+
88
+ Process
89
+ =======
90
+ A process helps reducing programming errors coming from race conditions and the
91
+ like, the only way to interact with a process is through messages.
92
+
93
+ Multiple processes should talk with eachother through messages.
94
+
95
+ Example
96
+ -------
97
+
98
+ ```ruby
99
+ require 'thread/process'
100
+
101
+ p = Thread.process {
102
+ loop {
103
+ puts receive.inspect
104
+ }
105
+ }
106
+
107
+ p << 42
108
+ p << 23
109
+ ```
110
+
111
+ Promise
112
+ =======
113
+ This implements the promise pattern, allowing you to pass around an object
114
+ where you can send a value and extract a value, in a thread-safe way, accessing
115
+ the value will wait for the value to be delivered.
116
+
117
+ Example
118
+ -------
119
+
120
+ ```ruby
121
+ require 'thread/promise'
122
+
123
+ p = Thread.promise
124
+
125
+ Thread.new {
126
+ sleep 5
127
+ p << 42
128
+ }
129
+
130
+ puts ~p # => 42
131
+ ```
132
+
133
+ Future
134
+ ======
135
+ A future is somewhat a promise, except you pass it a block to execute in
136
+ another thread.
137
+
138
+ The value returned by the block will be the value of the promise.
139
+
140
+ Example
141
+ -------
142
+
143
+ ```ruby
144
+ require 'thread/future'
145
+
146
+ f = Thread.future {
147
+ sleep 5
148
+
149
+ 42
150
+ }
151
+
152
+ puts ~f # => 42
153
+ ```
154
+
155
+ Delay
156
+ =====
157
+ A delay is kind of a promise, except the block is called when the value is
158
+ being accessed and the result is cached.
159
+
160
+ Example
161
+ -------
162
+
163
+ ```ruby
164
+ require 'thread/delay'
165
+
166
+ d = Thread.delay {
167
+ 42
168
+ }
169
+
170
+ puts ~d # => 42
171
+ ```
172
+
173
+ Every
174
+ =====
175
+ An every executes the block every given seconds and yields the value to the
176
+ every object, you can then check if the current value is old or how much time
177
+ is left until the second call is done.
178
+
179
+ Example
180
+ -------
181
+
182
+ ```ruby
183
+ require 'net/http'
184
+ require 'thread/every'
185
+
186
+ e = Thread.every(5) {
187
+ Net::HTTP.get(URI.parse('http://www.whattimeisit.com/')).match %r{<B>(.*?)<BR>\s+(.*?)</B>}m do |m|
188
+ { date: m[1], time: m[2] }
189
+ end
190
+ }
191
+
192
+ loop do
193
+ puts ~e
194
+ end
195
+ ```
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ #! /usr/bin/env ruby
2
+ require 'rake'
3
+
4
+ task :default => [:install, :test]
5
+
6
+ task :install do
7
+ sh 'gem install --no-force rspec'
8
+ sh 'gem build *.gemspec'
9
+ sh 'gem install *.gem'
10
+ end
11
+
12
+ task :test do
13
+ FileUtils.cd 'tests' do
14
+ sh 'rspec channel_spec.rb --backtrace --color --format doc'
15
+ sh 'rspec promise_spec.rb --backtrace --color --format doc'
16
+ sh 'rspec future_spec.rb --backtrace --color --format doc'
17
+ sh 'rspec delay_spec.rb --backtrace --color --format doc'
18
+ sh 'rspec pipe_spec.rb --backtrace --color --format doc'
19
+ sh 'rspec every_spec.rb --backtrace --color --format doc'
20
+ end
21
+ end
@@ -0,0 +1,105 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ require 'thread'
12
+
13
+ # A channel lets you send and receive various messages in a thread-safe way.
14
+ #
15
+ # It also allows for guards upon sending and retrieval, to ensure the passed
16
+ # messages are safe to be consumed.
17
+ class Thread::Channel
18
+ # Create a channel with optional initial messages and optional channel guard.
19
+ def initialize (messages = [], &block)
20
+ @messages = []
21
+ @mutex = Mutex.new
22
+ @check = block
23
+
24
+ messages.each {|o|
25
+ send o
26
+ }
27
+ end
28
+
29
+ # Send a message to the channel.
30
+ #
31
+ # If there's a guard, the value is passed to it, if the guard returns a falsy value
32
+ # an ArgumentError exception is raised and the message is not sent.
33
+ def send (what)
34
+ if @check && !@check.call(what)
35
+ raise ArgumentError, 'guard mismatch'
36
+ end
37
+
38
+ @mutex.synchronize {
39
+ @messages << what
40
+
41
+ cond.broadcast if cond?
42
+ }
43
+
44
+ self
45
+ end
46
+
47
+ # Receive a message, if there are none the call blocks until there's one.
48
+ #
49
+ # If a block is passed, it's used as guard to match to a message.
50
+ def receive (&block)
51
+ message = nil
52
+
53
+ if block
54
+ found = false
55
+
56
+ until found
57
+ @mutex.synchronize {
58
+ if index = @messages.find_index(&block)
59
+ message = @messages.delete_at(index)
60
+ found = true
61
+ else
62
+ cond.wait @mutex
63
+ end
64
+ }
65
+ end
66
+ else
67
+ @mutex.synchronize {
68
+ if @messages.empty?
69
+ cond.wait @mutex
70
+ end
71
+
72
+ message = @messages.shift
73
+ }
74
+ end
75
+
76
+ message
77
+ end
78
+
79
+ # Receive a message, if there are none the call returns nil.
80
+ #
81
+ # If a block is passed, it's used as guard to match to a message.
82
+ def receive! (&block)
83
+ if block
84
+ @messages.delete_at(@messages.find_index(&block))
85
+ else
86
+ @messages.shift
87
+ end
88
+ end
89
+
90
+ private
91
+ def cond?
92
+ instance_variable_defined? :@cond
93
+ end
94
+
95
+ def cond
96
+ @cond ||= ConditionVariable.new
97
+ end
98
+ end
99
+
100
+ class Thread
101
+ # Helper to create a channel.
102
+ def self.channel (*args, &block)
103
+ Thread::Channel.new(*args, &block)
104
+ end
105
+ end
@@ -0,0 +1,94 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ require 'thread'
12
+
13
+ # A delay is an object that incapsulates a block which is called upon
14
+ # value retrieval, and its result cached.
15
+ class Thread::Delay
16
+ # Create a delay with the passed block.
17
+ def initialize (&block)
18
+ raise ArgumentError, 'no block given' unless block
19
+
20
+ @mutex = Mutex.new
21
+ @block = block
22
+ end
23
+
24
+ # Check if an exception has been raised.
25
+ def exception?
26
+ @mutex.synchronize {
27
+ instance_variable_defined? :@exception
28
+ }
29
+ end
30
+
31
+ # Return the raised exception.
32
+ def exception
33
+ @mutex.synchronize {
34
+ @exception
35
+ }
36
+ end
37
+
38
+ # Check if the delay has been called.
39
+ def delivered?
40
+ @mutex.synchronize {
41
+ instance_variable_defined? :@value
42
+ }
43
+ end
44
+
45
+ alias realized? delivered?
46
+
47
+ # Get the value of the delay, if it's already been executed, return the
48
+ # cached result, otherwise execute the block and return the value.
49
+ #
50
+ # In case the block raises an exception, it will be raised, the exception is
51
+ # cached and will be raised every time you access the value.
52
+ def value
53
+ @mutex.synchronize {
54
+ raise @exception if instance_variable_defined? :@exception
55
+
56
+ return @value if instance_variable_defined? :@value
57
+
58
+ begin
59
+ @value = @block.call
60
+ rescue Exception => e
61
+ @exception = e
62
+
63
+ raise
64
+ end
65
+ }
66
+ end
67
+
68
+ alias ~ value
69
+
70
+ # Do the same as {#value}, but return nil in case of exception.
71
+ def value!
72
+ begin
73
+ value
74
+ rescue Exception
75
+ nil
76
+ end
77
+ end
78
+
79
+ alias ! value!
80
+ end
81
+
82
+ class Thread
83
+ # Helper to create Thread::Delay
84
+ def self.delay (&block)
85
+ Thread::Delay.new(&block)
86
+ end
87
+ end
88
+
89
+ module Kernel
90
+ # Helper to create a Thread::Delay
91
+ def delay (&block)
92
+ Thread::Delay.new(&block)
93
+ end
94
+ end
@@ -0,0 +1,197 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ require 'thread'
12
+
13
+ # An every runs the given block every given seconds, you can then get the
14
+ # value, check if the value is old and you can check how many seconds
15
+ # until the next run.
16
+ class Thread::Every
17
+ Cancel = Class.new(Exception)
18
+ Restart = Class.new(Exception)
19
+
20
+ # Create an every with the given seconds and block.
21
+ def initialize (every, &block)
22
+ raise ArgumentError, 'no block given' unless block
23
+
24
+ @every = every
25
+ @old = true
26
+ @mutex = Mutex.new
27
+ @thread = Thread.new {
28
+ loop do
29
+ begin
30
+ value = block.call
31
+
32
+ @mutex.synchronize {
33
+ @at = Time.now
34
+ @value = value
35
+ @old = false
36
+ @exception = nil
37
+ }
38
+ rescue Restart
39
+ next
40
+ rescue Exception => e
41
+ @mutex.synchronize {
42
+ @at = Time.now
43
+ @exception = e
44
+ }
45
+
46
+ break if e.is_a? Cancel
47
+ end
48
+
49
+ cond.broadcast if cond?
50
+
51
+ begin
52
+ sleep @every
53
+ rescue Restart
54
+ next
55
+ rescue Cancel => e
56
+ @mutex.synchronize {
57
+ @at = Time.now
58
+ @exception = e
59
+ }
60
+
61
+ break
62
+ end
63
+ end
64
+ }
65
+
66
+ ObjectSpace.define_finalizer self, self.class.finalizer(@thread)
67
+ end
68
+
69
+ # @private
70
+ def self.finalizer (thread)
71
+ proc {
72
+ thread.raise Cancel.new
73
+ }
74
+ end
75
+
76
+ # Change the number of seconds between each call.
77
+ def every (seconds)
78
+ @every = seconds
79
+
80
+ restart
81
+ end
82
+
83
+ # Cancel the every, {#value} will yield a Cancel exception.
84
+ def cancel
85
+ @mutex.synchronize {
86
+ @thread.raise Cancel.new('every cancelled')
87
+ }
88
+
89
+ self
90
+ end
91
+
92
+ # Check if the every has been cancelled.
93
+ def cancelled?
94
+ @mutex.synchronize {
95
+ @exception.is_a? Cancel
96
+ }
97
+ end
98
+
99
+ # Checks when the every was cancelled.
100
+ def cancelled_at
101
+ if cancelled?
102
+ @mutex.synchronize {
103
+ @at
104
+ }
105
+ end
106
+ end
107
+
108
+ # Restart the every.
109
+ def restart
110
+ @mutex.synchronize {
111
+ @thread.raise Restart.new
112
+ }
113
+
114
+ self
115
+ end
116
+
117
+ # Check if the every is running.
118
+ def running?
119
+ !cancelled?
120
+ end
121
+
122
+ # Check if the every is old, after the first #value call it becomes old,
123
+ # until another run of the block is gone)
124
+ def old?
125
+ @mutex.synchronize {
126
+ @old
127
+ }
128
+ end
129
+
130
+ # Gets the Time when the block was called.
131
+ def called_at
132
+ @mutex.synchronize {
133
+ @at
134
+ }
135
+ end
136
+
137
+ # Gets how many seconds are missing before another call.
138
+ def next_in
139
+ return if cancelled?
140
+
141
+ @mutex.synchronize {
142
+ @every - (Time.now - @at)
143
+ }
144
+ end
145
+
146
+ # Gets the current every value.
147
+ def value (timeout = nil)
148
+ @mutex.synchronize {
149
+ if @old
150
+ cond.wait(@mutex, *timeout)
151
+ end
152
+
153
+ @old = true
154
+
155
+ if @exception
156
+ raise @exception
157
+ else
158
+ @value
159
+ end
160
+ }
161
+ end
162
+
163
+ alias ~ value
164
+
165
+ # Gets the current every value, without blocking and waiting for the next
166
+ # call.
167
+ def value!
168
+ @mutex.synchronize {
169
+ @old = true
170
+
171
+ @value unless @exception
172
+ }
173
+ end
174
+
175
+ private
176
+ def cond?
177
+ instance_variable_defined? :@cond
178
+ end
179
+
180
+ def cond
181
+ @cond ||= ConditionVariable.new
182
+ end
183
+ end
184
+
185
+ class Thread
186
+ # Helper to create an every
187
+ def self.every (every, &block)
188
+ Thread::Every.new(every, &block)
189
+ end
190
+ end
191
+
192
+ module Kernel
193
+ # Helper to create an every
194
+ def every (every, &block)
195
+ Thread::Every.new(every, &block)
196
+ end
197
+ end