Skip to content

Commit

Permalink
Add WeakRef class (#3276)
Browse files Browse the repository at this point in the history
Weak references are implemented by boehm's register_disappearing_link
bcardiff authored Sep 8, 2016

Verified

This commit was signed with the committer’s verified signature.
1 parent fbb5b16 commit 7cd0fa6
Showing 3 changed files with 100 additions and 0 deletions.
73 changes: 73 additions & 0 deletions spec/std/weak_ref_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
require "spec"
require "weak_ref"

module WeakRefSpec
class State
@@count = {} of Symbol => Int64

def self.inc(key)
@@count[key] = @@count.fetch(key, 0i64) + 1
end

def self.count(key)
@@count.fetch(key, 0i64)
end

def self.reset
@@count.clear
end
end

class Foo
def initialize(@key : Symbol)
end

def finalize
State.inc @key
end
end
end

describe WeakRef do
it "should get dereference object" do
foo = WeakRefSpec::Foo.new :foo
ref = WeakRef.new(foo)
ref.should_not be_nil
ref.target.should be(foo)
end

it "WeakRefSpec::State counts released objects" do
WeakRefSpec::State.reset
WeakRefSpec::State.count(:foo).should eq 0
10.times do
WeakRefSpec::Foo.new(:foo)
end
GC.collect
WeakRefSpec::State.count(:foo).should be > 0
end

it "Referenced object should not be released" do
WeakRefSpec::State.reset
instances = [] of WeakRefSpec::Foo
WeakRefSpec::State.count(:strong_foo_ref).should eq 0
10.times do
instances << WeakRefSpec::Foo.new(:strong_foo_ref)
end
GC.collect
WeakRefSpec::State.count(:strong_foo_ref).should eq 0
end

it "Weak referenced object should be released if no other reference" do
WeakRefSpec::State.reset
instances = [] of WeakRef(WeakRefSpec::Foo)
last = nil
10.times do
last = WeakRefSpec::Foo.new(:weak_foo_ref)
instances << WeakRef.new(last)
end
GC.collect
WeakRefSpec::State.count(:weak_foo_ref).should be > 0
instances.select { |wr| wr.target.nil? }.size.should be > 0
instances[-1].target.should_not be_nil
end
end
8 changes: 8 additions & 0 deletions src/gc/boehm.cr
Original file line number Diff line number Diff line change
@@ -22,6 +22,9 @@ lib LibGC
fun disable = GC_disable
fun set_handle_fork = GC_set_handle_fork(value : Int)

fun base = GC_base(displaced_pointer : Void*) : Void*
fun general_register_disappearing_link = GC_general_register_disappearing_link(link : Void**, obj : Void*) : Int

type Finalizer = Void*, Void* ->
fun register_finalizer = GC_register_finalizer(obj : Void*, fn : Finalizer, cd : Void*, ofn : Finalizer*, ocd : Void**)
fun register_finalizer_ignore_self = GC_register_finalizer_ignore_self(obj : Void*, fn : Finalizer, cd : Void*, ofn : Finalizer*, ocd : Void**)
@@ -110,6 +113,11 @@ module GC
roots << Pointer(Void).new(object.object_id)
end

def self.register_disappearing_link(pointer : Void**)
base = LibGC.base(pointer.value)
LibGC.general_register_disappearing_link(pointer, base)
end

record Stats,
collections : LibC::ULong,
bytes_found : LibC::Long
19 changes: 19 additions & 0 deletions src/weak_ref.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Weak Reference class that allows a referenced object to be garbage-collected.
#
class WeakRef(T)
@target : Void*

def initialize(target : T)
@target = target.as(Void*)
GC.register_disappearing_link(pointerof(@target))
end

def self.allocate
GC.malloc_atomic(sizeof(self)).as(self)
end

# Returns the referenced object or `Nil` if it has been garbage-collected.
def target
@target.as(T?)
end
end

0 comments on commit 7cd0fa6

Please sign in to comment.