Skip to content

Commit

Permalink
Can no longer invoke Managed_Resource.with when Managed_Resource.fina…
Browse files Browse the repository at this point in the history
…lize was called
  • Loading branch information
JaroslavTulach committed Nov 27, 2024
1 parent c493ec0 commit ef15d90
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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.
*
* <p><b>Instance of this class</b> is convoluted with instances playing various roles:
*
* <ul>
* <li>this {@link ManagedResource} points to {@code resource}
* <li>this {@link ManagedResource} points to {@link PhantomReference} that is "phantom
* referencing" {@code this}
* <li>the implementation of {@link PhantomReference} is {@code Item} in {@link ResourceManager}
* <li>the {@code Item} <em>"phantom referencing"</em> {@code this} {@link ManagedResource} is
* stored in {@link ResourceManager} {@code pendingItems} collection.
* <li>the {@code Item} has a pointer to the {@code resource} as well
* <li>the {@code Item} has a pointer to the {@code finalizer} function
* </ul>
*
* Once all this braided chunk of objects is eligible for GC because <b>nobody holds pointer to
* {@link ManagedResource}</b>, 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")
Expand Down Expand Up @@ -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();
}
}
53 changes: 53 additions & 0 deletions test/Base_Tests/src/Runtime/Managed_Resource_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit ef15d90

Please sign in to comment.