Skip to content

Commit 5b563d4

Browse files
Can no longer invoke Managed_Resource.with when Managed_Resource.finalize was called
1 parent c493ec0 commit 5b563d4

File tree

5 files changed

+102
-11
lines changed

5 files changed

+102
-11
lines changed

distribution/lib/Standard/Base/0.0.0-dev/src/Runtime/Managed_Resource.enso

+5-1
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,15 @@ type Managed_Resource
5858
ADVANCED
5959

6060
Executes the provided action on the resource managed by the managed
61-
resource object.
61+
resource object. The action is only invoked if the managed resource
62+
has not yet been finalized.
6263

6364
Arguments:
6465
- action: The action that will be applied to the resource managed by
6566
resource.
67+
Returns:
68+
Value returned from the `action` or `Nothing` if the managed resource
69+
was already finalized
6670
with : (Any -> Any) -> Any
6771
with self ~action = @Builtin_Method "Managed_Resource.with"
6872

engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/WithNode.java

+11-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import org.enso.interpreter.dsl.BuiltinMethod;
77
import org.enso.interpreter.node.callable.InvokeCallableNode;
88
import org.enso.interpreter.runtime.EnsoContext;
9-
import org.enso.interpreter.runtime.ResourceManager;
109
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
1110
import org.enso.interpreter.runtime.data.ManagedResource;
1211
import org.enso.interpreter.runtime.state.State;
@@ -33,12 +32,17 @@ static WithNode build() {
3332

3433
@Specialization
3534
Object doWith(State state, VirtualFrame frame, ManagedResource self, Object action) {
36-
ResourceManager resourceManager = EnsoContext.get(this).getResourceManager();
37-
resourceManager.park(self);
38-
try {
39-
return invokeCallableNode.execute(action, frame, state, new Object[] {self.getResource()});
40-
} finally {
41-
resourceManager.unpark(self);
35+
var ctx = EnsoContext.get(this);
36+
var resourceManager = ctx.getResourceManager();
37+
if (self.getPhantomReference().refersTo(self)) {
38+
resourceManager.park(self);
39+
try {
40+
return invokeCallableNode.execute(action, frame, state, new Object[] {self.getResource()});
41+
} finally {
42+
resourceManager.unpark(self);
43+
}
44+
} else {
45+
return ctx.getBuiltins().nothing();
4246
}
4347
}
4448
}

engine/runtime/src/main/java/org/enso/interpreter/runtime/ResourceManager.java

+1
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ private Item(
408408
@CompilerDirectives.TruffleBoundary
409409
private void finalizeNow(EnsoContext context) {
410410
try {
411+
clear();
411412
InteropLibrary.getUncached(finalizer).execute(finalizer, underlying);
412413
} catch (Exception e) {
413414
context.getErr().println("Exception in finalizer: " + e.getMessage());

engine/runtime/src/main/java/org/enso/interpreter/runtime/data/ManagedResource.java

+32-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.enso.interpreter.runtime.data;
22

3+
import com.oracle.truffle.api.CompilerDirectives;
34
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
45
import com.oracle.truffle.api.dsl.Bind;
56
import com.oracle.truffle.api.interop.InteropLibrary;
@@ -12,7 +13,27 @@
1213
import org.enso.interpreter.runtime.callable.function.Function;
1314
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
1415

15-
/** A runtime representation of a managed resource. */
16+
/**
17+
* An Enso runtime representation of a managed resource.
18+
*
19+
* <p><b>Instance of this class</b> is convoluted with instances playing various roles:
20+
*
21+
* <ul>
22+
* <li>this {@link ManagedResource} points to {@code resource}
23+
* <li>this {@link ManagedResource} points to {@link PhantomReference} that is "phantom
24+
* referencing" {@code this}
25+
* <li>the implementation of {@link PhantomReference} is {@code Item} in {@link ResourceManager}
26+
* <li>the {@code Item} <em>"phantom referencing"</em> {@code this} {@link ManagedResource} is
27+
* stored in {@link ResourceManager} {@code pendingItems} collection.
28+
* <li>the {@code Item} has a pointer to the {@code resource} as well
29+
* <li>the {@code Item} has a pointer to the {@code finalizer} function
30+
* </ul>
31+
*
32+
* Once all this braided chunk of objects is eligible for GC because <b>nobody holds pointer to
33+
* {@link ManagedResource}</b>, the {@code Item} is put into {@link ResourceManager} {@code
34+
* referenceQueue} and process by the intricate machinery of {@link ResourceManager} and its {@code
35+
* ProcessItems} processor.
36+
*/
1637
@ExportLibrary(InteropLibrary.class)
1738
@ExportLibrary(TypesLibrary.class)
1839
@Builtin(pkg = "resource", stdlibName = "Standard.Base.Runtime.Managed_Resource.Managed_Resource")
@@ -97,8 +118,16 @@ Type getType(@Bind("$node") Node node) {
97118

98119
@ExportMessage
99120
@TruffleBoundary
121+
public String toDisplayString(boolean allowSideEffects, @Bind("$node") Node node) {
122+
var type = getType(node);
123+
return type.getName()
124+
+ " "
125+
+ InteropLibrary.getUncached().toDisplayString(resource, allowSideEffects);
126+
}
127+
128+
@ExportMessage.Ignore
100129
@Override
101-
public String toDisplayString(boolean allowSideEffects) {
102-
return resource.toString();
130+
public Object toDisplayString(boolean allowSideEffects) {
131+
throw CompilerDirectives.shouldNotReachHere();
103132
}
104133
}

test/Base_Tests/src/Runtime/Managed_Resource_Spec.enso

+53
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ from Standard.Base import all
22
import Standard.Base.Data.Vector.Builder
33
import Standard.Base.Errors.Illegal_State.Illegal_State
44
import Standard.Base.Runtime.Managed_Resource.Managed_Resource
5+
import Standard.Base.Runtime.Ref.Ref
56
import project.Runtime.GC_Example
67

78
from Standard.Test import all
@@ -68,6 +69,58 @@ add_specs suite_builder = suite_builder.group "Managed_Resource" group_builder->
6869
if messages.last != "OK" then
6970
Test.fail (messages.join '\n')
7071

72+
group_builder.specify "register_with_finalize" <|
73+
messages = Vector.build builder->
74+
builder.append ""
75+
76+
create_resource value =
77+
# registers new resource
78+
Managed_Resource.register (Ref.new value) v->
79+
v.put -1
80+
builder.append " finalizing:"+v.to_text
81+
82+
mr = create_resource 42
83+
84+
builder.append "Allocated: "+mr.to_text
85+
86+
# operates with its value
87+
out = mr.with v->
88+
builder.append " with :"+v.to_text
89+
v.put 7
90+
v.modify n->
91+
builder.append " modify:"+n.to_text
92+
6
93+
v
94+
builder.append "With finished:"+out.to_text
95+
96+
# finalizes the resource
97+
mr.finalize
98+
builder.append "Finalized:"+mr.to_text
99+
100+
# operation on finalized resource
101+
none = mr.with v->
102+
# should never be called
103+
builder.append " empty :"+v.to_text
104+
"Don't call me!"
105+
106+
builder.append none.to_text
107+
none.is_nothing . should_be_true
108+
109+
exp_text = """
110+
111+
Allocated: Managed_Resource 42
112+
with :42
113+
modify:7
114+
With finished:6
115+
finalizing:-1
116+
Finalized:Managed_Resource -1
117+
Nothing
118+
119+
msg_text = messages.join '\n'
120+
121+
if msg_text != exp_text then
122+
Test.fail (msg_text)
123+
71124
main filter=Nothing =
72125
suite = Test.build suite_builder->
73126
add_specs suite_builder

0 commit comments

Comments
 (0)