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 +4 -4
- data/README.md +1 -1
- data/lib/tool/thread_local.rb +47 -5
- data/lib/tool/version.rb +1 -1
- data/spec/thread_local_spec.rb +63 -18
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ad22826371837ebdeb6a642c310bf0b02815139
|
4
|
+
data.tar.gz: b16dc92838a3bb4776664d4cce4a868d3bf36067
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
data/lib/tool/thread_local.rb
CHANGED
@@ -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 =
|
41
|
-
@map
|
42
|
-
|
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
|
data/lib/tool/version.rb
CHANGED
data/spec/thread_local_spec.rb
CHANGED
@@ -1,26 +1,71 @@
|
|
1
1
|
require 'tool/thread_local'
|
2
2
|
|
3
3
|
describe Tool::ThreadLocal do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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.
|
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-
|
11
|
+
date: 2014-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|