diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime/Managed_Resource.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime/Managed_Resource.enso index 35701ebfdc74..9312b0a121b6 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime/Managed_Resource.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime/Managed_Resource.enso @@ -58,11 +58,15 @@ type Managed_Resource ADVANCED Executes the provided action on the resource managed by the managed - resource object. + resource object. The action is only invoked if the managed resource + has not yet been finalized. Arguments: - action: The action that will be applied to the resource managed by resource. + Returns: + Value returned from the `action` or `Nothing` if the managed resource + was already finalized with : (Any -> Any) -> Any with self ~action = @Builtin_Method "Managed_Resource.with" diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/WithNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/WithNode.java index 2dd940690f4c..1f61fab30186 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/WithNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/WithNode.java @@ -6,7 +6,6 @@ import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.callable.InvokeCallableNode; import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.ResourceManager; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.data.ManagedResource; import org.enso.interpreter.runtime.state.State; @@ -33,12 +32,17 @@ static WithNode build() { @Specialization Object doWith(State state, VirtualFrame frame, ManagedResource self, Object action) { - ResourceManager resourceManager = EnsoContext.get(this).getResourceManager(); - resourceManager.park(self); - try { - return invokeCallableNode.execute(action, frame, state, new Object[] {self.getResource()}); - } finally { - resourceManager.unpark(self); + var ctx = EnsoContext.get(this); + var resourceManager = ctx.getResourceManager(); + if (self.getPhantomReference().refersTo(self)) { + resourceManager.park(self); + try { + return invokeCallableNode.execute(action, frame, state, new Object[] {self.getResource()}); + } finally { + resourceManager.unpark(self); + } + } else { + return ctx.getBuiltins().nothing(); } } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/ResourceManager.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/ResourceManager.java index b8295c6b9457..78c8bb26937f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/ResourceManager.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/ResourceManager.java @@ -408,6 +408,7 @@ private Item( @CompilerDirectives.TruffleBoundary private void finalizeNow(EnsoContext context) { try { + clear(); InteropLibrary.getUncached(finalizer).execute(finalizer, underlying); } catch (Exception e) { context.getErr().println("Exception in finalizer: " + e.getMessage()); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/ManagedResource.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/ManagedResource.java index 424eac477f3c..826e5554e788 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/ManagedResource.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/ManagedResource.java @@ -1,5 +1,6 @@ package org.enso.interpreter.runtime.data; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.interop.InteropLibrary; @@ -12,7 +13,27 @@ import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; -/** A runtime representation of a managed resource. */ +/** + * An Enso runtime representation of a managed resource. + * + *

Instance of this class is convoluted with instances playing various roles: + * + *

+ * + * Once all this braided chunk of objects is eligible for GC because nobody holds pointer to + * {@link ManagedResource}, the {@code Item} is put into {@link ResourceManager} {@code + * referenceQueue} and process by the intricate machinery of {@link ResourceManager} and its {@code + * ProcessItems} processor. + */ @ExportLibrary(InteropLibrary.class) @ExportLibrary(TypesLibrary.class) @Builtin(pkg = "resource", stdlibName = "Standard.Base.Runtime.Managed_Resource.Managed_Resource") @@ -97,8 +118,16 @@ Type getType(@Bind("$node") Node node) { @ExportMessage @TruffleBoundary + public String toDisplayString(boolean allowSideEffects, @Bind("$node") Node node) { + var type = getType(node); + return type.getName() + + " " + + InteropLibrary.getUncached().toDisplayString(resource, allowSideEffects); + } + + @ExportMessage.Ignore @Override - public String toDisplayString(boolean allowSideEffects) { - return resource.toString(); + public Object toDisplayString(boolean allowSideEffects) { + throw CompilerDirectives.shouldNotReachHere(); } } diff --git a/test/Base_Tests/src/Runtime/Managed_Resource_Spec.enso b/test/Base_Tests/src/Runtime/Managed_Resource_Spec.enso index 14f3cd6730cc..69ddd4252397 100644 --- a/test/Base_Tests/src/Runtime/Managed_Resource_Spec.enso +++ b/test/Base_Tests/src/Runtime/Managed_Resource_Spec.enso @@ -2,6 +2,7 @@ from Standard.Base import all import Standard.Base.Data.Vector.Builder import Standard.Base.Errors.Illegal_State.Illegal_State import Standard.Base.Runtime.Managed_Resource.Managed_Resource +import Standard.Base.Runtime.Ref.Ref import project.Runtime.GC_Example from Standard.Test import all @@ -68,6 +69,58 @@ add_specs suite_builder = suite_builder.group "Managed_Resource" group_builder-> if messages.last != "OK" then Test.fail (messages.join '\n') + group_builder.specify "register_with_finalize" <| + messages = Vector.build builder-> + builder.append "" + + create_resource value = + # registers new resource + Managed_Resource.register (Ref.new value) v-> + v.put -1 + builder.append " finalizing:"+v.to_text + + mr = create_resource 42 + + builder.append "Allocated: "+mr.to_text + + # operates with its value + out = mr.with v-> + builder.append " with :"+v.to_text + v.put 7 + v.modify n-> + builder.append " modify:"+n.to_text + 6 + v + builder.append "With finished:"+out.to_text + + # finalizes the resource + mr.finalize + builder.append "Finalized:"+mr.to_text + + # operation on finalized resource + none = mr.with v-> + # should never be called + builder.append " empty :"+v.to_text + "Don't call me!" + + builder.append none.to_text + none.is_nothing . should_be_true + + exp_text = """ + + Allocated: Managed_Resource 42 + with :42 + modify:7 + With finished:6 + finalizing:-1 + Finalized:Managed_Resource -1 + Nothing + + msg_text = messages.join '\n' + + if msg_text != exp_text then + Test.fail (msg_text) + main filter=Nothing = suite = Test.build suite_builder-> add_specs suite_builder