< prev index next >

src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/ResourceScopeImpl.java

Print this page

 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 
 27 package jdk.internal.foreign;
 28 
 29 import jdk.incubator.foreign.MemorySegment;
 30 import jdk.incubator.foreign.ResourceScope;
 31 import jdk.incubator.foreign.SegmentAllocator;
 32 import jdk.internal.misc.ScopedMemoryAccess;
 33 import jdk.internal.ref.CleanerFactory;
 34 
 35 import java.lang.ref.Cleaner;
 36 import java.lang.ref.Reference;
 37 import java.util.Objects;
 38 
 39 /**
 40  * This class manages the temporal bounds associated with a memory segment as well
 41  * as thread confinement. A scope has a liveness bit, which is updated when the scope is closed
 42  * (this operation is triggered by {@link ResourceScope#close()}). This bit is consulted prior
 43  * to memory access (see {@link #checkValidState()}).
 44  * There are two kinds of memory scope: confined memory scope and shared memory scope.
 45  * A confined memory scope has an associated owner thread that confines some operations to
 46  * associated owner thread such as {@link #close()} or {@link #checkValidState()}.
 47  * Shared scopes do not feature an owner thread - meaning their operations can be called, in a racy
 48  * manner, by multiple threads. To guarantee temporal safety in the presence of concurrent thread,
 49  * shared scopes use a more sophisticated synchronization mechanism, which guarantees that no concurrent
 50  * access is possible when a scope is being closed (see {@link jdk.internal.misc.ScopedMemoryAccess}).
 51  */
 52 public abstract non-sealed class ResourceScopeImpl implements ResourceScope, ScopedMemoryAccess.Scope, SegmentAllocator {
 53 
 54     final ResourceList resourceList;



 55 
 56     @Override
 57     public void addCloseAction(Runnable runnable) {
 58         Objects.requireNonNull(runnable);
 59         addInternal(ResourceList.ResourceCleanup.ofRunnable(runnable));
 60     }
 61 
 62     @Override
 63     public boolean isImplicit() {
 64         return false;
 65     }
 66 
 67     /**
 68      * Add a cleanup action. If a failure occurred (because of a add vs. close race), call the cleanup action.
 69      * This semantics is useful when allocating new memory segments, since we first do a malloc/mmap and _then_
 70      * we register the cleanup (free/munmap) against the scope; so, if registration fails, we still have to
 71      * cleanup memory. From the perspective of the client, such a failure would manifest as a factory
 72      * returning a segment that is already "closed" - which is always possible anyway (e.g. if the scope
 73      * is closed _after_ the cleanup for the segment is registered but _before_ the factory returns the
 74      * new segment to the client). For this reason, it's not worth adding extra complexity to the segment
 75      * initialization logic here - and using an optimistic logic works well in practice.
 76      */
 77     public void addOrCleanupIfFail(ResourceList.ResourceCleanup resource) {
 78         try {
 79             addInternal(resource);
 80         } catch (Throwable ex) {
 81             resource.cleanup();
 82         }
 83     }
 84 
 85     void addInternal(ResourceList.ResourceCleanup resource) {
 86         try {
 87             checkValidStateSlow();
 88             resourceList.add(resource);
 89         } catch (ScopedMemoryAccess.Scope.ScopedAccessError err) {
 90             throw new IllegalStateException("Already closed");
 91         }
 92     }
 93 
 94     protected ResourceScopeImpl(Cleaner cleaner, ResourceList resourceList) {
 95         this.resourceList = resourceList;
 96         if (cleaner != null) {
 97             cleaner.register(this, resourceList);
 98         }
 99     }
100 
101     public static ResourceScopeImpl createImplicitScope() {
102         return new ImplicitScopeImpl(CleanerFactory.cleaner());
103     }
104 
105     public static ResourceScopeImpl createConfined(Thread thread, Cleaner cleaner) {
106         return new ConfinedScope(thread, cleaner);
107     }
108 
109     /**
110      * Creates a confined memory scope with given attachment and cleanup action. The returned scope
111      * is assumed to be confined on the current thread.
112      * @return a confined memory scope
113      */
114     public static ResourceScopeImpl createConfined(Cleaner cleaner) {
115         return new ConfinedScope(Thread.currentThread(), cleaner);
116     }
117 
118     /**
119      * Creates a shared memory scope with given attachment and cleanup action.
120      * @return a shared memory scope
121      */
122     public static ResourceScopeImpl createShared(Cleaner cleaner) {
123         return new SharedScope(cleaner);
124     }
125 
126     private final void release0(HandleImpl handle) {
127         try {
128             Objects.requireNonNull(handle);
129             if (handle.scope() != this) {
130                 throw new IllegalArgumentException("Cannot release an handle acquired from another scope");
131             }
132             handle.release();
133         } finally {
134             Reference.reachabilityFence(this);
135         }
136     }
137 
138     @Override
139     public final void release(ResourceScope.Handle handle) {
140         release0((HandleImpl)handle);
141     }
142 
143     @Override
144     public final void release(ScopedMemoryAccess.Scope.Handle handle) {
145         release0((HandleImpl)handle);
146     }
147 
148     @Override
149     public abstract HandleImpl acquire();
150 
151     /**
152      * Internal interface used to implement resource scope handles.
153      */
154     public non-sealed interface HandleImpl extends ResourceScope.Handle, ScopedMemoryAccess.Scope.Handle {
155 
156         @Override
157         ResourceScopeImpl scope();
158 
159         void release();







160     }
161 
162     /**
163      * Closes this scope, executing any cleanup action (where provided).
164      * @throws IllegalStateException if this scope is already closed or if this is
165      * a confined scope and this method is called outside of the owner thread.
166      */
167     public void close() {
168         try {
169             justClose();
170             resourceList.cleanup();




171         } finally {
172             Reference.reachabilityFence(this);
173         }
174     }
175 
176     abstract void justClose();
177 
178     /**
179      * Returns "owner" thread of this scope.
180      * @return owner thread (or null for a shared scope)
181      */
182     public abstract Thread ownerThread();
183 
184     /**
185      * Returns true, if this scope is still alive. This method may be called in any thread.
186      * @return {@code true} if this scope is not closed yet.
187      */
188     public abstract boolean isAlive();
189 
190 

198 
199     /**
200      * Checks that this scope is still alive (see {@link #isAlive()}).
201      * @throws IllegalStateException if this scope is already closed or if this is
202      * a confined scope and this method is called outside of the owner thread.
203      */
204     public final void checkValidStateSlow() {
205         if (ownerThread() != null && Thread.currentThread() != ownerThread()) {
206             throw new IllegalStateException("Attempted access outside owning thread");
207         } else if (!isAlive()) {
208             throw new IllegalStateException("Already closed");
209         }
210     }
211 
212     @Override
213     protected Object clone() throws CloneNotSupportedException {
214         throw new CloneNotSupportedException();
215     }
216 
217     /**
218      * Allocates a segment using this scope. Used by {@link SegmentAllocator#ofScope(ResourceScope)}.
219      */
220     @Override
221     public MemorySegment allocate(long bytesSize, long bytesAlignment) {
222         return MemorySegment.allocateNative(bytesSize, bytesAlignment, this);
223     }
224 
225     /**
226      * A non-closeable, shared scope. Similar to a shared scope, but its {@link #close()} method throws unconditionally.
227      * In addition, non-closeable scopes feature a much simpler scheme for generating resource scope handles, where
228      * the scope itself also acts as a resource scope handle and is returned by {@link #acquire()}.
229      */
230     static class ImplicitScopeImpl extends SharedScope implements HandleImpl {
231 
232         public ImplicitScopeImpl(Cleaner cleaner) {
233             super(cleaner);
234         }
235 
236         @Override
237         public HandleImpl acquire() {
238             return this;
239         }
240 
241         @Override
242         public boolean isImplicit() {
243             return true;
244         }
245 
246         @Override
247         public void close() {
248             throw new UnsupportedOperationException("Scope cannot be closed");
249         }
250 
251         @Override
252         public void release() {

253             // do nothing
254         }
255 
256         @Override
257         public ResourceScopeImpl scope() {
258             return this;

259         }
260     }
261 
262     /**
263      * The global, always alive, non-closeable, shared scope. This is like a {@link ImplicitScopeImpl non-closeable scope},
264      * except that the operation which adds new resources to the global scope does nothing: as the scope can never
265      * become not-alive, there is nothing to track.
266      */
267     public static final ResourceScopeImpl GLOBAL = new ImplicitScopeImpl( null) {
268         @Override
269         void addInternal(ResourceList.ResourceCleanup resource) {
270             // do nothing
271         }
272     };


273 
274     /**
275      * A list of all cleanup actions associated with a resource scope. Cleanup actions are modelled as instances
276      * of the {@link ResourceCleanup} class, and, together, form a linked list. Depending on whether a scope
277      * is shared or confined, different implementations of this class will be used, see {@link ConfinedScope.ConfinedResourceList}
278      * and {@link SharedScope.SharedResourceList}.
279      */
280     public abstract static class ResourceList implements Runnable {
281         ResourceCleanup fst;
282 
283         abstract void add(ResourceCleanup cleanup);
284 
285         abstract void cleanup();
286 
287         public final void run() {
288             cleanup(); // cleaner interop
289         }
290 
291         static void cleanup(ResourceCleanup first) {
292             ResourceCleanup current = first;

 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 
 27 package jdk.internal.foreign;
 28 
 29 import jdk.incubator.foreign.MemorySegment;
 30 import jdk.incubator.foreign.ResourceScope;
 31 import jdk.incubator.foreign.SegmentAllocator;
 32 import jdk.internal.misc.ScopedMemoryAccess;
 33 import jdk.internal.vm.annotation.ForceInline;
 34 
 35 import java.lang.ref.Cleaner;
 36 import java.lang.ref.Reference;
 37 import java.util.Objects;
 38 
 39 /**
 40  * This class manages the temporal bounds associated with a memory segment as well
 41  * as thread confinement. A scope has a liveness bit, which is updated when the scope is closed
 42  * (this operation is triggered by {@link ResourceScope#close()}). This bit is consulted prior
 43  * to memory access (see {@link #checkValidState()}).
 44  * There are two kinds of memory scope: confined memory scope and shared memory scope.
 45  * A confined memory scope has an associated owner thread that confines some operations to
 46  * associated owner thread such as {@link #close()} or {@link #checkValidState()}.
 47  * Shared scopes do not feature an owner thread - meaning their operations can be called, in a racy
 48  * manner, by multiple threads. To guarantee temporal safety in the presence of concurrent thread,
 49  * shared scopes use a more sophisticated synchronization mechanism, which guarantees that no concurrent
 50  * access is possible when a scope is being closed (see {@link jdk.internal.misc.ScopedMemoryAccess}).
 51  */
 52 public abstract non-sealed class ResourceScopeImpl implements ResourceScope, SegmentAllocator, ScopedMemoryAccess.Scope {
 53 
 54     final ResourceList resourceList;
 55     final Cleaner.Cleanable cleanable;
 56 
 57     static final int MAX_FORKS = Integer.MAX_VALUE;
 58 
 59     @Override
 60     public void addCloseAction(Runnable runnable) {
 61         Objects.requireNonNull(runnable);
 62         addInternal(ResourceList.ResourceCleanup.ofRunnable(runnable));
 63     }
 64 





 65     /**
 66      * Add a cleanup action. If a failure occurred (because of a add vs. close race), call the cleanup action.
 67      * This semantics is useful when allocating new memory segments, since we first do a malloc/mmap and _then_
 68      * we register the cleanup (free/munmap) against the scope; so, if registration fails, we still have to
 69      * cleanup memory. From the perspective of the client, such a failure would manifest as a factory
 70      * returning a segment that is already "closed" - which is always possible anyway (e.g. if the scope
 71      * is closed _after_ the cleanup for the segment is registered but _before_ the factory returns the
 72      * new segment to the client). For this reason, it's not worth adding extra complexity to the segment
 73      * initialization logic here - and using an optimistic logic works well in practice.
 74      */
 75     public void addOrCleanupIfFail(ResourceList.ResourceCleanup resource) {
 76         try {
 77             addInternal(resource);
 78         } catch (Throwable ex) {
 79             resource.cleanup();
 80         }
 81     }
 82 
 83     void addInternal(ResourceList.ResourceCleanup resource) {
 84         try {
 85             checkValidStateSlow();
 86             resourceList.add(resource);
 87         } catch (ScopedMemoryAccess.Scope.ScopedAccessError err) {
 88             throw new IllegalStateException("Already closed");
 89         }
 90     }
 91 
 92     protected ResourceScopeImpl(ResourceList resourceList, Cleaner cleaner) {
 93         this.resourceList = resourceList;
 94         cleanable = (cleaner != null) ?
 95             cleaner.register(this, resourceList) : null;





 96     }
 97 
 98     public static ResourceScopeImpl createConfined(Thread thread, Cleaner cleaner) {
 99         return new ConfinedScope(thread, cleaner);
100     }
101 













102     public static ResourceScopeImpl createShared(Cleaner cleaner) {
103         return new SharedScope(cleaner);
104     }
105 












106     @Override
107     public MemorySegment allocate(long bytesSize, long bytesAlignment) {
108         return MemorySegment.allocateNative(bytesSize, bytesAlignment, this);





109     }
110 
111     public abstract void release0();






112 
113     public abstract void acquire0();

114 
115     @Override
116     public void keepAlive(ResourceScope target) {
117         if (target == this) {
118             throw new IllegalArgumentException("Invalid target scope.");
119         }
120         ResourceScopeImpl targetImpl = (ResourceScopeImpl)target;
121         targetImpl.acquire0();
122         addCloseAction(targetImpl::release0);
123     }
124 
125     /**
126      * Closes this scope, executing any cleanup action (where provided).
127      * @throws IllegalStateException if this scope is already closed or if this is
128      * a confined scope and this method is called outside of the owner thread.
129      */
130     public void close() {
131         try {
132             justClose();
133             if (cleanable != null) {
134                 cleanable.clean();
135             } else {
136                 resourceList.cleanup();
137             }
138         } finally {
139             Reference.reachabilityFence(this);
140         }
141     }
142 
143     abstract void justClose();
144 
145     /**
146      * Returns "owner" thread of this scope.
147      * @return owner thread (or null for a shared scope)
148      */
149     public abstract Thread ownerThread();
150 
151     /**
152      * Returns true, if this scope is still alive. This method may be called in any thread.
153      * @return {@code true} if this scope is not closed yet.
154      */
155     public abstract boolean isAlive();
156 
157 

165 
166     /**
167      * Checks that this scope is still alive (see {@link #isAlive()}).
168      * @throws IllegalStateException if this scope is already closed or if this is
169      * a confined scope and this method is called outside of the owner thread.
170      */
171     public final void checkValidStateSlow() {
172         if (ownerThread() != null && Thread.currentThread() != ownerThread()) {
173             throw new IllegalStateException("Attempted access outside owning thread");
174         } else if (!isAlive()) {
175             throw new IllegalStateException("Already closed");
176         }
177     }
178 
179     @Override
180     protected Object clone() throws CloneNotSupportedException {
181         throw new CloneNotSupportedException();
182     }
183 
184     /**
185      * The global, always alive, non-closeable, shared scope. Similar to a shared scope, but its {@link #close()} method throws unconditionally.
186      * Adding new resources to the global scope, does nothing: as the scope can never become not-alive, there is nothing to track.
187      * Acquiring and or releasing a resource scope similarly does nothing.








188      */
189     static class GlobalScopeImpl extends SharedScope {




190 
191         public GlobalScopeImpl() {
192             super(null);






193         }
194 
195         @Override
196         public void close() {
197             throw new UnsupportedOperationException("Scope cannot be closed");
198         }
199 
200         @Override
201         @ForceInline
202         public void release0() {
203             // do nothing
204         }
205 
206         @Override
207         @ForceInline
208         public void acquire0() {
209             // do nothing
210         }

211 






212         @Override
213         void addInternal(ResourceList.ResourceCleanup resource) {
214             // do nothing
215         }
216     }
217 
218     public static final ResourceScopeImpl GLOBAL = new GlobalScopeImpl();
219 
220     /**
221      * A list of all cleanup actions associated with a resource scope. Cleanup actions are modelled as instances
222      * of the {@link ResourceCleanup} class, and, together, form a linked list. Depending on whether a scope
223      * is shared or confined, different implementations of this class will be used, see {@link ConfinedScope.ConfinedResourceList}
224      * and {@link SharedScope.SharedResourceList}.
225      */
226     public abstract static class ResourceList implements Runnable {
227         ResourceCleanup fst;
228 
229         abstract void add(ResourceCleanup cleanup);
230 
231         abstract void cleanup();
232 
233         public final void run() {
234             cleanup(); // cleaner interop
235         }
236 
237         static void cleanup(ResourceCleanup first) {
238             ResourceCleanup current = first;
< prev index next >