Refactor methods of Managed_Resource (#3460)

Promoted `with`, `take`, `finalize` to be methods of Managed_Resource
rather than static methods always taking `resource`, for consistency
reasons.

This required function dispatch boilerplate, similarly to `Ref`.
In future iterations we will address this boilerplate code.

Related to https://www.pivotaltracker.com/story/show/182212217
This commit is contained in:
Hubert Plociniczak 2022-05-18 19:27:42 +02:00 committed by GitHub
parent 8430ce2625
commit 12d6ef799f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 96 additions and 52 deletions

View File

@ -122,6 +122,8 @@
module][3457] module][3457]
- [Implemented `Table.parse_values`, parsing text columns according to a - [Implemented `Table.parse_values`, parsing text columns according to a
specified type.][3455] specified type.][3455]
- [Promote with, take, finalize to be methods of Managed_Resource
instance][3460]
[debug-shortcuts]: [debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
@ -189,6 +191,7 @@
[3442]: https://github.com/enso-org/enso/pull/3442 [3442]: https://github.com/enso-org/enso/pull/3442
[3457]: https://github.com/enso-org/enso/pull/3457 [3457]: https://github.com/enso-org/enso/pull/3457
[3455]: https://github.com/enso-org/enso/pull/3455 [3455]: https://github.com/enso-org/enso/pull/3455
[3460]: https://github.com/enso-org/enso/pull/3460
#### Enso Compiler #### Enso Compiler

View File

@ -36,7 +36,6 @@ type Managed_Resource
function once it is no longer in use. function once it is no longer in use.
Arguments: Arguments:
- resource: The resource to be managed automatically.
- function: The action to be executed on resource to clean it up when - function: The action to be executed on resource to clean it up when
it is no longer in use. it is no longer in use.
register : Any -> (Any -> Nothing) -> Managed_Resource register : Any -> (Any -> Nothing) -> Managed_Resource
@ -46,11 +45,8 @@ type Managed_Resource
Forces finalization of a managed resource using the registered finalizer, Forces finalization of a managed resource using the registered finalizer,
even if the resource is still reachable. even if the resource is still reachable.
finalize : Nothing
Arguments: finalize = @Builtin_Method "Managed_Resource.finalize"
- resource: The resource that should be finalized.
finalize : Managed_Resource -> Nothing
finalize resource = @Builtin_Method "Managed_Resource.finalize"
## ADVANCED ## ADVANCED
@ -58,20 +54,15 @@ type Managed_Resource
resource object. resource object.
Arguments: Arguments:
- resource: The managed resource on which to run the action.
- action: The action that will be applied to the resource managed by - action: The action that will be applied to the resource managed by
resource. resource.
with : Managed_Resource -> (Any -> Any) -> Any with : (Any -> Any) -> Any
with resource ~action = @Builtin_Method "Managed_Resource.with" with ~action = @Builtin_Method "Managed_Resource.with"
## ADVANCED ## ADVANCED
Takes the value held by the managed resource and unregisters the Takes the value held by the managed resource and unregisters the
finalization step for this resource, effectively removing it from the finalization step for this resource, effectively removing it from the
managed resources system. managed resources system.
Arguments:
- resource: The managed resource from which to acquire the underlying
resource.
take : Managed_Resource -> Any take : Managed_Resource -> Any
take resource = @Builtin_Method "Managed_Resource.take" take = @Builtin_Method "Managed_Resource.take"

View File

@ -727,7 +727,7 @@ type Output_Stream
out_stream.write_bytes "hello".utf_8 out_stream.write_bytes "hello".utf_8
out_stream.close out_stream.close
write_bytes : Vector.Vector -> Nothing ! File_Error write_bytes : Vector.Vector -> Nothing ! File_Error
write_bytes contents = Managed_Resource.with this.stream_resource java_stream-> write_bytes contents = this.stream_resource . with java_stream->
here.handle_java_exceptions this.file <| here.handle_java_exceptions this.file <|
java_stream.write contents.to_array java_stream.write contents.to_array
java_stream.flush java_stream.flush
@ -752,7 +752,7 @@ type Output_Stream
out_stream = file.new_output_stream [Option.Create] out_stream = file.new_output_stream [Option.Create]
out_stream.close out_stream.close
close : Nothing close : Nothing
close = Managed_Resource.finalize this.stream_resource close = this.stream_resource . finalize
## An input stream, allowing for interactive reading of contents from an open ## An input stream, allowing for interactive reading of contents from an open
file. file.
@ -786,7 +786,7 @@ type Input_Stream
in_stream.close in_stream.close
bytes bytes
read_all_bytes : Vector.Vector ! File_Error read_all_bytes : Vector.Vector ! File_Error
read_all_bytes = Managed_Resource.with this.stream_resource java_stream-> read_all_bytes = this.stream_resource . with java_stream->
here.handle_java_exceptions this.file <| here.handle_java_exceptions this.file <|
Vector.Vector java_stream.readAllBytes Vector.Vector java_stream.readAllBytes
@ -816,7 +816,7 @@ type Input_Stream
in_stream.close in_stream.close
bytes bytes
read_n_bytes : Integer -> Vector.Vector ! File_Error read_n_bytes : Integer -> Vector.Vector ! File_Error
read_n_bytes n = Managed_Resource.with this.stream_resource java_stream-> read_n_bytes n = this.stream_resource . with java_stream->
here.handle_java_exceptions this.file <| here.handle_java_exceptions this.file <|
bytes = java_stream.readNBytes n bytes = java_stream.readNBytes n
Vector.Vector bytes Vector.Vector bytes
@ -841,7 +841,7 @@ type Input_Stream
in_stream.close in_stream.close
bytes bytes
read_byte : Integer ! File_Error read_byte : Integer ! File_Error
read_byte = Managed_Resource.with this.stream_resource java_stream-> read_byte = this.stream_resource . with java_stream->
here.handle_java_exceptions this.file <| here.handle_java_exceptions this.file <|
java_stream.read java_stream.read
@ -864,7 +864,7 @@ type Input_Stream
in_stream = file.new_input_stream [Option.Read] in_stream = file.new_input_stream [Option.Read]
in_stream.close in_stream.close
close : Nothing close : Nothing
close = Managed_Resource.finalize this.stream_resource close = this.stream_resource . finalize
## PRIVATE ## PRIVATE
@ -876,7 +876,7 @@ type Input_Stream
Useful when integrating with polyglot functions requiring an Useful when integrating with polyglot functions requiring an
`InputStream` as an argument. `InputStream` as an argument.
with_java_stream : (Java_Input_Stream -> Any) -> Any with_java_stream : (Java_Input_Stream -> Any) -> Any
with_java_stream f = Managed_Resource.with this.stream_resource f with_java_stream f = this.stream_resource . with f
## PRIVATE ## PRIVATE

View File

@ -54,7 +54,7 @@ type Connection
The connection is not usable afterwards. The connection is not usable afterwards.
close : Nothing close : Nothing
close = close =
Managed_Resource.finalize this.connection_resource this.connection_resource . finalize
## ADVANCED ## ADVANCED
@ -109,7 +109,7 @@ type Connection
information to any thrown SQL errors. information to any thrown SQL errors.
with_prepared_statement : Text | Sql.Statement -> (PreparedStatement -> Any) -> Any with_prepared_statement : Text | Sql.Statement -> (PreparedStatement -> Any) -> Any
with_prepared_statement query action = with_prepared_statement query action =
prepare template holes = Managed_Resource.with this.connection_resource java_connection-> prepare template holes = this.connection_resource . with java_connection->
stmt = java_connection.prepareStatement template stmt = java_connection.prepareStatement template
Panic.catch Any (here.set_statement_values stmt holes) caught_panic-> Panic.catch Any (here.set_statement_values stmt holes) caught_panic->
stmt.close stmt.close
@ -182,7 +182,7 @@ type Connection
db_types = pairs.map p-> p.second.sql_type db_types = pairs.map p-> p.second.sql_type
insert_query = this.dialect.generate_sql <| IR.Insert name pairs insert_query = this.dialect.generate_sql <| IR.Insert name pairs
insert_template = insert_query.prepare.first insert_template = insert_query.prepare.first
Managed_Resource.with this.connection_resource java_connection-> this.connection_resource . with java_connection->
default_autocommit = java_connection.getAutoCommit default_autocommit = java_connection.getAutoCommit
java_connection.setAutoCommit False java_connection.setAutoCommit False
Resource.bracket Nothing (_ -> java_connection.setAutoCommit default_autocommit) _-> Resource.bracket Nothing (_ -> java_connection.setAutoCommit default_autocommit) _->

View File

@ -51,8 +51,8 @@ call.
> `Managed_Resource.register` call. > `Managed_Resource.register` call.
To perform operations on the underlying resource, use the To perform operations on the underlying resource, use the
`Managed_Resource.with resource action` method, where `resource` is the object `<managed-resource>.with action` method, where `<managed-resource>` is the
returned from the call to `Managed_Resource.register`, and `action` is a object returned from the call to `Managed_Resource.register`, and `action` is a
function taking the underlying object as its only argument. It is important that function taking the underlying object as its only argument. It is important that
the object passed to `action` is not stored and is not used past the return of the object passed to `action` is not stored and is not used past the return of
`action`. This means in particular that it is unsafe to give another thread a `action`. This means in particular that it is unsafe to give another thread a
@ -60,9 +60,8 @@ reference to that object, if the thread remains alive past the return of
`action`. If such an operation is necessary, the other thread should call `with` `action`. If such an operation is necessary, the other thread should call `with`
itself, using a reference to the original manged resource. itself, using a reference to the original manged resource.
A managed resource can be closed manually, using A managed resource can be closed manually, using `<managed-resource>.close`. The
`Managed_Resource.close resource`. The underlying object is then finalized underlying object is then finalized immediately.
immediately.
The finalization of a resource can be aborted using The finalization of a resource can be aborted using
`Managed_Resource.take resource`. This call will abort any automatic `Managed_Resource.take resource`. This call will abort any automatic

View File

@ -16,12 +16,12 @@ public abstract class FinalizeNode extends Node {
return FinalizeNodeGen.create(); return FinalizeNodeGen.create();
} }
abstract Object execute(Object _this, ManagedResource resource); abstract Object execute(Object _this);
@Specialization @Specialization
Object doClose(Object _this, ManagedResource resource) { Object doClose(ManagedResource _this) {
Context context = Context.get(this); Context context = Context.get(this);
context.getResourceManager().close(resource); context.getResourceManager().close(_this);
return context.getBuiltins().nothing().newInstance(); return context.getBuiltins().nothing().newInstance();
} }
} }

View File

@ -18,11 +18,11 @@ public abstract class TakeNode extends Node {
return TakeNodeGen.create(); return TakeNodeGen.create();
} }
abstract Object execute(Object _this, ManagedResource resource); abstract Object execute(Object _this);
@Specialization @Specialization
Object doTake(Object _this, ManagedResource resource) { Object doTake(ManagedResource _this) {
Context.get(this).getResourceManager().take(resource); Context.get(this).getResourceManager().take(_this);
return resource.getResource(); return _this.getResource();
} }
} }

View File

@ -30,22 +30,16 @@ public abstract class WithNode extends Node {
} }
abstract Stateful execute( abstract Stateful execute(
@MonadicState Object state, @MonadicState Object state, VirtualFrame frame, Object _this, Object action);
VirtualFrame frame,
Object _this,
ManagedResource resource,
Object action);
@Specialization @Specialization
Stateful doWith( Stateful doWith(Object state, VirtualFrame frame, ManagedResource _this, Object action) {
Object state, VirtualFrame frame, Object _this, ManagedResource resource, Object action) {
ResourceManager resourceManager = Context.get(this).getResourceManager(); ResourceManager resourceManager = Context.get(this).getResourceManager();
resourceManager.park(resource); resourceManager.park(_this);
try { try {
return invokeCallableNode.execute( return invokeCallableNode.execute(action, frame, state, new Object[] {_this.getResource()});
action, frame, state, new Object[] {resource.getResource()});
} finally { } finally {
resourceManager.unpark(resource); resourceManager.unpark(_this);
} }
} }
} }

View File

@ -1,10 +1,20 @@
package org.enso.interpreter.runtime.data; package org.enso.interpreter.runtime.data;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
import java.lang.ref.PhantomReference; import java.lang.ref.PhantomReference;
/** A runtime representation of a managed resource. */ /** A runtime representation of a managed resource. */
@ExportLibrary(MethodDispatchLibrary.class)
public class ManagedResource implements TruffleObject { public class ManagedResource implements TruffleObject {
private final Object resource; private final Object resource;
private PhantomReference<ManagedResource> phantomReference; private PhantomReference<ManagedResource> phantomReference;
@ -37,4 +47,51 @@ public class ManagedResource implements TruffleObject {
public void setPhantomReference(PhantomReference<ManagedResource> phantomReference) { public void setPhantomReference(PhantomReference<ManagedResource> phantomReference) {
this.phantomReference = phantomReference; this.phantomReference = phantomReference;
} }
@ExportMessage
boolean hasFunctionalDispatch() {
return true;
}
@ExportMessage
static class GetFunctionalDispatch {
static final int CACHE_SIZE = 10;
@CompilerDirectives.TruffleBoundary
static Function doResolve(UnresolvedSymbol symbol) {
Context context = getContext();
return symbol.resolveFor(
context.getBuiltins().managedResource(), context.getBuiltins().any());
}
static Context getContext() {
return Context.get(null);
}
@Specialization(
guards = {
"!getContext().isInlineCachingDisabled()",
"cachedSymbol == symbol",
"function != null"
},
limit = "CACHE_SIZE")
static Function resolveCached(
ManagedResource _this,
UnresolvedSymbol symbol,
@Cached("symbol") UnresolvedSymbol cachedSymbol,
@Cached("doResolve(cachedSymbol)") Function function) {
return function;
}
@Specialization(replaces = "resolveCached")
static Function resolve(ManagedResource _this, UnresolvedSymbol symbol)
throws MethodDispatchLibrary.NoSuchMethodException {
Function function = doResolve(symbol);
if (function == null) {
throw new MethodDispatchLibrary.NoSuchMethodException();
}
return function;
}
}
} }

View File

@ -87,7 +87,7 @@ class RuntimeManagementTest extends InterpreterTest {
|create_resource i = |create_resource i =
| c = Mock_File i | c = Mock_File i
| r = Managed_Resource.register c here.free_resource | r = Managed_Resource.register c here.free_resource
| Managed_Resource.with r f-> IO.println ("Accessing: " + f.to_text) | r . with f-> IO.println ("Accessing: " + f.to_text)
| |
|main = |main =
| here.create_resource 0 | here.create_resource 0
@ -127,8 +127,8 @@ class RuntimeManagementTest extends InterpreterTest {
|create_resource i = |create_resource i =
| c = Mock_File i | c = Mock_File i
| r = Managed_Resource.register c here.free_resource | r = Managed_Resource.register c here.free_resource
| Managed_Resource.with r f-> IO.println ("Accessing: " + f.to_text) | r . with f-> IO.println ("Accessing: " + f.to_text)
| if i % 2 == 0 then Managed_Resource.finalize r else Nothing | if i % 2 == 0 then r.finalize else Nothing
| |
|main = |main =
| here.create_resource 0 | here.create_resource 0
@ -168,8 +168,8 @@ class RuntimeManagementTest extends InterpreterTest {
|create_resource i = |create_resource i =
| c = Mock_File i | c = Mock_File i
| r = Managed_Resource.register c here.free_resource | r = Managed_Resource.register c here.free_resource
| Managed_Resource.with r f-> IO.println ("Accessing: " + f.to_text) | r . with f-> IO.println ("Accessing: " + f.to_text)
| if i % 2 == 0 then Managed_Resource.take r else Nothing | if i % 2 == 0 then r.take else Nothing
| |
|main = |main =
| here.create_resource 0 | here.create_resource 0