rubysl-thread 1.0.0

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.
@@ -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