wref 0.0.6 → 0.0.7
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 +7 -0
- data/Gemfile +8 -4
- data/Gemfile.lock +68 -18
- data/README.md +65 -0
- data/VERSION +1 -1
- data/lib/wref.rb +38 -271
- data/lib/wref/implementations/id_class_unique.rb +50 -0
- data/lib/wref/implementations/java_weak_reference.rb +26 -0
- data/lib/wref/implementations/ref.rb +24 -0
- data/lib/wref/implementations/weak_ref.rb +53 -0
- data/lib/wref/implementations/weakling.rb +31 -0
- data/lib/wref/map.rb +175 -0
- data/shippable.yml +12 -0
- data/spec/implementations/id_class_unique_spec.rb +6 -0
- data/spec/implementations/java_weak_reference_spec.rb +6 -0
- data/spec/implementations/ref_spec.rb +6 -0
- data/spec/implementations/weak_ref_spec.rb +6 -0
- data/spec/implementations/weakling_spec.rb +6 -0
- data/spec/spec_helper.rb +10 -3
- data/spec/support/garbage_collector_helper.rb +33 -0
- data/spec/support/map_collection.rb +87 -0
- data/spec/support/user.rb +8 -0
- data/spec/support/wref_collection.rb +68 -0
- data/spec/wref_spec.rb +5 -63
- data/wref.gemspec +42 -19
- metadata +74 -43
- data/README.rdoc +0 -19
@@ -0,0 +1,50 @@
|
|
1
|
+
class Wref::Implementations::IdClassUnique
|
2
|
+
def initialize(object)
|
3
|
+
@id = object.__id__
|
4
|
+
@class_name = object.class.name.to_sym
|
5
|
+
ObjectSpace.define_finalizer(object, method(:destroy))
|
6
|
+
@unique_id = object.__wref_unique_id__ if object.respond_to?(:__wref_unique_id__)
|
7
|
+
end
|
8
|
+
|
9
|
+
def get!
|
10
|
+
object = get
|
11
|
+
raise ::Wref::Recycled unless object
|
12
|
+
return object
|
13
|
+
end
|
14
|
+
|
15
|
+
def get
|
16
|
+
return nil if !@class_name || !@id
|
17
|
+
object = ObjectSpace._id2ref(@id)
|
18
|
+
|
19
|
+
#Some times this class-name will be nil for some reason - knj
|
20
|
+
object_class_name = object.class.name
|
21
|
+
|
22
|
+
if !object_class_name || @class_name != object_class_name.to_sym || @id != object.__id__
|
23
|
+
destroy
|
24
|
+
return nil
|
25
|
+
end
|
26
|
+
|
27
|
+
if @unique_id
|
28
|
+
destroy
|
29
|
+
return nil if !object.respond_to?(:__wref_unique_id__) || object.__wref_unique_id__ != @unique_id
|
30
|
+
end
|
31
|
+
|
32
|
+
return object
|
33
|
+
end
|
34
|
+
|
35
|
+
def alive?
|
36
|
+
if get
|
37
|
+
return true
|
38
|
+
else
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def destroy(*args)
|
46
|
+
@id = nil
|
47
|
+
@class_name = nil
|
48
|
+
@unique_id = nil
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Wref::Implementations::JavaWeakReference
|
2
|
+
def initialize(object)
|
3
|
+
require "java"
|
4
|
+
@weakref = java.lang.ref.WeakReference.new(object)
|
5
|
+
end
|
6
|
+
|
7
|
+
def get
|
8
|
+
return @weakref.get
|
9
|
+
end
|
10
|
+
|
11
|
+
def get!
|
12
|
+
object = @weakref.get
|
13
|
+
raise Wref::Recycled if object == nil
|
14
|
+
return object
|
15
|
+
end
|
16
|
+
|
17
|
+
def alive?
|
18
|
+
object = @weakref.get
|
19
|
+
|
20
|
+
if object == nil
|
21
|
+
return false
|
22
|
+
else
|
23
|
+
return true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Wref::Implementations::Ref
|
2
|
+
def initialize(object)
|
3
|
+
require "ref"
|
4
|
+
@ref = ::Ref::WeakReference.new(object)
|
5
|
+
end
|
6
|
+
|
7
|
+
def get
|
8
|
+
@ref.object
|
9
|
+
end
|
10
|
+
|
11
|
+
def get!
|
12
|
+
object = @ref.object
|
13
|
+
raise Wref::Recycled unless object
|
14
|
+
return object
|
15
|
+
end
|
16
|
+
|
17
|
+
def alive?
|
18
|
+
if @ref.object
|
19
|
+
return true
|
20
|
+
else
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class Wref::Implementations::WeakRef
|
2
|
+
def initialize(object)
|
3
|
+
require "weakref"
|
4
|
+
@id = object.__id__
|
5
|
+
@class_name = object.class.name.to_sym
|
6
|
+
@weak_ref = ::WeakRef.new(object)
|
7
|
+
ObjectSpace.define_finalizer(object, method(:destroy))
|
8
|
+
end
|
9
|
+
|
10
|
+
def get
|
11
|
+
return nil unless @id
|
12
|
+
|
13
|
+
begin
|
14
|
+
object = @weak_ref.__getobj__
|
15
|
+
rescue WeakRef::RefError
|
16
|
+
destroy
|
17
|
+
return nil
|
18
|
+
end
|
19
|
+
|
20
|
+
object_class_name = object.class.name
|
21
|
+
|
22
|
+
if !object_class_name || @class_name != object_class_name.to_sym || @id != object.__id__
|
23
|
+
destroy
|
24
|
+
return nil
|
25
|
+
end
|
26
|
+
|
27
|
+
return object
|
28
|
+
end
|
29
|
+
|
30
|
+
def get!
|
31
|
+
if object = get
|
32
|
+
return object
|
33
|
+
else
|
34
|
+
raise Wref::Recycled
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def alive?
|
39
|
+
if @weak_ref.weakref_alive? && get
|
40
|
+
return true
|
41
|
+
else
|
42
|
+
return false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def destroy(*args)
|
49
|
+
@id = nil
|
50
|
+
@class_name = nil
|
51
|
+
@unique_id = nil
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Wref::Implementations::Weakling
|
2
|
+
def initialize(object)
|
3
|
+
require "weakling"
|
4
|
+
@weak_ref = ::Weakling::WeakRef.new(object)
|
5
|
+
end
|
6
|
+
|
7
|
+
def get
|
8
|
+
begin
|
9
|
+
@weak_ref.get
|
10
|
+
rescue ::WeakRef::RefError, ::Java::JavaLang::NullPointerException
|
11
|
+
return nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def get!
|
16
|
+
begin
|
17
|
+
@weak_ref.get
|
18
|
+
rescue ::WeakRef::RefError, ::Java::JavaLang::NullPointerException
|
19
|
+
raise Wref::Recycled
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def alive?
|
24
|
+
begin
|
25
|
+
@weak_ref.get
|
26
|
+
return true
|
27
|
+
rescue ::WeakRef::RefError, ::Java::JavaLang::NullPointerException
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/wref/map.rb
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
#A weak hash-map.
|
2
|
+
#===Examples
|
3
|
+
# map = Wref::Map.new
|
4
|
+
# map[1] = obj
|
5
|
+
# obj = nil
|
6
|
+
#
|
7
|
+
# sleep 0.5
|
8
|
+
#
|
9
|
+
# begin
|
10
|
+
# obj = map[1]
|
11
|
+
# print "Object still exists in memory."
|
12
|
+
# rescue Wref::Recycled
|
13
|
+
# print "Object has been garbage-collected."
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# obj = map.get(1)
|
17
|
+
# print "Object still exists in memory." if obj
|
18
|
+
class Wref::Map
|
19
|
+
def initialize(args = {})
|
20
|
+
require "monitor"
|
21
|
+
|
22
|
+
@map = {}
|
23
|
+
@mutex = Monitor.new
|
24
|
+
@impl = args[:impl]
|
25
|
+
end
|
26
|
+
|
27
|
+
#Sets a new object in the map with a given ID.
|
28
|
+
def set(id, obj)
|
29
|
+
wref = Wref.new(obj, impl: @impl)
|
30
|
+
|
31
|
+
@mutex.synchronize do
|
32
|
+
@map[id] = wref
|
33
|
+
end
|
34
|
+
|
35
|
+
return nil
|
36
|
+
end
|
37
|
+
|
38
|
+
#Returns an object by ID or raises a RefError.
|
39
|
+
#===Examples
|
40
|
+
# begin
|
41
|
+
# obj = map.get!(1)
|
42
|
+
# print "Object still exists in memory."
|
43
|
+
# rescue Wref::Recycled
|
44
|
+
# print "Object has been garbage-collected."
|
45
|
+
# end
|
46
|
+
def get!(id)
|
47
|
+
wref = nil
|
48
|
+
@mutex.synchronize do
|
49
|
+
raise Wref::Recycled unless @map.key?(id)
|
50
|
+
wref = @map[id]
|
51
|
+
end
|
52
|
+
|
53
|
+
if object = wref.get
|
54
|
+
return object
|
55
|
+
else
|
56
|
+
delete(id)
|
57
|
+
raise Wref::Recycled
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
#The same as 'get!' but returns nil instead of WeakRef-error. This can be used to avoid writing lots of code.
|
62
|
+
#===Examples
|
63
|
+
# obj = map.get(1)
|
64
|
+
# print "Object still exists in memory." if obj
|
65
|
+
def get(id)
|
66
|
+
begin
|
67
|
+
return get!(id)
|
68
|
+
rescue Wref::Recycled
|
69
|
+
return nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
#Scans the whole map and removes dead references. After the implementation of automatic clean-up by using ObjectSpace.define_finalizer, there should be no reason to call this method.
|
74
|
+
def clean
|
75
|
+
keys = nil
|
76
|
+
@mutex.synchronize do
|
77
|
+
keys = @map.keys
|
78
|
+
end
|
79
|
+
|
80
|
+
keys.each do |key|
|
81
|
+
begin
|
82
|
+
get(key) #this will remove the key if the object no longer exists.
|
83
|
+
rescue Wref::Recycled
|
84
|
+
#ignore.
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
return nil
|
89
|
+
end
|
90
|
+
|
91
|
+
#Returns true if a given key exists and the object it holds is alive.
|
92
|
+
def valid?(key)
|
93
|
+
@mutex.synchronize do
|
94
|
+
return false unless @map.key?(key)
|
95
|
+
end
|
96
|
+
|
97
|
+
begin
|
98
|
+
@map[key].get
|
99
|
+
return true
|
100
|
+
rescue Wref::Recycled
|
101
|
+
return false
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
#Returns true if the given key exists in the hash.
|
106
|
+
#===Examples
|
107
|
+
# print "Key exists but we dont know if the value has been garbage-collected." if map.key?(1)
|
108
|
+
def key?(key)
|
109
|
+
@mutex.synchronize do
|
110
|
+
if @map.key?(key) && get(key)
|
111
|
+
return true
|
112
|
+
else
|
113
|
+
return false
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
#Returns the length of the hash. This may not be true since invalid objects is also counted.
|
119
|
+
def length
|
120
|
+
@mutex.synchronize do
|
121
|
+
return @map.length
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
#Cleans the hash and returns the length. This is slower but more accurate than the ordinary length that just returns the hash-length.
|
126
|
+
def length_valid
|
127
|
+
clean
|
128
|
+
return length
|
129
|
+
end
|
130
|
+
|
131
|
+
#Deletes a key in the hash.
|
132
|
+
def delete(key)
|
133
|
+
@mutex.synchronize do
|
134
|
+
wref = @map[key]
|
135
|
+
object = @map.delete(key)
|
136
|
+
|
137
|
+
if object
|
138
|
+
return object.get
|
139
|
+
else
|
140
|
+
return nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
#Iterates over every valid object in the weak map.
|
146
|
+
#===Examples
|
147
|
+
# map.each do |obj|
|
148
|
+
# puts "Object alive: #{obj}"
|
149
|
+
# end
|
150
|
+
def each(&block)
|
151
|
+
enum = Enumerator.new do |yielder|
|
152
|
+
ids = nil
|
153
|
+
@mutex.synchronize do
|
154
|
+
ids = @map.keys
|
155
|
+
end
|
156
|
+
|
157
|
+
ids.each do |id|
|
158
|
+
if obj = get(id)
|
159
|
+
yielder << [id, obj]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
if block
|
165
|
+
enum.each(&block)
|
166
|
+
else
|
167
|
+
return enum
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
#Make it hash-compatible.
|
172
|
+
alias has_key? key?
|
173
|
+
alias [] get
|
174
|
+
alias []= set
|
175
|
+
end
|
data/shippable.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- ruby-head
|
4
|
+
- ruby-2.1-head
|
5
|
+
- ruby-2.0-head
|
6
|
+
- ruby-1.9.3-head
|
7
|
+
- ruby-1.9.2-head
|
8
|
+
- jruby-head
|
9
|
+
script:
|
10
|
+
- CODECLIMATE_REPO_TOKEN=23d002ad9b86687a067e6bbc81320cd979ffad756d1be69ca4e24c1038ee19a6 bundle exec rspec
|
11
|
+
notifications:
|
12
|
+
email: false
|
data/spec/spec_helper.rb
CHANGED
@@ -1,12 +1,19 @@
|
|
1
|
+
require "codeclimate-test-reporter"
|
2
|
+
CodeClimate::TestReporter.start
|
3
|
+
|
1
4
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
5
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
-
require
|
4
|
-
require
|
6
|
+
require "rspec"
|
7
|
+
require "wref"
|
8
|
+
require "weakling" if RUBY_ENGINE == "jruby"
|
9
|
+
require "ref"
|
5
10
|
|
6
11
|
# Requires supporting files with custom matchers and macros, etc,
|
7
12
|
# in ./support/ and its subdirectories.
|
8
13
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
14
|
|
10
15
|
RSpec.configure do |config|
|
11
|
-
|
16
|
+
config.expect_with :rspec do |c|
|
17
|
+
c.syntax = [:should, :expect]
|
18
|
+
end
|
12
19
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module GarbageCollectorHelper
|
2
|
+
def force_garbage_collect
|
3
|
+
GC.enable
|
4
|
+
|
5
|
+
sleep 0.01
|
6
|
+
|
7
|
+
if RUBY_ENGINE == "jruby"
|
8
|
+
java.lang.System.gc
|
9
|
+
else
|
10
|
+
if RUBY_VERSION.start_with?("2")
|
11
|
+
GC.start(full_mark: true, immediate_sweep: true)
|
12
|
+
else
|
13
|
+
GC.start
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
sleep 0.01
|
18
|
+
|
19
|
+
GC.disable
|
20
|
+
end
|
21
|
+
|
22
|
+
def force_garbage_collection
|
23
|
+
force_garbage_collect
|
24
|
+
|
25
|
+
10000.times do
|
26
|
+
some_str = User.new("User #{Digest::MD5.hexdigest(Time.now.to_f.to_s)}")
|
27
|
+
weak_ref = described_class.new(some_str)
|
28
|
+
some_str = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
force_garbage_collect
|
32
|
+
end
|
33
|
+
end
|