tool 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 725ca5923dffd3a0ae1191c35fd80011f341750b
4
- data.tar.gz: a09be4b173b7bd8aa8a8d2281ef2b2e274714ca8
3
+ metadata.gz: 3ad22826371837ebdeb6a642c310bf0b02815139
4
+ data.tar.gz: b16dc92838a3bb4776664d4cce4a868d3bf36067
5
5
  SHA512:
6
- metadata.gz: c28c6074f6bbb5827825f4376ad9c4866661988fbc9247a74488122cb71faf30ce46e26a6e84cdea20259c000acf9d49bdbe70aef7c375d30e112fec1bf1e2fd
7
- data.tar.gz: bfc3a87def2d13a293ded338fc9e4c3f3a2e6ebe3cc3faaf71ed922c8c69e4499a58eda3d08f59e1d22cffae6c7e842bf2a9e34332bd11d3a31bd670fe8c1efd
6
+ metadata.gz: 4e68bf215746003f18c31579c68b31993821d82aa55fd0933633da2955139e5a169ad72d16cffb2fdd1f735452956292401d598018f86d0aa90ff1db252bc720
7
+ data.tar.gz: 4aea8b0920a9e735b9fa3a2548b7562517be8841b3cd7bac2011d4e782346e4841006bf43c94a81fc382fd4b819ade7d7392589844fc84c8090ebba5b3099d86
data/README.md CHANGED
@@ -7,4 +7,4 @@ Included:
7
7
  * **Decoration** - Mixin for easy method decorations.
8
8
  * **EqualityMap** - Weak reference caching based on key equality.
9
9
  * **ThreadLocal** - True thread locals that are properly garbage collected.
10
- * **WarningFilter** - Enable Ruby's warnings, but filter out caused by third party gems.
10
+ * **WarningFilter** - Enable Ruby's warnings, but filter out those caused by third party gems.
@@ -29,17 +29,59 @@ module Tool
29
29
  # Thread.new { p local }.join # [:foo]
30
30
  # p local # [:foo, :bar]
31
31
  class ThreadLocal < Delegator
32
+ @mutex ||= Mutex.new
33
+ @locals ||= []
34
+
35
+ # Thread finalizer.
36
+ # @!visibility private
37
+ def self.cleanup(id)
38
+ @locals.keep_if do |local|
39
+ next false unless local.weakref_alive?
40
+ local.__cleanup__
41
+ true
42
+ end
43
+ end
44
+
45
+ # Generates weak reference to thread and sets up finalizer.
46
+ # @return [WeakRef]
47
+ # @!visibility private
48
+ def self.ref(thread)
49
+ thread[:weakref] ||= begin
50
+ ObjectSpace.define_finalizer(thread, method(:cleanup))
51
+ WeakRef.new(thread)
52
+ end
53
+ end
54
+
55
+ # @see #initialize
56
+ # @!visibility private
57
+ def self.new(*)
58
+ result = super(default)
59
+ @mutex.synchronize { @locals << WeakRef.new(result) }
60
+ result
61
+ end
62
+
32
63
  def initialize(default = {})
33
- @mutex = Mutex.new
34
64
  @default = default.dup
35
- @map = {}
65
+ @map = {}
36
66
  end
37
67
 
68
+ # @see Delegator
38
69
  # @!visibility private
39
70
  def __getobj__
40
- ref = Thread.current[:weakref] ||= WeakRef.new(Thread.current)
41
- @map.delete_if { |key, value| !key.weakref_alive? }
42
- @mutex.synchronize { @map[ref] ||= @default.dup }
71
+ ref = ::Tool::ThreadLocal.ref(Thread.current)
72
+ @map[ref] ||= @default.dup
73
+ end
74
+
75
+ # @return [Integer] number of threads with specific locals
76
+ # @!visibility private
77
+ def __size__
78
+ @map.size
79
+ end
80
+
81
+ # Remove locals for dead or GC'ed threads
82
+ # @!visibility private
83
+ def __cleanup__
84
+ @map.keep_if { |key, value| key.weakref_alive? and key.alive? }
43
85
  end
44
86
  end
45
87
  end
@@ -1,3 +1,3 @@
1
1
  module Tool
2
- VERSION = '0.2.0'
2
+ VERSION = '0.2.1'
3
3
  end
@@ -1,26 +1,71 @@
1
1
  require 'tool/thread_local'
2
2
 
3
3
  describe Tool::ThreadLocal do
4
- specify 'normal access' do
5
- subject[:foo] = 'bar'
6
- expect(subject[:foo]).to be == 'bar'
7
- end
4
+ describe :__getobj__ do
5
+ specify 'normal access' do
6
+ subject[:foo] = 'bar'
7
+ expect(subject[:foo]).to be == 'bar'
8
+ end
9
+
10
+ specify 'concurrent access' do
11
+ subject[:foo] = 'bar'
12
+ value = Thread.new { subject[:foo] = 'baz' }.value
13
+ expect(value).to be == 'baz'
14
+ expect(subject[:foo]).to be == 'bar'
15
+ end
16
+
17
+ specify 'with an array as value' do
18
+ list = Tool::ThreadLocal.new([])
19
+ foo = Thread.new { 10.times { list << :foo; sleep(0.01) }; list.to_a }
20
+ bar = Thread.new { 10.times { list << :bar; sleep(0.01) }; list.to_a }
21
+ expect(list).to be_empty
22
+ list << :list
23
+ expect(list) .to be == [ :list ]
24
+ expect(foo.value) .to be == [ :foo ] * 10
25
+ expect(bar.value) .to be == [ :bar ] * 10
26
+ end
27
+
28
+ specify 'deals with garbage collected threads' do
29
+ subject[:a] = 'A'
30
+
31
+ Thread.new do
32
+ subject[:b] = 'B'
33
+ Thread.new do
34
+ subject[:c] = 'C'
35
+ end.value
36
+ end.value
8
37
 
9
- specify 'concurrent access' do
10
- subject[:foo] = 'bar'
11
- value = Thread.new { subject[:foo] = 'baz' }.value
12
- expect(value).to be == 'baz'
13
- expect(subject[:foo]).to be == 'bar'
38
+ GC.start
39
+ expect(subject[:a]).to be == 'A'
40
+ end
14
41
  end
15
42
 
16
- specify 'with an array as value' do
17
- list = Tool::ThreadLocal.new([])
18
- foo = Thread.new { 10.times { list << :foo; sleep(0.01) }; list.to_a }
19
- bar = Thread.new { 10.times { list << :bar; sleep(0.01) }; list.to_a }
20
- expect(list).to be_empty
21
- list << :list
22
- expect(list) .to be == [ :list ]
23
- expect(foo.value) .to be == [ :foo ] * 10
24
- expect(bar.value) .to be == [ :bar ] * 10
43
+ describe :__size__ do
44
+ specify 'with one thread' do
45
+ subject[:a] = 'A'
46
+ expect(subject.__size__).to be == 1
47
+ end
48
+
49
+ specify 'with multiple threads' do
50
+ subject[:a] = 'A'
51
+ thread = Thread.new { subject[:b] = 'B'; sleep }
52
+ sleep 0.01
53
+ expect(subject.__size__).to be == 2
54
+ thread.kill
55
+ end
56
+
57
+ specify 'with dead threads' do
58
+ subject[:a] = 'A'
59
+
60
+ Thread.new do
61
+ subject[:b] = 'B'
62
+ Thread.new do
63
+ subject[:c] = 'C'
64
+ end.value
65
+ end.value
66
+
67
+ GC.start
68
+ expect(subject.__size__).to be == 1
69
+ end
25
70
  end
26
71
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tool
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Haase
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-22 00:00:00.000000000 Z
11
+ date: 2014-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec