diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fa496af7e..ca12ddb340 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime/Resource.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime/Resource.enso index b7db4c23f5..a96f5e2f52 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime/Resource.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Runtime/Resource.enso @@ -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" diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso index f68fe8d7a4..f92dd21f3f 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso @@ -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 diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Connection/Connection.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Connection/Connection.enso index 03b3883af6..67b64e1e39 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Connection/Connection.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Connection/Connection.enso @@ -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) _-> diff --git a/docs/semantics/managed-resources.md b/docs/semantics/managed-resources.md index e9b7dae070..bcd4237bd7 100644 --- a/docs/semantics/managed-resources.md +++ b/docs/semantics/managed-resources.md @@ -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 +`.with action` method, where `` 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 `.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 diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/FinalizeNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/FinalizeNode.java index f5141de68a..0c9c3d54b7 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/FinalizeNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/FinalizeNode.java @@ -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(); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/TakeNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/TakeNode.java index 2c791295fe..bacc19749f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/TakeNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/resource/TakeNode.java @@ -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(); } } 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 f26d3ebb10..3e9f0e4e89 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 @@ -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); } } } 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 fde2b7a953..81fbf36824 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,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 phantomReference; @@ -37,4 +47,51 @@ public class ManagedResource implements TruffleObject { public void setPhantomReference(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; + } + } } diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/RuntimeManagementTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/RuntimeManagementTest.scala index 46cb270403..290ea01d99 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/RuntimeManagementTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/RuntimeManagementTest.scala @@ -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