vinted-thread 0.1.1

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