1 /*
  2  * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package jdk.incubator.foreign;
 27 
 28 import jdk.internal.foreign.ResourceScopeImpl;
 29 import jdk.internal.ref.CleanerFactory;
 30 
 31 import java.lang.invoke.MethodHandle;
 32 import java.lang.ref.Cleaner;
 33 import java.nio.ByteBuffer;
 34 import java.nio.channels.FileChannel;
 35 import java.nio.file.Path;
 36 import java.util.Objects;
 37 import java.util.Spliterator;
 38 
 39 /**
 40  * A resource scope manages the lifecycle of one or more resources. Resources (e.g. {@link MemorySegment}) associated
 41  * with a resource scope can only be accessed while the resource scope is {@linkplain #isAlive() alive},
 42  * and by the {@linkplain #ownerThread() thread} associated with the resource scope (if any).
 43  *
 44  * <h2>Deterministic deallocation</h2>
 45  *
 46  * Resource scopes support <em>deterministic deallocation</em>; that is, they can be {@linkplain ResourceScope#close() closed}
 47  * explicitly. When a resource scope is closed, it is no longer {@link #isAlive() alive}, and subsequent
 48  * operations on resources associated with that scope (e.g. attempting to access a {@link MemorySegment} instance)
 49  * will fail with {@link IllegalStateException}.
 50  * <p>
 51  * Closing a resource scope will cause all the {@linkplain #addCloseAction(Runnable) close actions} associated with that scope to be called.
 52  * Moreover, closing a resource scope might trigger the releasing of the underlying memory resources associated with said scope; for instance:
 53  * <ul>
 54  *     <li>closing the scope associated with a {@linkplain MemorySegment#allocateNative(long, long, ResourceScope) native memory segment}
 55  *     results in <em>freeing</em> the native memory associated with it;</li>
 56  *     <li>closing the scope associated with a {@linkplain MemorySegment#mapFile(Path, long, long, FileChannel.MapMode, ResourceScope) mapped memory segment}
 57  *     results in the backing memory-mapped file to be unmapped;</li>
 58  *     <li>closing the scope associated with an {@linkplain CLinker#upcallStub(MethodHandle, FunctionDescriptor, ResourceScope) upcall stub}
 59  *     results in releasing the stub;</li>
 60  *     <li>closing the scope associated with a {@linkplain VaList variable arity list} results in releasing the memory
 61  *     associated with that variable arity list instance.</li>
 62  * </ul>
 63  *
 64  * <h2>Implicit deallocation</h2>
 65  *
 66  * Resource scopes can be associated with a {@link Cleaner} instance, so that they are also closed automatically,
 67  * once the scope instance becomes <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>.
 68  * This can be useful to allow for predictable, deterministic resource deallocation, while still preventing accidental
 69  * native memory leaks. In case a managed resource scope is closed explicitly, no further action will be taken when
 70  * the scope becomes unreachable; that is, {@linkplain #addCloseAction(Runnable) close actions} associated with a
 71  * resource scope, whether managed or not, are called <em>exactly once</em>.
 72  *
 73  * <h2><a id = "global-scope">Global scope</a></h2>
 74  *
 75  * An important implicit resource scope is the so called {@linkplain #globalScope() global scope}; the global scope is
 76  * a resource scope that cannot be closed, either explicitly or implicitly. As a result, the global scope will never
 77  * attempt to release resources associated with it. Examples of resources associated with the global scope are:
 78  * <ul>
 79  *     <li>heap segments created from {@linkplain MemorySegment#ofArray(int[]) arrays} or
 80  *     {@linkplain MemorySegment#ofByteBuffer(ByteBuffer) buffers};</li>
 81  *     <li>variable arity lists {@linkplain VaList#ofAddress(MemoryAddress, ResourceScope) obtained} from raw memory addresses;
 82  *     <li>native symbols {@linkplain SymbolLookup#lookup(String) obtained} from a {@linkplain SymbolLookup#loaderLookup() loader lookup},
 83  *     or from the {@link CLinker}.</li>
 84  * </ul>
 85  * In other words, the global scope is used to indicate that the lifecycle of one or more resources must, where
 86  * needed, be managed independently by clients.
 87  *
 88  * <h2><a id = "thread-confinement">Thread confinement</a></h2>
 89  *
 90  * Resource scopes can be divided into two categories: <em>thread-confined</em> resource scopes, and <em>shared</em>
 91  * resource scopes.
 92  * <p>
 93  * {@linkplain #newConfinedScope() Confined resource scopes}, support strong thread-confinement guarantees. Upon creation,
 94  * they are assigned an {@linkplain #ownerThread() owner thread}, typically the thread which initiated the creation operation.
 95  * After creating a confined resource scope, only the owner thread will be allowed to directly manipulate the resources
 96  * associated with this resource scope. Any attempt to perform resource access from a thread other than the
 97  * owner thread will result in a runtime failure.
 98  * <p>
 99  * {@linkplain #newSharedScope() Shared resource scopes}, on the other hand, have no owner thread;
100  * as such, resources associated with shared resource scopes can be accessed by multiple threads.
101  * This might be useful when multiple threads need to access the same resource concurrently (e.g. in the case of parallel processing).
102  * For instance, a client might obtain a {@link Spliterator} from a segment backed by a shared scope, which can then be used to slice the
103  * segment and allow multiple threads to work in parallel on disjoint segment slices. The following code can be used to sum
104  * all int values in a memory segment in parallel:
105  *
106  * <blockquote><pre>{@code
107 try (ResourceScope scope = ResourceScope.newSharedScope()) {
108     SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.sequenceLayout(1024, ValueLayout.JAVA_INT);
109     MemorySegment segment = MemorySegment.allocateNative(SEQUENCE_LAYOUT, scope);
110     int sum = segment.elements(ValueLayout.JAVA_INT).parallel()
111                         .mapToInt(s -> s.get(ValueLayout.JAVA_INT, 0))
112                         .sum();
113 }
114  * }</pre></blockquote>
115  *
116  * <p>
117  * Shared resource scopes, while powerful, must be used with caution: if one or more threads accesses
118  * a resource associated with a shared scope while the scope is being closed from another thread, an exception might occur on both
119  * the accessing and the closing threads. Clients should refrain from attempting to close a shared resource scope repeatedly
120  * (e.g. keep calling {@link #close()} until no exception is thrown). Instead, clients of shared resource scopes
121  * should always ensure that proper synchronization mechanisms (e.g. using temporal dependencies, see below) are put in place
122  * so that threads closing shared resource scopes can never race against threads accessing resources managed by same scopes.
123  *
124  * <h2>Temporal dependencies</h2>
125  *
126  * Resource scopes can depend on each other. More specifically, a scope can feature
127  * {@linkplain #keepAlive(ResourceScope) temporal dependencies} on one or more other resource scopes.
128  * Such a resource scope cannot be closed (either implicitly or explicitly) until <em>all</em> the scopes it depends on
129  * have also been closed.
130  * <p>
131  * This can be useful when clients need to perform a critical operation on a memory segment, during which they have
132  * to ensure that the scope associated with that segment will not be closed; this can be done as follows:
133  *
134  * <blockquote><pre>{@code
135 MemorySegment segment = ...
136 try (ResourceScope criticalScope = ResourceScope.newConfinedScope()) {
137     criticalScope.keepAlive(segment.scope());
138     <critical operation on segment>
139 }
140  * }</pre></blockquote>
141  *
142  * Note that a resource scope does not become <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>
143  * until all the scopes it depends on have been closed.
144  *
145  * @implSpec
146  * Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
147  */
148 public sealed interface ResourceScope extends AutoCloseable permits ResourceScopeImpl {
149     /**
150      * Is this resource scope alive?
151      * @return true, if this resource scope is alive.
152      * @see ResourceScope#close()
153      */
154     boolean isAlive();
155 
156     /**
157      * The thread owning this resource scope.
158      * @return the thread owning this resource scope, or {@code null} if this resource scope is shared.
159      */
160     Thread ownerThread();
161 
162     /**
163      * Closes this resource scope. As a side effect, if this operation completes without exceptions, this scope will be marked
164      * as <em>not alive</em>, and subsequent operations on resources associated with this scope will fail with {@link IllegalStateException}.
165      * Additionally, upon successful closure, all native resources associated with this resource scope will be released.
166      *
167      * @apiNote This operation is not idempotent; that is, closing an already closed resource scope <em>always</em> results in an
168      * exception being thrown. This reflects a deliberate design choice: resource scope state transitions should be
169      * manifest in the client code; a failure in any of these transitions reveals a bug in the underlying application
170      * logic.
171      *
172      * @throws IllegalStateException if one of the following condition is met:
173      * <ul>
174      *     <li>this resource scope is not <em>alive</em>
175      *     <li>this resource scope is confined, and this method is called from a thread other than the thread owning this resource scope</li>
176      *     <li>this resource scope is shared and a resource associated with this scope is accessed while this method is called</li>
177      *     <li>one or more scopes which {@linkplain #keepAlive(ResourceScope) depend} on this resource scope have not been closed.
178      * </ul>
179      * @throws UnsupportedOperationException if this resource scope is the {@linkplain #globalScope() global scope}.
180      */
181     void close();
182 
183     /**
184      * Add a custom cleanup action which will be executed when the resource scope is closed.
185      * The order in which custom cleanup actions are invoked once the scope is closed is unspecified.
186      * @param runnable the custom cleanup action to be associated with this scope.
187      * @throws IllegalStateException if this scope has been closed, or if access occurs from
188      * a thread other than the thread owning this scope.
189      */
190     void addCloseAction(Runnable runnable);
191 
192     /**
193      * Creates a temporal dependency between this scope and the target scope. As a result, the target scope cannot
194      * be {@linkplain #close() closed} <em>before</em> this scope.
195      * @implNote A given scope can support up to {@link Integer#MAX_VALUE} pending keep alive requests.
196      * @param target the scope that needs to be kept alive.
197      * @throws IllegalArgumentException if {@code target == this}.
198      * @throws IllegalStateException if this scope or {@code target} have been closed, or if access occurs from
199      * a thread other than the thread owning this scope or {@code target}.
200      */
201     void keepAlive(ResourceScope target);
202 
203     /**
204      * Creates a new confined scope.
205      * @return a new confined scope.
206      */
207     static ResourceScope newConfinedScope() {
208         return ResourceScopeImpl.createConfined( Thread.currentThread(), null);
209     }
210 
211     /**
212      * Creates a new confined scope, managed by the provided cleaner instance.
213      * @param cleaner the cleaner to be associated with the returned scope.
214      * @return a new confined scope, managed by {@code cleaner}.
215      */
216     static ResourceScope newConfinedScope(Cleaner cleaner) {
217         Objects.requireNonNull(cleaner);
218         return ResourceScopeImpl.createConfined(Thread.currentThread(), cleaner);
219     }
220 
221     /**
222      * Creates a new shared scope.
223      * @return a new shared scope.
224      */
225     static ResourceScope newSharedScope() {
226         return ResourceScopeImpl.createShared(null);
227     }
228 
229     /**
230      * Creates a new shared scope, managed by the provided cleaner instance.
231      * @param cleaner the cleaner to be associated with the returned scope.
232      * @return a new shared scope, managed by {@code cleaner}.
233      */
234     static ResourceScope newSharedScope(Cleaner cleaner) {
235         Objects.requireNonNull(cleaner);
236         return ResourceScopeImpl.createShared(cleaner);
237     }
238 
239     /**
240      * Creates a new shared scope, managed by a private {@link Cleaner} instance. Equivalent to (but likely more efficient than)
241      * the following code:
242      * <pre>{@code
243     newSharedScope(Cleaner.create());
244      * }</pre>
245      * @return a shared scope, managed by a private {@link Cleaner} instance.
246      */
247     static ResourceScope newImplicitScope() {
248         return newSharedScope(CleanerFactory.cleaner());
249     }
250 
251     /**
252      * Returns the <a href="ResourceScope.html#global-scope"><em>global scope</em></a>.
253      * @return the <a href="ResourceScope.html#global-scope"><em>global scope</em></a>.
254      */
255     static ResourceScope globalScope() {
256         return ResourceScopeImpl.GLOBAL;
257     }
258 }