ref-2.0.0/0000755000175000017500000000000012547750061011524 5ustar sbadiasbadiaref-2.0.0/Rakefile0000644000175000017500000000430112547750061013167 0ustar sbadiasbadia#!/usr/bin/env rake $:.push File.join(File.dirname(__FILE__), 'lib') require 'ref' begin require 'rspec' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) do |t| t.rspec_opts = '--color --backtrace --format documentation' end task :default => :spec rescue LoadError puts 'Error loading Rspec rake tasks, probably building the gem...' end spec = eval(File.read(File.expand_path('../ref.gemspec', __FILE__))) GEM_NAME = 'ref' EXTENSION_NAME = 'extension' JAVA_EXT_NAME = 'ref_ext' CORE_GEMSPEC = Gem::Specification.load('ref.gemspec') if Ref.jruby? CORE_GEM = "#{GEM_NAME}-#{Ref::VERSION}-java.gem" require 'rake/javaextensiontask' Rake::JavaExtensionTask.new(JAVA_EXT_NAME, CORE_GEMSPEC) do |ext| ext.ext_dir = 'ext' end else CORE_GEM = "#{GEM_NAME}-#{Ref::VERSION}.gem" end task :clean do rm_f Dir.glob('./**/*.so') rm_f Dir.glob('./**/*.bundle') rm_f Dir.glob('./lib/*.jar') mkdir_p 'pkg' end namespace :build do build_deps = [:clean] build_deps << :compile if Ref.jruby? desc "Build #{CORE_GEM} into the pkg directory" task :core => build_deps do sh "gem build #{CORE_GEMSPEC.name}.gemspec" sh 'mv *.gem pkg/' end end task :build => ['build:core'] namespace :test do namespace :performance do desc "Run a speed test on how long it takes to create 100000 weak references" task :weak_reference do puts "Testing performance of weak references..." unless Ref.jruby? t = Time.now Process.fork do 100000.times do Ref::WeakReference.new(Object.new) end end Process.wait puts "Creating 100,000 weak references took #{Time.now - t} seconds" else puts 'Cannot run weak_reference performance test on JRuby - Fork is not available on this platform.' end end desc "Run a speed test on how long it takes to create 100000 soft references" task :soft_reference do puts "Testing performance of soft references..." t = Time.now 100000.times do Ref::SoftReference.new(Object.new) end GC.start GC.start puts "Creating 100,000 soft references took #{Time.now - t} seconds" end end end ref-2.0.0/.gitignore0000644000175000017500000000010412547750061013507 0ustar sbadiasbadiapkg tmp rdoc *.rbc coverage .ruby-version .ruby-gemset Gemfile.lock ref-2.0.0/HISTORY.txt0000644000175000017500000000063712547750061013434 0ustar sbadiasbadia1.0.5 - Fix breaking test in ruby 2.0 1.0.4 - Support for BasicObject in pure ruby weak reference implementation. 1.0.3 - Support Ruby 2.0 WeakRef implementation - Replace autoload with require to make library thread safe 1.0.2 - Fix mock object used for testing (Burgestrand) 1.0.1 - No code changes. Just including the license file in the release and removing deprecated tasks. 1.0.0 - Initial release.ref-2.0.0/.travis.yml0000644000175000017500000000043012547750061013632 0ustar sbadiasbadialanguage: ruby rvm: - 2.2.0 - 2.1.5 - 2.1.4 - 2.0.0 - 1.9.3 - ruby-head - jruby-1.7.18 - jruby-head - rbx-2 jdk: - oraclejdk8 sudo: false branches: only: - master matrix: allow_failures: - rvm: ruby-head - rvm: jruby-head - rvm: 1.9.3 ref-2.0.0/README.md0000644000175000017500000000650512547750061013011 0ustar sbadiasbadia# Ref [![Gem Version](https://badge.fury.io/rb/ref.svg)](http://badge.fury.io/rb/ref) [![Build Status](https://travis-ci.org/ruby-concurrency/ref.svg?branch=master)](https://travis-ci.org/ruby-concurrency/ref) [![Coverage Status](https://img.shields.io/coveralls/ruby-concurrency/ref/master.svg)](https://coveralls.io/r/ruby-concurrency/ref) [![Code Climate](https://codeclimate.com/github/ruby-concurrency/ref.svg)](https://codeclimate.com/github/ruby-concurrency/ref) [![Dependency Status](https://gemnasium.com/ruby-concurrency/ref.svg)](https://gemnasium.com/ruby-concurrency/ref) [![License](https://img.shields.io/badge/license-MIT-green.svg)](http://opensource.org/licenses/MIT) [![Gitter chat](http://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/ruby-concurrency/concurrent-ruby) This library provides object references for Ruby as well as some common utilities for working with references. Object references are used to point to other objects and come in three distinct flavors that interact differently with the garbage collector. * `Ref::StrongReference` - This is a plain old pointer to another object. * `Ref::WeakReference` - This is a pointer to another object, but it is not seen by the garbage collector and the memory used by the object can be reclaimed at any time. * `Ref::SoftReference` - This is similar to a weak reference, but the garbage collector is not as eager to reclaim the referenced object. All of these classes extend from a common `Ref::Reference` class and have a common interface. Weak and soft references are useful when you have instantiated objects that you may want to use again but can recreate if necessary. Since the garbage collector determines when to reclaim the memory used by the objects, you don't need to worry about bloating the Ruby heap. ## Example Usage ```ruby ref = Ref::WeakReference.new("hello") ref.object # should be "hello" ObjectSpace.garbage_collect ref.object # should be nil (assuming the garbage collector reclaimed the reference) ``` ## Goodies This library also includes tools for some common uses of weak and soft references. * `Ref::WeakKeyMap` - A map of keys to values where the keys are weak references * `Ref::WeakValueMap` - A map of keys to values where the values are weak references * `Ref::SoftKeyMap` - A map of keys to values where the keys are soft references * `Ref::SoftValueMap` - A map of keys to values where the values are soft references * `Ref::ReferenceQueue` - A thread safe implementation of a queue that will add references to itself as their objects are garbage collected. ## Problems with WeakRef Ruby does come with the `WeakRef` class in the standard library. However, there are [issues with this class](https://bugs.ruby-lang.org/issues/4168) across several different Ruby runtimes. This gem provides a common interface to weak references that works across MRI, Ruby Enterprise Edition, YARV, JRuby and Rubinius. 1. Rubinius - Rubinius implements `WeakRef` with a lighter weight version of delegation and works very well. 2. YARV 1.9 - `WeakRef` is unsafe to use because the garbage collector can run in a different system thread than a thread allocating memory. This exposes a bug where a `WeakRef` may end up pointing to a completely different object than it originally referenced. 3. MRI Ruby 2.0+ has a good implementation of `WeakRef`. ref-2.0.0/ref.gemspec0000644000175000017500000000214112547750061013643 0ustar sbadiasbadia$:.push File.join(File.dirname(__FILE__), 'lib') require 'ref/version' Gem::Specification.new do |s| s.name = 'ref' s.version = Ref::VERSION s.authors = ['Brian Durand', 'The Ruby Concurrency Team'] s.email = ['bbdurand@gmail.com', 'concurrent-ruby@googlegroups.com'] s.homepage = "http://github.com/ruby-concurrency/ref" s.summary = "Library that implements weak, soft, and strong references in Ruby." s.description = "Library that implements weak, soft, and strong references in Ruby that work across multiple runtimes (MRI,Jruby and Rubinius). Also includes implementation of maps/hashes that use references and a reference queue." s.license = "MIT" s.date = Time.now.strftime('%Y-%m-%d') s.files = ['README.md', 'MIT_LICENSE'] s.files += Dir['lib/**/*.*'] s.files += Dir['ext/**/*.*'] s.files += Dir['test/**/*.*'] s.require_paths = ['lib'] s.has_rdoc = true s.rdoc_options = ["--charset=UTF-8", "--main", "README.md"] s.extra_rdoc_files = ["README.md"] s.required_ruby_version = '>= 1.9.3' end ref-2.0.0/lib/0000755000175000017500000000000012547750061012272 5ustar sbadiasbadiaref-2.0.0/lib/ref.rb0000644000175000017500000000263512547750061013401 0ustar sbadiasbadiamodule Ref $LOAD_PATH.unshift(File.dirname(__FILE__)) require 'ref/abstract_reference_value_map' require 'ref/abstract_reference_key_map' require 'ref/reference' require 'ref/reference_queue' if defined?(Java) begin require 'ref_ext' require 'org/jruby/ext/ref/references' rescue LoadError require 'ref/soft_reference' require 'ref/weak_reference' warn 'Error loading Rspec rake tasks, probably building the gem...' end else require 'ref/soft_reference' if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx' # If using Rubinius set the implementation to use WeakRef since it is very efficient and using finalizers is not. require 'ref/weak_reference/weak_ref' elsif defined?(::ObjectSpace::WeakMap) # Ruby 2.0 has a working implementation of weakref.rb backed by the new ObjectSpace::WeakMap require 'ref/weak_reference/weak_ref' elsif defined?(::ObjectSpace._id2ref) # If ObjectSpace can lookup objects from their object_id, then use the pure ruby implementation. require 'ref/weak_reference/pure_ruby' else # Otherwise, wrap the standard library WeakRef class require 'ref/weak_reference/weak_ref' end end require 'ref/soft_key_map' require 'ref/soft_value_map' require 'ref/strong_reference' require 'ref/weak_key_map' require 'ref/weak_value_map' def self.jruby? defined?(Java) end end ref-2.0.0/lib/ref/0000755000175000017500000000000012547750061013046 5ustar sbadiasbadiaref-2.0.0/lib/ref/version.rb0000644000175000017500000000004312547750061015055 0ustar sbadiasbadiamodule Ref VERSION = '2.0.0' end ref-2.0.0/lib/ref/weak_key_map.rb0000644000175000017500000000211112547750061016022 0ustar sbadiasbadiamodule Ref # Implementation of a map in which only weakly referenced keys are kept to the map values. # This allows the garbage collector to reclaim these objects if the only reference to them # is the weak reference in the map. # # This is often useful for cache implementations since the map can be allowed to grow # without bound and the garbage collector can be relied on to clean it up as necessary. # One must be careful, though, when accessing entries since they can be collected at # any time until there is a strong reference to the key. # # === Example usage: # # cache = Ref::WeakKeyMap.new # obj = MyObject.find_by_whatever # obj_info = Service.lookup_object_info(obj) # cache[obj] = Service.lookup_object_info(obj) # cache[obj] # The values looked up from the service # obj = nil # ObjectSpace.garbage_collect # cache.keys # empty array since the keys and values have been reclaimed # # See AbstractReferenceKeyMap for details. class WeakKeyMap < AbstractReferenceKeyMap self.reference_class = WeakReference end end ref-2.0.0/lib/ref/strong_reference.rb0000644000175000017500000000067512547750061016735 0ustar sbadiasbadiamodule Ref # This implementation of Reference holds a strong reference to an object. The # referenced object will not be garbage collected as long as the strong reference # exists. class StrongReference < Reference # Create a new strong reference to an object. def initialize(obj) @obj = obj @referenced_object_id = obj.__id__ end # Get the referenced object. def object @obj end end end ref-2.0.0/lib/ref/weak_reference.rb0000644000175000017500000000161112547750061016337 0ustar sbadiasbadiamodule Ref # A WeakReference represents a reference to an object that is not seen by # the tracing phase of the garbage collector. This allows the referenced # object to be garbage collected as if nothing is referring to it. # # === Example usage: # # foo = Object.new # ref = Ref::WeakReference.new(foo) # ref.object # should be foo # ObjectSpace.garbage_collect # ref.object # should be nil class WeakReference < Reference # Create a weak reference to an object. def initialize(obj) raise NotImplementedError.new("This is an abstract class; you must require an implementation") end # Get the referenced object. If the object has been reclaimed by the # garbage collector, then this will return nil. def object raise NotImplementedError.new("This is an abstract class; you must require an implementation") end end end ref-2.0.0/lib/ref/soft_value_map.rb0000644000175000017500000000217012547750061016377 0ustar sbadiasbadiamodule Ref # Implementation of a map in which soft references are kept to the map values. # This allows the garbage collector to reclaim these objects if the # only reference to them is the soft reference in the map. # # This is often useful for cache implementations since the map can be allowed to grow # without bound and the garbage collector can be relied on to clean it up as necessary. # One must be careful, though, when accessing entries since the values can be collected # at any time until there is a strong reference to them. # # === Example usage: # # cache = Ref::SoftValueMap.new # foo = "foo" # cache["strong"] = foo # add a value with a strong reference # cache["soft"] = "bar" # add a value without a strong reference # cache["strong"] # "foo" # cache["soft"] # "bar" # ObjectSpace.garbage_collect # ObjectSpace.garbage_collect # cache["strong"] # "foo" # cache["soft"] # nil # # See AbstractReferenceValueMap for details. class SoftValueMap < AbstractReferenceValueMap self.reference_class = SoftReference end end ref-2.0.0/lib/ref/abstract_reference_value_map.rb0000644000175000017500000001074712547750061021256 0ustar sbadiasbadiamodule Ref # Abstract base class for WeakValueMap and SoftValueMap. # # The classes behave similar to Hashes, but the values in the map are not strong references # and can be reclaimed by the garbage collector at any time. When a value is reclaimed, the # map entry will be removed. class AbstractReferenceValueMap class << self def reference_class=(klass) #:nodoc: @reference_class = klass end def reference_class #:nodoc: raise NotImplementedError.new("#{name} is an abstract class and cannot be instantiated") unless @reference_class @reference_class end end # Create a new map. Values added to the map will be cleaned up by the garbage # collector if there are no other reference except in the map. def initialize @references = {} @references_to_keys_map = {} @lock = Monitor.new @reference_cleanup = lambda{|object_id| remove_reference_to(object_id)} end # Get a value from the map by key. If the value has been reclaimed by the garbage # collector, this will return nil. def [](key) @lock.synchronize do ref = @references[key] value = ref.object if ref value end end alias_method :get, :[] # Add a key/value to the map. def []=(key, value) ObjectSpace.define_finalizer(value, @reference_cleanup) key = key.dup if key.is_a?(String) @lock.synchronize do @references[key] = self.class.reference_class.new(value) keys_for_id = @references_to_keys_map[value.__id__] unless keys_for_id keys_for_id = [] @references_to_keys_map[value.__id__] = keys_for_id end keys_for_id << key end value end alias_method :put, :[]= # Remove the entry associated with the key from the map. def delete(key) ref = @references.delete(key) if ref keys_to_id = @references_to_keys_map[ref.referenced_object_id] if keys_to_id keys_to_id.delete(key) @references_to_keys_map.delete(ref.referenced_object_id) if keys_to_id.empty? end ref.object else nil end end # Get the list of all values that have not yet been garbage collected. def values vals = [] each{|k,v| vals << v} vals end # Turn the map into an arry of [key, value] entries def to_a array = [] each{|k,v| array << [k, v]} array end # Returns a hash containing the names and values for the struct’s members. def to_h hash = {} each{|k,v| hash[k] = v} hash end # Iterate through all the key/value pairs in the map that have not been reclaimed # by the garbage collector. def each @references.each do |key, ref| value = ref.object yield(key, value) if value end end # Clear the map of all key/value pairs. def clear @lock.synchronize do @references.clear @references_to_keys_map.clear end end # Returns a new struct containing the contents of `other` and the contents # of `self`. If no block is specified, the value for entries with duplicate # keys will be that of `other`. Otherwise the value for each duplicate key # is determined by calling the block with the key, its value in `self` and # its value in `other`. def merge(other_hash, &block) to_h.merge(other_hash, &block).reduce(self.class.new) do |map, pair| map[pair.first] = pair.last map end end # Merge the values from another hash into this map. def merge!(other_hash) @lock.synchronize do other_hash.each { |key, value| self[key] = value } end end # The number of entries in the map def size @references.count do |_, ref| ref.object end end alias_method :length, :size # True if there are entries that exist in the map def empty? @references.each do |_, ref| return false if ref.object end true end def inspect live_entries = {} each do |key, value| live_entries[key] = value end live_entries.inspect end private def remove_reference_to(object_id) @lock.synchronize do keys = @references_to_keys_map[object_id] if keys keys.each do |key| @references.delete(key) end @references_to_keys_map.delete(object_id) end end end end end ref-2.0.0/lib/ref/abstract_reference_key_map.rb0000644000175000017500000001032012547750061020715 0ustar sbadiasbadiamodule Ref # Abstract base class for WeakKeyMap and SoftKeyMap. # # The classes behave similar to Hashes, but the keys in the map are not strong references # and can be reclaimed by the garbage collector at any time. When a key is reclaimed, the # map entry will be removed. class AbstractReferenceKeyMap class << self def reference_class=(klass) #:nodoc: @reference_class = klass end def reference_class #:nodoc: raise NotImplementedError.new("#{name} is an abstract class and cannot be instantiated") unless @reference_class @reference_class end end # Create a new map. Values added to the hash will be cleaned up by the garbage # collector if there are no other reference except in the map. def initialize @values = {} @references_to_keys_map = {} @lock = Monitor.new @reference_cleanup = lambda{|object_id| remove_reference_to(object_id)} end # Get a value from the map by key. If the value has been reclaimed by the garbage # collector, this will return nil. def [](key) @lock.synchronize do rkey = ref_key(key) @values[rkey] if rkey end end alias_method :get, :[] # Add a key/value to the map. def []=(key, value) ObjectSpace.define_finalizer(key, @reference_cleanup) @lock.synchronize do @references_to_keys_map[key.__id__] = self.class.reference_class.new(key) @values[key.__id__] = value end end alias_method :put, :[]= # Remove the value associated with the key from the map. def delete(key) @lock.synchronize do rkey = ref_key(key) if rkey @references_to_keys_map.delete(rkey) @values.delete(rkey) else nil end end end # Get an array of keys that have not yet been garbage collected. def keys @values.keys.collect{|rkey| @references_to_keys_map[rkey].object}.compact end # Turn the map into an arry of [key, value] entries. def to_a array = [] each{|k,v| array << [k, v]} array end # Returns a hash containing the names and values for the struct’s members. def to_h hash = {} each{|k,v| hash[k] = v} hash end # Iterate through all the key/value pairs in the map that have not been reclaimed # by the garbage collector. def each @references_to_keys_map.each do |rkey, ref| key = ref.object yield(key, @values[rkey]) if key end end # Clear the map of all key/value pairs. def clear @lock.synchronize do @values.clear @references_to_keys_map.clear end end # Returns a new struct containing the contents of `other` and the contents # of `self`. If no block is specified, the value for entries with duplicate # keys will be that of `other`. Otherwise the value for each duplicate key # is determined by calling the block with the key, its value in `self` and # its value in `other`. def merge(other_hash, &block) to_h.merge(other_hash, &block).reduce(self.class.new) do |map, pair| map[pair.first] = pair.last map end end # Merge the values from another hash into this map. def merge!(other_hash) @lock.synchronize do other_hash.each { |key, value| self[key] = value } end end # The number of entries in the map def size @references_to_keys_map.count do |_, ref| ref.object end end alias_method :length, :size # True if there are entries that exist in the map def empty? @references_to_keys_map.each do |_, ref| return false if ref.object end true end def inspect live_entries = {} each do |key, value| live_entries[key] = value end live_entries.inspect end private def ref_key (key) ref = @references_to_keys_map[key.__id__] if ref && ref.object ref.referenced_object_id else nil end end def remove_reference_to(object_id) @lock.synchronize do @references_to_keys_map.delete(object_id) @values.delete(object_id) end end end end ref-2.0.0/lib/ref/reference.rb0000644000175000017500000000147112547750061015334 0ustar sbadiasbadiamodule Ref # This class serves as a generic reference mechanism to other objects. The # actual reference can be either a WeakReference, SoftReference, or StrongReference. class Reference # The object id of the object being referenced. attr_reader :referenced_object_id # Create a new reference to an object. def initialize(obj) raise NotImplementedError.new("cannot instantiate a generic reference") end # Get the referenced object. This could be nil if the reference # is a WeakReference or a SoftReference and the object has been reclaimed by the garbage collector. def object raise NotImplementedError end def inspect obj = object "<##{self.class.name}: #{obj ? obj.inspect : "##{referenced_object_id} (not accessible)"}>" end end end ref-2.0.0/lib/ref/soft_reference.rb0000644000175000017500000000422312547750061016365 0ustar sbadiasbadiamodule Ref # A SoftReference represents a reference to an object that is not seen by # the tracing phase of the garbage collector. This allows the referenced # object to be garbage collected as if nothing is referring to it. # # A SoftReference differs from a WeakReference in that the garbage collector # is not so eager to reclaim soft references so they should persist longer. # # === Example usage: # # foo = Object.new # ref = Ref::SoftReference.new(foo) # ref.object # should be foo # ObjectSpace.garbage_collect # ref.object # should be foo # ObjectSpace.garbage_collect # ObjectSpace.garbage_collect # ref.object # should be nil class SoftReference < Reference @@strong_references = [{}] @@gc_flag_set = false # Number of garbage collection cycles after an object is used before a reference to it can be reclaimed. MIN_GC_CYCLES = 10 @@lock = Monitor.new @@finalizer = lambda do |object_id| @@lock.synchronize do while @@strong_references.size >= MIN_GC_CYCLES do @@strong_references.shift end @@strong_references.push({}) if @@strong_references.size < MIN_GC_CYCLES @@gc_flag_set = false end end # Create a new soft reference to an object. def initialize(obj) @referenced_object_id = obj.__id__ @weak_reference = WeakReference.new(obj) add_strong_reference(obj) end # Get the referenced object. If the object has been reclaimed by the # garbage collector, then this will return nil. def object obj = @weak_reference.object # add a temporary strong reference each time the object is referenced. add_strong_reference(obj) if obj obj end private # Create a strong reference to the object. This reference will live # for three passes of the garbage collector. def add_strong_reference(obj) #:nodoc: @@lock.synchronize do @@strong_references.last[obj] = true unless @@gc_flag_set @@gc_flag_set = true ObjectSpace.define_finalizer(Object.new, @@finalizer) end end end end end ref-2.0.0/lib/ref/soft_key_map.rb0000644000175000017500000000211112547750061016046 0ustar sbadiasbadiamodule Ref # Implementation of a map in which only softly referenced keys are kept to the map values. # This allows the garbage collector to reclaim these objects if the only reference to them # is the soft reference in the map. # # This is often useful for cache implementations since the map can be allowed to grow # without bound and the garbage collector can be relied on to clean it up as necessary. # One must be careful, though, when accessing entries since they can be collected at # any time until there is a strong reference to the key. # # === Example usage: # # cache = Ref::SoftKeyMap.new # obj = MyObject.find_by_whatever # obj_info = Service.lookup_object_info(obj) # cache[obj] = Service.lookup_object_info(obj) # cache[obj] # The values looked up from the service # obj = nil # ObjectSpace.garbage_collect # cache.keys # empty array since the keys and values have been reclaimed # # See AbstractReferenceKeyMap for details. class SoftKeyMap < AbstractReferenceKeyMap self.reference_class = SoftReference end end ref-2.0.0/lib/ref/weak_reference/0000755000175000017500000000000012547750061016013 5ustar sbadiasbadiaref-2.0.0/lib/ref/weak_reference/weak_ref.rb0000644000175000017500000000120212547750061020116 0ustar sbadiasbadiarequire 'weakref' module Ref class WeakReference < Reference # This implementation of a weak reference simply wraps the standard WeakRef implementation # that comes with the Ruby standard library. def initialize(obj) #:nodoc: @referenced_object_id = obj.__id__ @ref = ::WeakRef.new(obj) end def object #:nodoc: @ref.__getobj__ rescue => e # Jruby implementation uses RefError while MRI uses WeakRef::RefError if (defined?(RefError) && e.is_a?(RefError)) || (defined?(::WeakRef::RefError) && e.is_a?(::WeakRef::RefError)) nil else raise e end end end end ref-2.0.0/lib/ref/weak_reference/pure_ruby.rb0000644000175000017500000000712312547750061020357 0ustar sbadiasbadiamodule Ref # This is a pure ruby implementation of a weak reference. It is much more # efficient than the WeakRef implementation bundled in MRI 1.8 and 1.9 # subclass Delegator which is very heavy to instantiate and utilizes a # because it does not fair amount of memory under Ruby 1.8. class WeakReference < Reference class ReferencePointer def initialize(object) @referenced_object_id = object.__id__ add_backreference(object) end def cleanup obj = ObjectSpace._id2ref(@referenced_object_id) rescue nil remove_backreference(obj) if obj end def object obj = ObjectSpace._id2ref(@referenced_object_id) obj if verify_backreferences(obj) rescue RangeError nil end private # Verify that the object is the same one originally set for the weak reference. def verify_backreferences(obj) #:nodoc: return nil unless supports_backreference?(obj) backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__) backreferences && backreferences.include?(object_id) end # Add a backreference to the object. def add_backreference(obj) #:nodoc: return unless supports_backreference?(obj) backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__) unless backreferences backreferences = [] obj.instance_variable_set(:@__weak_backreferences__, backreferences) end backreferences << object_id end # Remove backreferences from the object. def remove_backreference(obj) #:nodoc: return unless supports_backreference?(obj) backreferences = obj.instance_variable_get(:@__weak_backreferences__) if obj.instance_variable_defined?(:@__weak_backreferences__) if backreferences backreferences.dup.delete(object_id) obj.send(:remove_instance_variable, :@__weak_backreferences__) if backreferences.empty? end end def supports_backreference?(obj) obj.respond_to?(:instance_variable_get) && obj.respond_to?(:instance_variable_defined?) rescue NoMethodError false end end @@weak_references = {} @@lock = Monitor.new # Finalizer that cleans up weak references when references are destroyed. @@reference_finalizer = lambda do |object_id| @@lock.synchronize do reference_pointer = @@weak_references.delete(object_id) reference_pointer.cleanup if reference_pointer end end # Create a new weak reference to an object. The existence of the weak reference # will not prevent the garbage collector from reclaiming the referenced object. def initialize(obj) #:nodoc: @referenced_object_id = obj.__id__ @@lock.synchronize do @reference_pointer = ReferencePointer.new(obj) @@weak_references[self.object_id] = @reference_pointer end ObjectSpace.define_finalizer(self, @@reference_finalizer) end # Get the reference object. If the object has already been garbage collected, # then this method will return nil. def object #:nodoc: if @reference_pointer obj = @reference_pointer.object unless obj @@lock.synchronize do @@weak_references.delete(object_id) @reference_pointer.cleanup @reference_pointer = nil end end obj end end end end ref-2.0.0/lib/ref/weak_value_map.rb0000644000175000017500000000212612547750061016354 0ustar sbadiasbadiamodule Ref # Implementation of a map in which weak references are kept to the map values. # This allows the garbage collector to reclaim these objects if the # only reference to them is the weak reference in the map. # # This is often useful for cache implementations since the map can be allowed to grow # without bound and the garbage collector can be relied on to clean it up as necessary. # One must be careful, though, when accessing entries since the values can be collected # at any time until there is a strong reference to them. # # === Example usage: # # cache = Ref::WeakValueMap.new # foo = "foo" # cache["strong"] = foo # add a value with a strong reference # cache["weak"] = "bar" # add a value without a strong reference # cache["strong"] # "foo" # cache["weak"] # "bar" # ObjectSpace.garbage_collect # cache["strong"] # "foo" # cache["weak"] # nil # # See AbstractReferenceValueMap for details. class WeakValueMap < AbstractReferenceValueMap self.reference_class = WeakReference end end ref-2.0.0/lib/ref/reference_queue.rb0000644000175000017500000000473612547750061016547 0ustar sbadiasbadiamodule Ref # This class provides a simple thread safe container to hold a reference queue. Instances # of WeakReference can be added to the queue and as the objects pointed to by those references # are cleaned up by the garbage collector, the references will be added to the queue. # # The reason for using a reference queue is that it tends to be more efficient than adding # individual finalizers to objects and the cleanup code can be handled by a thread outside # of garbage collection. # # In general, you should create your own subclass of WeakReference that contains the logic # needed to complete the cleanup. The object pointed to will have already been cleaned up # and the reference cannot maintain a reference to the object. # # === Example usage: # # class MyRef < Ref::WeakReference # def cleanup # # Do something... # end # end # # queue = Ref::ReferenceQueue.new # ref = MyRef.new(Object.new) # queue.monitor(ref) # queue.shift # = nil # ObjectSpace.garbage_collect # r = queue.shift # = ref # r.cleanup class ReferenceQueue def initialize @queue = [] @references = {} @lock = Monitor.new @finalizer = lambda do |object_id| @lock.synchronize do ref = @references.delete(object_id) @queue.push(ref) if ref end end end # Monitor a reference. When the object the reference points to is garbage collected, # the reference will be added to the queue. def monitor(reference) obj = reference.object if obj @lock.synchronize do @references[reference.referenced_object_id] = reference end ObjectSpace.define_finalizer(obj, @finalizer) else push(reference) end end # Add a reference to the queue. def push(reference) if reference @lock.synchronize do @queue.push(reference) end end end # Pull the last reference off the queue. Returns +nil+ if their are no references. def pop @lock.synchronize do @queue.pop end end # Pull the next reference off the queue. Returns +nil+ if there are no references. def shift @lock.synchronize do @queue.shift end end # Return +true+ if the queue is empty. def empty? @queue.empty? end # Get the current size of the queue. def size @queue.size end end end ref-2.0.0/ext/0000755000175000017500000000000012547750061012324 5ustar sbadiasbadiaref-2.0.0/ext/java/0000755000175000017500000000000012547750061013245 5ustar sbadiasbadiaref-2.0.0/ext/java/org/0000755000175000017500000000000012547750061014034 5ustar sbadiasbadiaref-2.0.0/ext/java/org/jruby/0000755000175000017500000000000012547750061015167 5ustar sbadiasbadiaref-2.0.0/ext/java/org/jruby/ext/0000755000175000017500000000000012547750061015767 5ustar sbadiasbadiaref-2.0.0/ext/java/org/jruby/ext/ref/0000755000175000017500000000000012547750061016543 5ustar sbadiasbadiaref-2.0.0/ext/java/org/jruby/ext/ref/ReferencesService.java0000644000175000017500000000203212547750061023005 0ustar sbadiasbadiapackage org.jruby.ext.ref; import java.io.IOException; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyModule; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.load.BasicLibraryService; /** * This library adds native Java support for weak and soft references. * * @author Brian Durand */ public class ReferencesService implements BasicLibraryService { public boolean basicLoad(Ruby runtime) throws IOException { RubyModule refModule = runtime.getModule("Ref"); RubyClass referenceClass = refModule.getClass("Reference"); RubyClass rubyWeakReferenceClass = runtime.defineClassUnder("WeakReference", referenceClass, RubyWeakReference.ALLOCATOR, refModule); rubyWeakReferenceClass.defineAnnotatedMethods(RubyWeakReference.class); RubyClass rubySoftReferenceClass = runtime.defineClassUnder("SoftReference", referenceClass, RubySoftReference.ALLOCATOR, refModule); rubySoftReferenceClass.defineAnnotatedMethods(RubySoftReference.class); return true; } } ref-2.0.0/ext/java/org/jruby/ext/ref/RubySoftReference.java0000644000175000017500000000251612547750061023006 0ustar sbadiasbadiapackage org.jruby.ext.ref; import java.lang.ref.SoftReference; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyObject; import org.jruby.anno.JRubyMethod; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; public class RubySoftReference extends RubyObject { private SoftReference _ref; private static final String REFERENCED_OBJECT_ID_VARIABLE = "@referenced_object_id".intern(); public RubySoftReference(Ruby runtime, RubyClass klass) { super(runtime, klass); } public static final ObjectAllocator ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klass) { return new RubySoftReference(runtime, klass); } }; @JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE) public IRubyObject initialize(ThreadContext context, IRubyObject obj) { _ref = new SoftReference(obj); fastSetInstanceVariable(REFERENCED_OBJECT_ID_VARIABLE, obj.id()); return context.getRuntime().getNil(); } @JRubyMethod(name = "object") public IRubyObject object() { IRubyObject obj = (IRubyObject)_ref.get(); if (obj != null) { return obj; } else { return getRuntime().getNil(); } } } ref-2.0.0/ext/java/org/jruby/ext/ref/RubyWeakReference.java0000644000175000017500000000251612547750061022762 0ustar sbadiasbadiapackage org.jruby.ext.ref; import java.lang.ref.WeakReference; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyObject; import org.jruby.anno.JRubyMethod; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; public class RubyWeakReference extends RubyObject { private WeakReference _ref; private static final String REFERENCED_OBJECT_ID_VARIABLE = "@referenced_object_id".intern(); public RubyWeakReference(Ruby runtime, RubyClass klass) { super(runtime, klass); } public static final ObjectAllocator ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klass) { return new RubyWeakReference(runtime, klass); } }; @JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE) public IRubyObject initialize(ThreadContext context, IRubyObject obj) { _ref = new WeakReference(obj); fastSetInstanceVariable(REFERENCED_OBJECT_ID_VARIABLE, obj.id()); return context.getRuntime().getNil(); } @JRubyMethod(name = "object") public IRubyObject object() { IRubyObject obj = (IRubyObject)_ref.get(); if (obj != null) { return obj; } else { return getRuntime().getNil(); } } } ref-2.0.0/MIT_LICENSE0000644000175000017500000000204012547750061013236 0ustar sbadiasbadiaCopyright (c) 2013 Brian Durand Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ref-2.0.0/spec/0000755000175000017500000000000012547750061012456 5ustar sbadiasbadiaref-2.0.0/spec/shared/0000755000175000017500000000000012547750061013724 5ustar sbadiasbadiaref-2.0.0/spec/shared/reference_key_map_shared.rb0000644000175000017500000001326312547750061021247 0ustar sbadiasbadiarequire 'spec_helper' shared_examples 'a reference key map' do let(:value_1) { 'value 1' } let(:value_2) { 'value 2' } let(:value_3) { 'value 3' } let(:key_1) { Object.new } let(:key_2) { Object.new } let(:key_3) { Object.new } let(:hash) { map_class.new } context 'keeps entries with strong references' do specify do Ref::Mock.use do hash[key_1] = "value 1" hash[key_2] = "value 2" expect(hash[key_1]).to eq "value 1" expect(hash[key_2]).to eq "value 2" end end end context 'removes entries that have been garbage collected' do specify do Ref::Mock.use do hash[key_1] = value_1 hash[key_2] = value_2 expect(hash[key_1]).to eq(value_1) expect(hash[key_2]).to eq(value_2) Ref::Mock.gc(key_2) expect(hash[key_1]).to eq(value_1) expect(hash[key_2]).to be_nil end end end context 'can clear the map' do specify do Ref::Mock.use do hash[key_1] = value_1 hash[key_2] = value_2 hash.clear expect(hash[key_1]).to be_nil expect(hash[key_2]).to be_nil end end end context 'can delete entries' do specify do Ref::Mock.use do hash[key_1] = value_1 hash[key_2] = value_2 Ref::Mock.gc(key_2) expect(hash.delete(key_2)).to be_nil expect(hash.delete(key_1)).to eq(value_1) expect(hash[key_1]).to be_nil end end end context 'can merge in another hash' do specify do Ref::Mock.use do hash[key_1] = value_1 hash[key_2] = value_2 hash.merge!(key_3 => value_3) expect(hash[key_2]).to eq 'value 2' expect(hash[key_1]).to eq value_1 Ref::Mock.gc(key_2) expect(hash[key_2]).to be_nil expect(hash[key_1]).to eq value_1 expect(hash[key_3]).to eq value_3 end end end context 'can get all keys' do specify do Ref::Mock.use do hash[key_1] = value_1 hash[key_2] = value_2 hash[key_3] = value_3 expect([key_1, key_2, key_3] - hash.keys).to eq [] Ref::Mock.gc(key_2) expect([key_1, key_2, key_3] - hash.keys).to eq [key_2] end end end context 'can turn into an array' do specify do Ref::Mock.use do hash[key_1] = value_1 hash[key_2] = value_2 hash[key_3] = value_3 order = lambda{|a,b| a.last <=> b.last} expect([[key_1, "value 1"], [key_2, "value 2"], [key_3, "value 3"]].sort(&order)).to eq hash.to_a.sort(&order) Ref::Mock.gc(key_2) expect([[key_1, "value 1"], [key_3, "value 3"]].sort(&order)).to eq hash.to_a.sort(&order) end end end context 'can turn into a hash' do specify do Ref::Mock.use do hash[key_1] = value_1 hash[key_2] = value_2 hash[key_3] = value_3 order = lambda{|a,b| a.last <=> b.last} expect({key_1 => "value 1", key_2 => "value 2", key_3 => "value 3"}.sort(&order)).to eq hash.to_h.sort(&order) Ref::Mock.gc(key_2) expect({key_1 => "value 1", key_3 => "value 3"}.sort(&order)).to eq hash.to_h.sort(&order) end end end context 'can interate over all entries' do specify do Ref::Mock.use do hash[key_1] = value_1 hash[key_2] = value_2 hash[key_3] = value_3 keys = [] values = [] hash.each{|k,v| keys << k; values << v} expect([key_1, key_2, key_3] - keys).to eq [] expect(["value 1", "value 2", "value 3"]).to eq values.sort Ref::Mock.gc(key_2) keys = [] values = [] hash.each{|k,v| keys << k; values << v} expect([key_1, key_2, key_3] - keys).to eq [key_2] expect(["value 1", "value 3"]).to eq values.sort end end end context 'size' do specify do Ref::Mock.use do hash = map_class.new expect(hash.empty?).to eq true expect(hash.size).to eq 0 key_1 = Object.new key_2 = Object.new hash[key_1] = "value 1" hash[key_2] = "value 2" expect(hash.size).to eq 2 Ref::Mock.gc(key_2) expect(hash.empty?).to eq false expect(hash.size).to eq 1 end end end context 'inspect' do specify do Ref::Mock.use do hash[Object.new] = "value 1" expect(hash.inspect).to_not be_nil end end end context '#merge' do let(:new_value){ Object.new } let(:values){ {key_1 => value_1, key_2 => value_2, key_3 => value_3} } let(:this) do values.each{|k, v| hash[k] = v } hash end let(:other){ {key_3 => new_value} } it 'updates all members with the new values from a given hash' do expect(this.merge(other)[key_3]).to eq new_value end it 'calls the given block for each key in `other`' do actual = 0 this.merge(key_2 => 'yes', key_3 => 'no'){|member, thisval, otherval| actual += 1; Object.new } expect(actual).to eq 2 end it 'retains the value for all members without values in the given hash' do expect(this.merge(other)[key_1]).to eq value_1 end it 'adds members not in the original hash' do new_key = Object.new new_value = 'life, the universe, and everything' expect(this.merge(new_key => new_value)[new_key]).to eq new_value end it 'returns a deep copy when merging an empty hash' do other = this.merge({}) values.each do |key, value| expect(other[key]).to eq value end end it 'returns a new object' do expect(this.merge(other).object_id).to_not eq this.object_id expect(this.merge(other).object_id).to_not eq other.object_id end end end ref-2.0.0/spec/shared/mock.rb0000644000175000017500000001052512547750061015205 0ustar sbadiasbadiamodule Ref # This module provides mock weak and strong references that are designed to be # used in tests. You can define a block where all weak and soft references created # will be mock references. You can then mimic running the garbage collector on # the objects pointed to by the references. # # Example usage: # # Ref::Mock.use do # obj = Object.new # ref = Ref::WeakReference.new(obj) # ref.object # obj # Ref::Mock.gc(obj) # mimics the garbage collector reclaiming the referenced object # ref.object # nil # end module Mock class << self # Use the mock implementation inside a block and then restore the original implementation. def use if object_space yield else setup begin yield ensure cleanup end end end # Start using mock references. def setup raise "Ref::Mock already setup" if object_space @object_space = {} class << ObjectSpace unless method_defined?(:define_finalizer_with_mock_reference) def define_finalizer_with_mock_reference(obj, finalizer) if ::Ref::Mock.object_space.include?(obj.__id__) ::Ref::Mock.object_space[obj.__id__] << finalizer else define_finalizer_without_mock_reference(obj, finalizer) end end end alias_method :define_finalizer_without_mock_reference, :define_finalizer alias_method :define_finalizer, :define_finalizer_with_mock_reference end class << WeakReference unless method_defined?(:new_with_mock_reference) def new_with_mock_reference(obj) if self == Mock::MockWeakReference new_without_mock_reference(obj) else Mock::MockWeakReference.new(obj) end end end alias_method :new_without_mock_reference, :new alias_method :new, :new_with_mock_reference end class << SoftReference unless method_defined?(:new_with_mock_reference) def new_with_mock_reference(obj) if self == Mock::MockSoftReference new_without_mock_reference(obj) else Mock::MockSoftReference.new(obj) end end end alias_method :new_without_mock_reference, :new alias_method :new, :new_with_mock_reference end end # Stop using mock references. def cleanup @object_space = nil class << ObjectSpace alias_method :define_finalizer_with_mock_reference, :define_finalizer alias_method :define_finalizer, :define_finalizer_without_mock_reference end class << WeakReference alias_method :new_with_mock_reference, :new alias_method :new, :new_without_mock_reference end class << SoftReference alias_method :new_with_mock_reference, :new alias_method :new, :new_without_mock_reference end end def object_space # :nodoc: @object_space if instance_variable_defined?(:@object_space) end # Simulate garbage collection of the objects passed in as arguments. If no objects # are specified, all objects will be reclaimed. def gc(*objects) objects = if objects.empty? object_space.keys else objects.map { |obj| obj.__id__ } end objects.each do |id| finalizers = object_space.delete(id) if finalizers finalizers.each{|finalizer| finalizer.call(id)} end end end end module MockReference #:nodoc: def initialize(obj) @object = obj @referenced_object_id = obj.__id__ raise "Reference::Mock not setup yet" unless Mock.object_space Mock.object_space[obj.__id__] ||= [] end def object if @object && Mock.object_space.include?(@object.__id__) @object else @object = nil end end end class MockWeakReference < WeakReference #:nodoc: include MockReference end class MockSoftReference < SoftReference #:nodoc: include MockReference end end end ref-2.0.0/spec/shared/reference_value_map_shared.rb0000644000175000017500000001337212547750061021574 0ustar sbadiasbadiarequire 'spec_helper' shared_examples 'a reference value map' do let(:key_1) { 'key 1' } let(:key_2) { 'key 2' } let(:key_3) { 'key 3' } let(:value_1) { 'value 1' } let(:value_2) { 'value 2' } let(:value_3) { 'value 3' } let(:hash) { map_class.new } context 'keeps entries with strong references' do specify do Ref::Mock.use do hash['key 1'] = value_1 hash['key 2'] = value_2 expect(hash['key 1'] ).to eq(value_1) expect(hash['key 2'] ).to eq(value_2) end end end context 'removes entries that have been garbage collected' do specify do Ref::Mock.use do hash['key 1'] = value_1 hash['key 2'] = value_2 expect(hash['key 1']).to eq(value_1) expect(hash['key 2']).to eq(value_2) Ref::Mock.gc(value_2) expect(value_1).to eq(value_1) expect(hash['key 2']).to be_nil end end end context 'can clear the map' do specify do hash['key 1'] = value_1 hash['key 2'] = value_2 hash.clear expect(hash['key 1']).to be_nil expect(hash['key 2']).to be_nil end end context 'can delete entries' do specify do Ref::Mock.use do hash["key 1"] = value_1 hash["key 2"] = value_2 Ref::Mock.gc(value_2) expect(hash.delete("key 2")).to be_nil expect(hash.delete("key 1")).to eq value_1 expect(hash["key 1"]).to be_nil end end end context 'can merge in another hash' do specify do Ref::Mock.use do hash["key 1"] = value_1 hash["key 2"] = value_2 hash.merge!("key 3" => value_3) expect('value 2').to eq hash['key 2'] expect(hash['key 1']).to eq value_1 Ref::Mock.gc(value_2) expect(hash['key 2']).to be_nil expect(hash['key 1']).to eq value_1 expect(hash['key 3']).to eq value_3 end end end context 'can get all values' do specify do Ref::Mock.use do hash["key 1"] = value_1 hash["key 2"] = value_2 hash["key 3"] = value_3 expect(["value 1", "value 2", "value 3"].sort).to eq hash.values.sort Ref::Mock.gc(value_2) expect(["value 1", "value 3"].sort).to eq hash.values.sort end end end context 'can turn into an array' do specify do Ref::Mock.use do hash["key 1"] = value_1 hash["key 2"] = value_2 hash["key 3"] = value_3 order = lambda{|a,b| a.first <=> b.first} expect([["key 1", "value 1"], ["key 2", "value 2"], ["key 3", "value 3"]].sort(&order)).to eq hash.to_a.sort(&order) Ref::Mock.gc(value_2) expect([["key 1", "value 1"], ["key 3", "value 3"]].sort(&order)).to eq hash.to_a.sort(&order) end end end context 'can turn into a hash' do specify do Ref::Mock.use do hash["key 1"] = value_1 hash["key 2"] = value_2 hash["key 3"] = value_3 order = lambda{|a,b| a.first <=> b.first} expect({"key 1" => "value 1", "key 2" => "value 2", "key 3" => "value 3"}.sort(&order)).to eq hash.to_a.sort(&order) Ref::Mock.gc(value_2) expect({"key 1" => "value 1", "key 3" => "value 3"}.sort(&order)).to eq hash.to_a.sort(&order) end end end context 'can interate over all entries' do specify do Ref::Mock.use do hash["key 1"] = value_1 hash["key 2"] = value_2 hash["key 3"] = value_3 keys = [] values = [] hash.each{|k,v| keys << k; values << v} expect(["key 1", "key 2", "key 3"]).to eq keys.sort expect(["value 1", "value 2", "value 3"]).to eq values.sort Ref::Mock.gc(value_2) keys = [] values = [] hash.each{|k,v| keys << k; values << v} expect(["key 1", "key 3"]).to eq keys.sort expect(["value 1", "value 3"]).to eq values.sort end end end context 'size' do specify do Ref::Mock.use do hash = map_class.new expect(hash.empty?).to eq true expect(hash.size).to eq 0 value_1 = "value 1" value_2 = "value 2" hash["key 1"] = value_1 hash["key 2"] = value_2 expect(hash.size).to eq 2 Ref::Mock.gc(value_2) expect(hash.empty?).to eq false expect(hash.size).to eq 1 end end end context 'inspect' do specify do Ref::Mock.use do hash["key 1"] = "value 1" expect(hash.inspect).to_not be_nil end end end context '#merge' do let(:new_value){ Object.new } let(:values){ {key_1 => value_1, key_2 => value_2, key_3 => value_3} } let(:this) do values.each{|k, v| hash[k] = v } hash end let(:other){ {key_3 => new_value} } it 'updates all members with the new values from a given hash' do expect(this.merge(other)[key_3]).to eq new_value end it 'calls the given block for each key in `other`' do actual = 0 this.merge(key_2 => 'yes', key_3 => 'no'){|member, thisval, otherval| actual += 1; Object.new } expect(actual).to eq 2 end it 'retains the value for all members without values in the given hash' do expect(this.merge(other)[key_1]).to eq value_1 end it 'adds members not in the original hash' do new_key = Object.new new_value = 'life, the universe, and everything' expect(this.merge(new_key => new_value)[new_key]).to eq new_value end it 'returns a deep copy when merging an empty hash' do other = this.merge({}) values.each do |key, value| expect(other[key]).to eq value end end it 'returns a new object' do expect(this.merge(other).object_id).to_not eq this.object_id expect(this.merge(other).object_id).to_not eq other.object_id end end end ref-2.0.0/spec/ref/0000755000175000017500000000000012547750061013232 5ustar sbadiasbadiaref-2.0.0/spec/ref/soft_reference_spec.rb0000644000175000017500000000406112547750061017563 0ustar sbadiasbadiarequire 'spec_helper' describe Ref::SoftReference do describe '#object' do context 'when has not garbage collected objects' do it 'gets the object' do obj = Object.new ref = Ref::SoftReference.new(obj) expect(obj).to eq ref.object expect(obj.object_id).to eq ref.referenced_object_id end end context 'when has a lot of objects' do # Since we can't reliably control the garbage collector, this is a brute force test. # It might not always fail if the garbage collector and memory allocator don't # cooperate, but it should fail often enough on continuous integration to # hilite any problems. Set the environment variable QUICK_TEST to "true" if you # want to make the tests run quickly. it 'get the correct object' do id_to_ref = {} (ENV["QUICK_TEST"] == "true" ? 1000 : 100000).times do |i| obj = Object.new if id_to_ref.key?(obj.object_id) ref = id_to_ref[obj.object_id] if ref.object fail "soft reference found with a live reference to an object that was not the one it was created with" break end end %w(Here are a bunch of objects that are allocated and can then be cleaned up by the garbage collector) id_to_ref[obj.object_id] = Ref::SoftReference.new(obj) if i % 1000 == 0 GC.start sleep(0.01) end end end end context 'when references are not collected immediately' do it "the object can't be nil" do ref = Ref::SoftReference.new(Object.new) 9.times{ arr = %w(allocate some memory on the heap); arr *= 100; GC.start } expect(ref.object).to_not be_nil end end end describe '#inspect' do context 'when GC is called' do it 'inspects not be nil' do ref = Ref::SoftReference.new(Object.new) expect(ref.inspect).to_not be_nil GC.start GC.start expect(ref.inspect).to_not be_nil end end end end ref-2.0.0/spec/ref/soft_value_map_spec.rb0000644000175000017500000000031012547750061017567 0ustar sbadiasbadiarequire 'spec_helper' describe Ref::SoftValueMap do it_behaves_like 'a reference value map' do let(:map_class) { Ref::SoftValueMap } let(:reference_class) { Ref::SoftReference } end end ref-2.0.0/spec/ref/weak_reference_spec.rb0000644000175000017500000000364212547750061017543 0ustar sbadiasbadiarequire 'spec_helper' describe Ref::WeakReference do describe '#object' do context 'when has not garbage collected objects' do it 'gets the object' do obj = Object.new ref_1 = Ref::WeakReference.new(obj) ref_2 = Ref::WeakReference.new(obj) expect(obj).to eq ref_1.object expect(obj.object_id).to eq ref_1.referenced_object_id expect(obj).to eq ref_2.object expect(obj.object_id).to eq ref_2.referenced_object_id end end context 'when has a lot of objects' do # Since we can't reliably control the garbage collector, this is a brute force test. # It might not always fail if the garbage collector and memory allocator don't # cooperate, but it should fail often enough on continuous integration to # hilite any problems. Set the environment variable QUICK_TEST to "true" if you # want to make the tests run quickly. it 'get the correct object' do id_to_ref = {} (ENV["QUICK_TEST"] == "true" ? 1000 : 100000).times do |i| obj = Object.new if id_to_ref.key?(obj.object_id) ref = id_to_ref[obj.object_id] if ref.object fail "weak reference found with a live reference to an object that was not the one it was created with" break end end %w(Here are a bunch of objects that are allocated and can then be cleaned up by the garbage collector) id_to_ref[obj.object_id] = Ref::WeakReference.new(obj) if i % 1000 == 0 GC.start sleep(0.01) end end end end end describe '#inspect' do context 'when GC is called' do it 'inspects not be nil' do ref = Ref::SoftReference.new(Object.new) expect(ref.inspect).to_not be_nil GC.start GC.start expect(ref.inspect).to_not be_nil end end end end ref-2.0.0/spec/ref/reference_queue_spec.rb0000644000175000017500000000357112547750061017741 0ustar sbadiasbadiarequire 'spec_helper' describe Ref::ReferenceQueue do let(:queue) { Ref::ReferenceQueue.new } let(:obj_1) { Object.new } let(:obj_2) { Object.new } let(:ref_1) { Ref::WeakReference.new(obj_1) } let(:ref_2) { Ref::WeakReference.new(obj_2) } describe '#push' do context 'when the queue is empty' do it { expect(queue.size).to be(0) } it { expect(queue).to be_empty } end context 'when the queue is not empty' do before do queue.push(ref_1) queue.push(ref_2) end it { expect(queue.size).to be(2) } it { expect(queue).to_not be_empty } end end describe '#shift' do context 'when the queue is not empty' do before do queue.push(ref_1) queue.push(ref_2) end it { expect(queue.shift).to be(ref_1) } end context 'when the queue is empty' do it { expect(queue.shift).to be_nil } end end describe '#pop' do context 'when the queue is not empty' do before do queue.push(ref_1) queue.push(ref_2) end it { expect(queue.pop).to be(ref_2) } end context 'when the queue is empty' do it { expect(queue.pop).to be_nil } end end context 'references are added immediately if the_object has been collected' do specify do Ref::Mock.use do ref = Ref::WeakReference.new(obj_1) Ref::Mock.gc(obj_1) queue.monitor(ref) expect(ref).to equal(queue.shift) end end end context 'references are added when the object has been collected' do specify do Ref::Mock.use do ref = Ref::WeakReference.new(obj_1) queue.monitor(ref) result = queue.shift expect(result).to eq nil Ref::Mock.gc(obj_1) object = queue.shift expect(ref.referenced_object_id).to eq object.referenced_object_id end end end end ref-2.0.0/spec/ref/mock_spec.rb0000644000175000017500000000136012547750061015522 0ustar sbadiasbadiarequire 'spec_helper' describe Ref::Mock do context 'gc with argument' do specify do Ref::Mock.use do obj_1 = Object.new obj_2 = Object.new ref_1 = Ref::WeakReference.new(obj_1) ref_2 = Ref::WeakReference.new(obj_2) Ref::Mock.gc(obj_1) expect(ref_1.object).to be_nil expect(ref_2.object).to equal(obj_2) end end end context 'gc with no argument' do specify do Ref::Mock.use do obj_1 = Object.new obj_2 = Object.new ref_1 = Ref::WeakReference.new(obj_1) ref_2 = Ref::WeakReference.new(obj_2) Ref::Mock.gc expect(ref_1.object).to be_nil expect(ref_2.object).to be_nil end end end end ref-2.0.0/spec/ref/strong_reference_spec.rb0000644000175000017500000000054112547750061020123 0ustar sbadiasbadiarequire 'spec_helper' describe Ref::StrongReference do let(:obj) { Object.new } let(:ref) { Ref::StrongReference.new(obj) } context '#object' do it { expect(obj).to equal(ref.object) } it { expect(obj.object_id).to equal(ref.referenced_object_id) } end context '#inspect' do it { expect(ref.inspect).to_not be_empty } end end ref-2.0.0/spec/ref/weak_value_map_spec.rb0000644000175000017500000000031012547750061017543 0ustar sbadiasbadiarequire 'spec_helper' describe Ref::WeakValueMap do it_behaves_like 'a reference value map' do let(:map_class) { Ref::WeakValueMap } let(:reference_class) { Ref::WeakReference } end end ref-2.0.0/spec/ref/soft_key_map_spec.rb0000644000175000017500000000030212547750061017244 0ustar sbadiasbadiarequire 'spec_helper' describe Ref::SoftKeyMap do it_behaves_like 'a reference key map' do let(:map_class) { Ref::SoftKeyMap } let(:reference_class) { Ref::SoftReference } end end ref-2.0.0/spec/ref/weak_key_map_spec.rb0000644000175000017500000000030212547750061017220 0ustar sbadiasbadiarequire 'spec_helper' describe Ref::WeakKeyMap do it_behaves_like 'a reference key map' do let(:map_class) { Ref::WeakKeyMap } let(:reference_class) { Ref::WeakReference } end end ref-2.0.0/spec/spec_helper.rb0000644000175000017500000000120112547750061015266 0ustar sbadiasbadiarequire 'simplecov' require 'coveralls' SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter ] SimpleCov.start do project_name 'ref' add_filter '/coverage/' add_filter '/doc/' add_filter '/pkg/' add_filter '/spec/' add_filter '/tasks/' end require 'ref' # import all the support files Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |f| require File.expand_path(f) } Dir[File.join(File.dirname(__FILE__), 'shared/**/*.rb')].each { |f| require File.expand_path(f) } RSpec.configure do |config| config.order = 'random' end ref-2.0.0/Gemfile0000644000175000017500000000072312547750061013021 0ustar sbadiasbadiasource 'https://rubygems.org' gemspec group :development do gem 'rake', '~> 10.3.2' gem 'rake-compiler', '0.9.5' end group :testing do gem 'test-unit', '~> 3.0.9' gem 'rspec', '~> 3.1.0' gem 'coveralls', '~> 0.7.3', :require => false end group :documentation do gem 'yard', '~> 0.8.7.4', :require => false gem 'inch', '~> 0.4.6', :platforms => :mri, :require => false gem 'redcarpet', '~> 3.1.2', platforms: :mri # understands github markdown end