tool 0.2.0 → 0.2.1

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