rubysl-thread 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c626288948b9685e901de3bcdef1eea63bb81fc3
4
+ data.tar.gz: 668d50eab397667930f7f05242fa5208825852b8
5
+ SHA512:
6
+ metadata.gz: 2b8db86da74b9e1dbda841831a1f9725b73039f2818e5aa980df4837566d2f7a24fbb78422fdf46a39bbc947bb7d995df14c3d93157beb30e518e5db4bb57cd2
7
+ data.tar.gz: f07c6bf9154ca2111b5cc739e54b841014b95b342e212ca907912e5f06ac7e4c1f13ce7506acfaff091becf740d6ed7c902610fb2867b8ebee39735bee9a1026
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ env:
3
+ - RUBYLIB=lib
4
+ script: bundle exec mspec
5
+ rvm:
6
+ - 1.8.7
7
+ - rbx-nightly-18mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubysl-thread.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2013, Brian Shirai
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ 3. Neither the name of the library nor the names of its contributors may be
13
+ used to endorse or promote products derived from this software without
14
+ specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
20
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,29 @@
1
+ # Rubysl::Thread
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rubysl-thread'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rubysl-thread
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,2 @@
1
+ require "rubysl/thread/version"
2
+ require "rubysl/thread/thread"
@@ -0,0 +1,433 @@
1
+ #
2
+ # thread.rb - thread support classes
3
+ # $Date: 2006-12-31 07:02:22 -0800 (Sun, 31 Dec 2006) $
4
+ # by Yukihiro Matsumoto <matz@netlab.co.jp>
5
+ #
6
+ # Copyright (C) 2001 Yukihiro Matsumoto
7
+ # Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
8
+ # Copyright (C) 2000 Information-technology Promotion Agency, Japan
9
+ #
10
+
11
+ unless defined? Thread
12
+ raise "Thread not available for this ruby interpreter"
13
+ end
14
+
15
+ unless defined? ThreadError
16
+ class ThreadError < StandardError
17
+ end
18
+ end
19
+
20
+ if $DEBUG
21
+ Thread.abort_on_exception = true
22
+ end
23
+
24
+ class Thread
25
+ #
26
+ # Wraps a block in Thread.critical, restoring the original value upon exit
27
+ # from the critical section.
28
+ #
29
+ def Thread.exclusive
30
+ _old = Thread.critical
31
+ begin
32
+ Thread.critical = true
33
+ return yield
34
+ ensure
35
+ Thread.critical = _old
36
+ end
37
+ end
38
+ end
39
+
40
+ class Mutex
41
+ def initialize
42
+ @owner = nil
43
+ end
44
+
45
+ # Check and only allow it to be marshal'd if there are no waiters.
46
+ def marshal_dump
47
+ raise "Unable to dump locked mutex" unless @waiters.empty?
48
+ 1
49
+ end
50
+
51
+ # Implemented because we must since we use marshal_load PLUS we need
52
+ # to create AND prime @lock. If we didn't do this, then Marshal
53
+ # wouldn't prime the lock anyway.
54
+ def marshal_load(bunk)
55
+ initialize
56
+ end
57
+
58
+ def locked?
59
+ Rubinius.locked?(self)
60
+ end
61
+
62
+ def try_lock
63
+ # Locking implies a memory barrier, so we don't need to use
64
+ # one explicitly.
65
+ if Rubinius.try_lock(self)
66
+ @owner = Thread.current
67
+ true
68
+ else
69
+ false
70
+ end
71
+ end
72
+
73
+ def lock
74
+ Rubinius.memory_barrier
75
+ if @owner == Thread.current
76
+ raise ThreadError, "Recursively locking not allowed"
77
+ end
78
+
79
+ Rubinius.lock self
80
+ @owner = Thread.current
81
+ Rubinius.memory_barrier
82
+ return self
83
+ end
84
+
85
+ def unlock
86
+ Rubinius.memory_barrier
87
+
88
+ if @owner != Thread.current
89
+ raise ThreadError, "#{Thread.current.inspect} not owner, #{@owner.inspect} is"
90
+ end
91
+
92
+ @owner = nil
93
+ Rubinius.unlock self
94
+ end
95
+
96
+ def synchronize
97
+ lock
98
+ begin
99
+ yield
100
+ ensure
101
+ unlock
102
+ end
103
+ end
104
+ end
105
+
106
+ #
107
+ # ConditionVariable objects augment class Mutex. Using condition variables,
108
+ # it is possible to suspend while in the middle of a critical section until a
109
+ # resource becomes available.
110
+ #
111
+ # Example:
112
+ #
113
+ # require 'thread'
114
+ #
115
+ # mutex = Mutex.new
116
+ # resource = ConditionVariable.new
117
+ #
118
+ # a = Thread.new {
119
+ # mutex.synchronize {
120
+ # # Thread 'a' now needs the resource
121
+ # resource.wait(mutex)
122
+ # # 'a' can now have the resource
123
+ # }
124
+ # }
125
+ #
126
+ # b = Thread.new {
127
+ # mutex.synchronize {
128
+ # # Thread 'b' has finished using the resource
129
+ # resource.signal
130
+ # }
131
+ # }
132
+ #
133
+ class ConditionVariable
134
+ #
135
+ # Creates a new ConditionVariable
136
+ #
137
+ def initialize
138
+ @waiters = []
139
+ end
140
+
141
+ #
142
+ # Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
143
+ #
144
+ def wait(mutex, timeout=nil)
145
+ Rubinius.lock(self)
146
+
147
+ begin
148
+ wchan = Rubinius::Channel.new
149
+
150
+ begin
151
+ mutex.unlock
152
+ @waiters.push wchan
153
+ Rubinius.unlock(self)
154
+ signaled = wchan.receive_timeout timeout
155
+ ensure
156
+ mutex.lock
157
+ Rubinius.lock(self)
158
+
159
+ unless signaled or @waiters.delete(wchan)
160
+ # we timed out, but got signaled afterwards (e.g. while waiting to
161
+ # acquire @lock), so pass that signal on to the next waiter
162
+ @waiters.shift << true unless @waiters.empty?
163
+ end
164
+ end
165
+
166
+ if timeout
167
+ !!signaled
168
+ else
169
+ self
170
+ end
171
+ ensure
172
+ Rubinius.unlock(self)
173
+ end
174
+ end
175
+
176
+ #
177
+ # Wakes up the first thread in line waiting for this lock.
178
+ #
179
+ def signal
180
+ Rubinius.lock(self)
181
+ begin
182
+ @waiters.shift << true unless @waiters.empty?
183
+ ensure
184
+ Rubinius.unlock(self)
185
+ end
186
+ self
187
+ end
188
+
189
+ #
190
+ # Wakes up all threads waiting for this lock.
191
+ #
192
+ def broadcast
193
+ Rubinius.lock(self)
194
+ begin
195
+ @waiters.shift << true until @waiters.empty?
196
+ ensure
197
+ Rubinius.unlock(self)
198
+ end
199
+ self
200
+ end
201
+ end
202
+
203
+ #
204
+ # This class provides a way to synchronize communication between threads.
205
+ #
206
+ # Example:
207
+ #
208
+ # require 'thread'
209
+ #
210
+ # queue = Queue.new
211
+ #
212
+ # producer = Thread.new do
213
+ # 5.times do |i|
214
+ # sleep rand(i) # simulate expense
215
+ # queue << i
216
+ # puts "#{i} produced"
217
+ # end
218
+ # end
219
+ #
220
+ # consumer = Thread.new do
221
+ # 5.times do |i|
222
+ # value = queue.pop
223
+ # sleep rand(i/2) # simulate expense
224
+ # puts "consumed #{value}"
225
+ # end
226
+ # end
227
+ #
228
+ # consumer.join
229
+ #
230
+ class Queue
231
+ #
232
+ # Creates a new queue.
233
+ #
234
+ def initialize
235
+ @que = []
236
+ @que.taint # enable tainted comunication
237
+ self.taint
238
+ @waiting = []
239
+ @waiting.taint
240
+ @mutex = Mutex.new
241
+ @resource = ConditionVariable.new
242
+ end
243
+
244
+ #
245
+ # Pushes +obj+ to the queue.
246
+ #
247
+ def push(obj)
248
+ @mutex.synchronize do
249
+ @que.push obj
250
+ @resource.signal
251
+ end
252
+ end
253
+
254
+ #
255
+ # Alias of push
256
+ #
257
+ alias << push
258
+
259
+ #
260
+ # Alias of push
261
+ #
262
+ alias enq push
263
+
264
+ #
265
+ # Retrieves data from the queue. If the queue is empty, the calling thread is
266
+ # suspended until data is pushed onto the queue. If +non_block+ is true, the
267
+ # thread isn't suspended, and an exception is raised.
268
+ #
269
+ def pop(non_block=false)
270
+ while true
271
+ @mutex.synchronize do
272
+ #FIXME: some code in net or somewhere violates encapsulation
273
+ #and demands that a waiting queue exist for Queue, as a result
274
+ #we have to do a linear search here to remove the current Thread.
275
+ @waiting.delete(Thread.current)
276
+ if @que.empty?
277
+ raise ThreadError, "queue empty" if non_block
278
+ @waiting.push Thread.current
279
+ @resource.wait(@mutex)
280
+ else
281
+ retval = @que.shift
282
+ @resource.signal
283
+ return retval
284
+ end
285
+ end
286
+ end
287
+ end
288
+
289
+ #
290
+ # Alias of pop
291
+ #
292
+ alias shift pop
293
+
294
+ #
295
+ # Alias of pop
296
+ #
297
+ alias deq pop
298
+
299
+ #
300
+ # Returns +true+ if the queue is empty.
301
+ #
302
+ def empty?
303
+ @que.empty?
304
+ end
305
+
306
+ #
307
+ # Removes all objects from the queue.
308
+ #
309
+ def clear
310
+ @que.clear
311
+ end
312
+
313
+ #
314
+ # Returns the length of the queue.
315
+ #
316
+ def length
317
+ @que.length
318
+ end
319
+
320
+ #
321
+ # Alias of length.
322
+ #
323
+ alias size length
324
+
325
+ #
326
+ # Returns the number of threads waiting on the queue.
327
+ #
328
+ def num_waiting
329
+ @waiting.size
330
+ end
331
+ end
332
+
333
+ #
334
+ # This class represents queues of specified size capacity. The push operation
335
+ # may be blocked if the capacity is full.
336
+ #
337
+ # See Queue for an example of how a SizedQueue works.
338
+ #
339
+ class SizedQueue < Queue
340
+ #
341
+ # Creates a fixed-length queue with a maximum size of +max+.
342
+ #
343
+ def initialize(max)
344
+ raise ArgumentError, "queue size must be positive" unless max > 0
345
+ @max = max
346
+ @queue_wait = []
347
+ @queue_wait.taint # enable tainted comunication
348
+ @size_mutex = Mutex.new
349
+ @sem = ConditionVariable.new
350
+ super()
351
+ end
352
+
353
+ #
354
+ # Returns the maximum size of the queue.
355
+ #
356
+ def max
357
+ @max
358
+ end
359
+
360
+ #
361
+ # Sets the maximum size of the queue.
362
+ #
363
+ def max=(max)
364
+ @size_mutex.synchronize do
365
+ @max = max
366
+ @sem.broadcast(@size_mutex)
367
+ end
368
+ max
369
+ end
370
+
371
+ #
372
+ # Pushes +obj+ to the queue. If there is no space left in the queue, waits
373
+ # until space becomes available.
374
+ #
375
+ def push(obj)
376
+ while true
377
+ @size_mutex.synchronize do
378
+ @queue_wait.delete(Thread.current)
379
+ if @que.size >= @max
380
+ @queue_wait.push Thread.current
381
+ @sem.wait(@size_mutex)
382
+ else
383
+ return super(obj)
384
+ end
385
+ end
386
+ end
387
+ end
388
+
389
+ #
390
+ # Alias of push
391
+ #
392
+ alias << push
393
+
394
+ #
395
+ # Alias of push
396
+ #
397
+ alias enq push
398
+
399
+ #
400
+ # Retrieves data from the queue and runs a waiting thread, if any.
401
+ #
402
+ def pop(*args)
403
+ retval = super
404
+
405
+ @size_mutex.synchronize do
406
+ if @que.size < @max
407
+ @sem.broadcast
408
+ end
409
+ end
410
+
411
+ return retval
412
+ end
413
+
414
+ #
415
+ # Alias of pop
416
+ #
417
+ alias shift pop
418
+
419
+ #
420
+ # Alias of pop
421
+ #
422
+ alias deq pop
423
+
424
+ #
425
+ # Returns the number of threads waiting on the queue.
426
+ #
427
+ def num_waiting
428
+ @waiting.size + @queue_wait.size
429
+ end
430
+ end
431
+
432
+ # Documentation comments:
433
+ # - How do you make RDoc inherit documentation from superclass?
@@ -0,0 +1,5 @@
1
+ module RubySL
2
+ module Thread
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require "rubysl/thread"
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ require './lib/rubysl/thread/version'
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "rubysl-thread"
6
+ spec.version = RubySL::Thread::VERSION
7
+ spec.authors = ["Brian Shirai"]
8
+ spec.email = ["brixen@gmail.com"]
9
+ spec.description = %q{Support classes for working with threads.}
10
+ spec.summary = %q{Support classes for working with threads.}
11
+ spec.homepage = "https://github.com/rubysl/rubysl-thread"
12
+ spec.license = "BSD"
13
+
14
+ spec.files = `git ls-files`.split($/)
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_development_dependency "bundler", "~> 1.3"
20
+ spec.add_development_dependency "rake", "~> 10.0"
21
+ spec.add_development_dependency "mspec", "~> 1.5"
22
+ end
@@ -0,0 +1,28 @@
1
+ ruby_version_is "".."1.9" do
2
+ require 'thread'
3
+
4
+ describe "Thread.exclusive" do
5
+ before :each do
6
+ ScratchPad.clear
7
+ end
8
+
9
+ it "sets Thread.critical to true and yields" do
10
+ Thread.exclusive { ScratchPad.record Thread.critical }
11
+ ScratchPad.recorded.should == true
12
+ end
13
+
14
+ it "returns the result of yielding" do
15
+ Thread.exclusive { :result }.should == :result
16
+ end
17
+
18
+ it "resets Thread.critical after yielding" do
19
+ Thread.exclusive {}
20
+ Thread.critical.should be_false
21
+ end
22
+
23
+ it "resets Thread.critical if the block raises" do
24
+ lambda { Thread.exclusive { raise Exception } }.should raise_error(Exception)
25
+ Thread.critical.should be_false
26
+ end
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubysl-thread
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Shirai
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.5'
55
+ description: Support classes for working with threads.
56
+ email:
57
+ - brixen@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - .travis.yml
64
+ - Gemfile
65
+ - LICENSE
66
+ - README.md
67
+ - Rakefile
68
+ - lib/rubysl/thread.rb
69
+ - lib/rubysl/thread/thread.rb
70
+ - lib/rubysl/thread/version.rb
71
+ - lib/thread.rb
72
+ - rubysl-thread.gemspec
73
+ - spec/exclusive_spec.rb
74
+ homepage: https://github.com/rubysl/rubysl-thread
75
+ licenses:
76
+ - BSD
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.0.7
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: Support classes for working with threads.
98
+ test_files:
99
+ - spec/exclusive_spec.rb