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]
- [Implemented `Table.parse_values`, parsing text columns according to a
specified type.][3455]
- [Promote with, take, finalize to be methods of Managed_Resource
instance][3460]
[debug-shortcuts]:
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
[3457]: https://github.com/enso-org/enso/pull/3457
[3455]: https://github.com/enso-org/enso/pull/3455
[3460]: https://github.com/enso-org/enso/pull/3460
#### Enso Compiler

View File

@ -36,7 +36,6 @@ type Managed_Resource
function once it is no longer in use.
Arguments:
- resource: The resource to be managed automatically.
- function: The action to be executed on resource to clean it up when
it is no longer in use.
register : Any -> (Any -> Nothing) -> Managed_Resource
@ -46,11 +45,8 @@ type Managed_Resource
Forces finalization of a managed resource using the registered finalizer,
even if the resource is still reachable.
Arguments:
- resource: The resource that should be finalized.
finalize : Managed_Resource -> Nothing
finalize resource = @Builtin_Method "Managed_Resource.finalize"
finalize : Nothing
finalize = @Builtin_Method "Managed_Resource.finalize"
## ADVANCED
@ -58,20 +54,15 @@ type Managed_Resource
resource object.
Arguments:
- resource: The managed resource on which to run the action.
- action: The action that will be applied to the resource managed by
resource.
with : Managed_Resource -> (Any -> Any) -> Any
with resource ~action = @Builtin_Method "Managed_Resource.with"
with : (Any -> Any) -> Any
with ~action = @Builtin_Method "Managed_Resource.with"
## ADVANCED
Takes the value held by the managed resource and unregisters the
finalization step for this resource, effectively removing it from the
managed resources system.
Arguments:
- resource: The managed resource from which to acquire the underlying
resource.
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.close
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 <|
java_stream.write contents.to_array
java_stream.flush
@ -752,7 +752,7 @@ type Output_Stream
out_stream = file.new_output_stream [Option.Create]
out_stream.close
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
file.
@ -786,7 +786,7 @@ type Input_Stream
in_stream.close
bytes
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 <|
Vector.Vector java_stream.readAllBytes
@ -816,7 +816,7 @@ type Input_Stream
in_stream.close
bytes
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 <|
bytes = java_stream.readNBytes n
Vector.Vector bytes
@ -841,7 +841,7 @@ type Input_Stream
in_stream.close
bytes
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 <|
java_stream.read
@ -864,7 +864,7 @@ type Input_Stream
in_stream = file.new_input_stream [Option.Read]
in_stream.close
close : Nothing
close = Managed_Resource.finalize this.stream_resource
close = this.stream_resource . finalize
## PRIVATE
@ -876,7 +876,7 @@ type Input_Stream
Useful when integrating with polyglot functions requiring an
`InputStream` as an argument.
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

View File

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

View File

@ -51,8 +51,8 @@ call.
> `Managed_Resource.register` call.
To perform operations on the underlying resource, use the
`Managed_Resource.with resource action` method, where `resource` is the object
returned from the call to `Managed_Resource.register`, and `action` is a
`<managed-resource>.with action` method, where `<managed-resource>` is the
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
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
@ -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`
itself, using a reference to the original manged resource.
A managed resource can be closed manually, using
`Managed_Resource.close resource`. The underlying object is then finalized
immediately.
A managed resource can be closed manually, using `<managed-resource>.close`. The
underlying object is then finalized immediately.
The finalization of a resource can be aborted using
`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();
}
abstract Object execute(Object _this, ManagedResource resource);
abstract Object execute(Object _this);
@Specialization
Object doClose(Object _this, ManagedResource resource) {
Object doClose(ManagedResource _this) {
Context context = Context.get(this);
context.getResourceManager().close(resource);
context.getResourceManager().close(_this);
return context.getBuiltins().nothing().newInstance();
}
}

View File

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

View File

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

View File

@ -1,10 +1,20 @@
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.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;
/** A runtime representation of a managed resource. */
@ExportLibrary(MethodDispatchLibrary.class)
public class ManagedResource implements TruffleObject {
private final Object resource;
private PhantomReference<ManagedResource> phantomReference;
@ -37,4 +47,51 @@ public class ManagedResource implements TruffleObject {
public void setPhantomReference(PhantomReference<ManagedResource> 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 =
| c = Mock_File i
| 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 =
| here.create_resource 0
@ -127,8 +127,8 @@ class RuntimeManagementTest extends InterpreterTest {
|create_resource i =
| c = Mock_File i
| r = Managed_Resource.register c here.free_resource
| Managed_Resource.with r f-> IO.println ("Accessing: " + f.to_text)
| if i % 2 == 0 then Managed_Resource.finalize r else Nothing
| r . with f-> IO.println ("Accessing: " + f.to_text)
| if i % 2 == 0 then r.finalize else Nothing
|
|main =
| here.create_resource 0
@ -168,8 +168,8 @@ class RuntimeManagementTest extends InterpreterTest {
|create_resource i =
| c = Mock_File i
| r = Managed_Resource.register c here.free_resource
| Managed_Resource.with r f-> IO.println ("Accessing: " + f.to_text)
| if i % 2 == 0 then Managed_Resource.take r else Nothing
| r . with f-> IO.println ("Accessing: " + f.to_text)
| if i % 2 == 0 then r.take else Nothing
|
|main =
| here.create_resource 0