Skip to content

Commit ccdc1fa

Browse files
committed
New finalizer specs
1 parent 3544d33 commit ccdc1fa

File tree

7 files changed

+80
-36
lines changed

7 files changed

+80
-36
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
slow:ObjectSpace.define_finalizer will call the finalizer
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
fails:ObjectSpace.undefine_finalizer if not used leaves the finalizer in place
2+
slow:ObjectSpace.undefine_finalizer successfully unregisters a finalizer
Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
1-
# Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. This
1+
# Copyright (c) 2017, 2019 Oracle and/or its affiliates. All rights reserved. This
22
# code is released under a tri EPL/GPL/LGPL license. You can use it,
33
# redistribute it and/or modify it under the terms of the:
44
#
55
# Eclipse Public License version 1.0, or
66
# GNU General Public License version 2, or
77
# GNU Lesser General Public License version 2.1.
88

9+
require 'objspace'
10+
911
require_relative '../../ruby/spec_helper'
1012

1113
describe "ObjectSpace.define_finalizer" do
1214

13-
it "will eventually call the finalizer with enough GC activity" do
14-
do_break = false
15-
until do_break
16-
expected_id = nil
17-
ObjectSpace.define_finalizer Object.new.tap { |object| expected_id = object.object_id }, proc { |finalized_id|
18-
finalized_id.should == expected_id
19-
do_break = true
20-
}
15+
# These specs require that the GC runs a full stop-the-world collection that
16+
# will push references on Runtime#gc(). All GCs on HotSpot and the SVM do
17+
# this, apart from the Epsilon collector on HotSpot. The remaining issue is
18+
# that the references are popped asynchronously at the JVM level, and then
19+
# from there popped again asynchronously in TruffleRuby, so we do still need
20+
# to wait with a timeout in order to assert that it has been done.
21+
# Truffle::Debug.drain_finalization_queue, which drains the queue in
22+
# TruffleRuby in the foreground, does seem to help increase test throughput.
23+
24+
it "will call the finalizer" do
25+
channel = Truffle::Channel.new
26+
finalizer = proc {
27+
channel.send :finalized
28+
}
29+
Object.new.tap do |object|
30+
ObjectSpace.define_finalizer object, finalizer
31+
ObjectSpace.reachable_objects_from(object).should include(finalizer)
2132
end
33+
GC.start
34+
Truffle::Debug.drain_finalization_queue # Not needed for correctness
35+
channel.receive_timeout(3).should == :finalized
2236
end
2337

2438
end

spec/truffle/objspace/reachable_objects_from_spec.rb

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
# Copyright (c) 2015, 2017 Oracle and/or its affiliates. All rights reserved. This
1+
# Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved. This
22
# code is released under a tri EPL/GPL/LGPL license. You can use it,
33
# redistribute it and/or modify it under the terms of the:
44
#
55
# Eclipse Public License version 1.0, or
66
# GNU General Public License version 2, or
77
# GNU Lesser General Public License version 2.1.
88

9-
require_relative '../../ruby/spec_helper'
109
require 'objspace'
1110

12-
# Truffle-specific behavior
11+
require_relative '../../ruby/spec_helper'
12+
1313
describe "ObjectSpace.reachable_objects_from" do
14+
15+
# This is a standard method, but our implementation returns more objects so we keep this spec here
16+
1417
it "enumerates objects directly reachable from a given object" do
1518
ObjectSpace.reachable_objects_from(nil).should == [NilClass]
1619
ObjectSpace.reachable_objects_from(['a', 'b', 'c']).should include(Array, 'a', 'b', 'c')
@@ -62,4 +65,13 @@
6265
reachable = ObjectSpace.reachable_objects_from(q)
6366
reachable.should include(o)
6467
end
68+
69+
it "finds finalizers" do
70+
object = Object.new
71+
finalizer = proc { }
72+
ObjectSpace.define_finalizer object, finalizer
73+
reachable = ObjectSpace.reachable_objects_from(object)
74+
reachable.should include(finalizer)
75+
end
76+
6577
end
Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,33 @@
1-
# Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. This
1+
# Copyright (c) 2017, 2019 Oracle and/or its affiliates. All rights reserved. This
22
# code is released under a tri EPL/GPL/LGPL license. You can use it,
33
# redistribute it and/or modify it under the terms of the:
44
#
55
# Eclipse Public License version 1.0, or
66
# GNU General Public License version 2, or
77
# GNU Lesser General Public License version 2.1.
88

9+
require 'objspace'
10+
911
require_relative '../../ruby/spec_helper'
1012

1113
describe "ObjectSpace.undefine_finalizer" do
1214

13-
it "if not used leaves the finalizer in place" do
14-
finalized = 0
15-
finalizer = proc {
16-
finalized += 1
17-
}
18-
started = Time.now
19-
until Time.now > started + 3
20-
object = Object.new
21-
ObjectSpace.define_finalizer object, finalizer
22-
end
23-
finalized.should > 0
24-
end
15+
# See comment in define_finalizer_spec.rb
2516

2617
it "successfully unregisters a finalizer" do
27-
finalized = 0
28-
finalizer = proc {
29-
finalized += 1
30-
}
31-
started = Time.now
32-
until Time.now > started + 3
33-
object = Object.new
18+
channel = Truffle::Channel.new
19+
Object.new.tap do |object|
20+
finalizer = proc {
21+
channel.send :finalized
22+
}
3423
ObjectSpace.define_finalizer object, finalizer
24+
ObjectSpace.reachable_objects_from(object).should include(finalizer)
3525
ObjectSpace.undefine_finalizer object
26+
ObjectSpace.reachable_objects_from(object).should_not include(finalizer)
3627
end
37-
finalized.should == 0
28+
GC.start
29+
Truffle::Debug.drain_finalization_queue # Not needed for correctness
30+
channel.try_receive.should be_nil
3831
end
3932

4033
end

src/main/java/org/truffleruby/core/FinalizationService.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved. This
2+
* Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved. This
33
* code is released under a tri EPL/GPL/LGPL license. You can use it,
44
* redistribute it and/or modify it under the terms of the:
55
*
@@ -14,10 +14,12 @@
1414
import java.util.Collection;
1515
import java.util.Deque;
1616
import java.util.LinkedList;
17+
import java.util.Set;
1718

1819
import org.truffleruby.RubyContext;
1920

2021
import com.oracle.truffle.api.object.DynamicObject;
22+
import org.truffleruby.language.objects.ObjectGraphNode;
2123

2224
/**
2325
* Finalizers are implemented with phantom references and reference queues, and are run in a
@@ -50,7 +52,7 @@ public DynamicObject getRoot() {
5052
}
5153
}
5254

53-
public static class FinalizerReference extends PhantomReference<Object> implements ReferenceProcessingService.ProcessingReference<FinalizerReference> {
55+
public static class FinalizerReference extends PhantomReference<Object> implements ReferenceProcessingService.ProcessingReference<FinalizerReference>, ObjectGraphNode {
5456

5557
/**
5658
* All accesses to this Deque must be synchronized by taking the
@@ -115,6 +117,11 @@ private void collectRoots(Collection<DynamicObject> roots) {
115117
public ReferenceProcessingService<FinalizerReference> service() {
116118
return service;
117119
}
120+
121+
@Override
122+
public void getAdjacentObjects(Set<DynamicObject> reachable) {
123+
collectRoots(reachable);
124+
}
118125
}
119126

120127
/** The finalizer Ruby thread, spawned lazily. */
@@ -135,6 +142,10 @@ public synchronized FinalizerReference addFinalizer(Object object, FinalizerRefe
135142
return finalizerReference;
136143
}
137144

145+
public final void drainFinalizationQueue() {
146+
referenceProcessor.drainReferenceQueue();
147+
}
148+
138149
@Override
139150
protected void processReference(ProcessingReference<?> finalizerReference) {
140151
super.processReference(finalizerReference);

src/main/java/org/truffleruby/debug/TruffleDebugNodes.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved. This
2+
* Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved. This
33
* code is released under a tri EPL/GPL/LGPL license. You can use it,
44
* redistribute it and/or modify it under the terms of the:
55
*
@@ -962,4 +962,16 @@ protected ReadObjectFieldNode createReadAssociatedNode() {
962962

963963
}
964964

965+
@CoreMethod(names = "drain_finalization_queue", onSingleton = true)
966+
public abstract static class DrainFinalizationQueueNode extends CoreMethodArrayArgumentsNode {
967+
968+
@TruffleBoundary
969+
@Specialization
970+
public DynamicObject drainFinalizationQueue() {
971+
getContext().getFinalizationService().drainFinalizationQueue();
972+
return nil();
973+
}
974+
975+
}
976+
965977
}

0 commit comments

Comments
 (0)