seekingalpha_thread 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,164 @@
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 future is an object that incapsulates a block which is called in a
14
+ # different thread, upon retrieval the caller gets blocked until the block has
15
+ # finished running, and its result is returned and cached.
16
+ class Thread::Future
17
+ Cancel = Class.new(Exception)
18
+
19
+ # Create a future with the passed block and optionally using the passed pool.
20
+ def initialize(pool = nil, &block)
21
+ raise ArgumentError, 'no block given' unless block
22
+
23
+ @mutex = Mutex.new
24
+
25
+ task = proc {
26
+ begin
27
+ deliver block.call
28
+ rescue Exception => e
29
+ @exception = e
30
+
31
+ deliver nil
32
+ end
33
+ }
34
+
35
+ @thread = pool ? pool.process(&task) : Thread.new(&task)
36
+
37
+ ObjectSpace.define_finalizer self, self.class.finalizer(@thread)
38
+ end
39
+
40
+ # @private
41
+ def self.finalizer(thread)
42
+ proc {
43
+ thread.raise Cancel.new
44
+ }
45
+ end
46
+
47
+ # Check if an exception has been raised.
48
+ def exception?
49
+ @mutex.synchronize {
50
+ instance_variable_defined? :@exception
51
+ }
52
+ end
53
+
54
+ # Return the raised exception.
55
+ def exception
56
+ @mutex.synchronize {
57
+ @exception
58
+ }
59
+ end
60
+
61
+ # Check if the future has been called.
62
+ def delivered?
63
+ @mutex.synchronize {
64
+ instance_variable_defined? :@value
65
+ }
66
+ end
67
+
68
+ alias realized? delivered?
69
+
70
+ # Cancel the future, {#value} will yield a Cancel exception
71
+ def cancel
72
+ return self if delivered?
73
+
74
+ @mutex.synchronize {
75
+ @thread.raise Cancel.new('future cancelled')
76
+ }
77
+
78
+ self
79
+ end
80
+
81
+ # Check if the future has been cancelled
82
+ def cancelled?
83
+ @mutex.synchronize {
84
+ @exception.is_a? Cancel
85
+ }
86
+ end
87
+
88
+ # Get the value of the future, if it's not finished running this call will block.
89
+ #
90
+ # In case the block raises an exception, it will be raised, the exception is cached
91
+ # and will be raised every time you access the value.
92
+ #
93
+ # An optional timeout can be passed which will return nil if nothing has been
94
+ # delivered.
95
+ def value(timeout = nil)
96
+ raise @exception if exception?
97
+
98
+ return @value if delivered?
99
+
100
+ @mutex.synchronize {
101
+ cond.wait(@mutex, *timeout)
102
+ }
103
+
104
+ if exception?
105
+ raise @exception
106
+ elsif delivered?
107
+ return @value
108
+ end
109
+ end
110
+
111
+ alias ~ value
112
+
113
+ # Do the same as {#value}, but return nil in case of exception.
114
+ def value!(timeout = nil)
115
+ begin
116
+ value(timeout)
117
+ rescue Exception
118
+ nil
119
+ end
120
+ end
121
+
122
+ alias ! value!
123
+
124
+ private
125
+ def cond?
126
+ instance_variable_defined? :@cond
127
+ end
128
+
129
+ def cond
130
+ @cond ||= ConditionVariable.new
131
+ end
132
+
133
+ def deliver (value)
134
+ return if delivered?
135
+
136
+ @mutex.synchronize {
137
+ @value = value
138
+
139
+ cond.broadcast if cond?
140
+ }
141
+
142
+ self
143
+ end
144
+ end
145
+
146
+ class Thread
147
+ # Helper to create a future
148
+ def self.future(pool = nil, &block)
149
+ Thread::Future.new(pool, &block)
150
+ end
151
+ end
152
+
153
+ module Kernel
154
+ # Helper to create a future.
155
+ def future(pool = nil, &block)
156
+ Thread::Future.new(pool, &block)
157
+ end
158
+ end
159
+
160
+ class Thread::Pool
161
+ def future(&block)
162
+ Thread.future self, &block
163
+ end
164
+ end
@@ -0,0 +1,119 @@
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 pipe lets you execute various tasks on a set of data in parallel,
14
+ # each datum inserted in the pipe is passed along through queues to the various
15
+ # functions composing the pipe, the final result is inserted in the final queue.
16
+ class Thread::Pipe
17
+ # A task encapsulates a part of the pipe.
18
+ class Task
19
+ attr_accessor :input, :output
20
+
21
+ # Create a Task which will call the passed function and get input
22
+ # from the optional parameter and put output in the optional parameter.
23
+ def initialize(func, input = Queue.new, output = Queue.new)
24
+ @input = input
25
+ @output = output
26
+ @handling = false
27
+
28
+ @thread = Thread.new {
29
+ while true
30
+ value = @input.deq
31
+
32
+ @handling = true
33
+ begin
34
+ value = func.call(value)
35
+ @output.enq value
36
+ rescue Exception; end
37
+ @handling = false
38
+ end
39
+ }
40
+ end
41
+
42
+ # Check if the task has nothing to do.
43
+ def empty?
44
+ !@handling && @input.empty? && @output.empty?
45
+ end
46
+
47
+ # Stop the task.
48
+ def kill
49
+ @thread.raise
50
+ end
51
+ end
52
+
53
+ # Create a pipe using the optionally passed objects as input and
54
+ # output queue.
55
+ #
56
+ # The objects must respond to #enq and #deq, and block on #deq.
57
+ def initialize(input = Queue.new, output = Queue.new)
58
+ @tasks = []
59
+
60
+ @input = input
61
+ @output = output
62
+
63
+ ObjectSpace.define_finalizer self, self.class.finalizer(@tasks)
64
+ end
65
+
66
+ # @private
67
+ def self.finalizer(tasks)
68
+ proc {
69
+ tasks.each(&:kill)
70
+ }
71
+ end
72
+
73
+ # Add a task to the pipe, it must respond to #call and #arity,
74
+ # and #arity must return 1.
75
+ def |(func)
76
+ if func.arity != 1
77
+ raise ArgumentError, 'wrong arity'
78
+ end
79
+
80
+ Task.new(func, (@tasks.empty? ? @input : Queue.new), @output).tap {|t|
81
+ @tasks.last.output = t.input unless @tasks.empty?
82
+ @tasks << t
83
+ }
84
+
85
+ self
86
+ end
87
+
88
+ # Check if the pipe is empty.
89
+ def empty?
90
+ @input.empty? && @output.empty? && @tasks.all?(&:empty?)
91
+ end
92
+
93
+ # Insert data in the pipe.
94
+ def enq(data)
95
+ return if @tasks.empty?
96
+
97
+ @input.enq data
98
+
99
+ self
100
+ end
101
+
102
+ alias push enq
103
+ alias << enq
104
+
105
+ # Get an element from the output queue.
106
+ def deq(non_block = false)
107
+ @output.deq(non_block)
108
+ end
109
+
110
+ alias pop deq
111
+ alias ~ deq
112
+ end
113
+
114
+ class Thread
115
+ # Helper to create a pipe.
116
+ def self.|(func)
117
+ Pipe.new | func
118
+ end
119
+ end