/*
 * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package java.lang.foreign;

import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.ref.CleanerFactory;

import java.lang.foreign.MemorySegment.Scope;
import java.util.function.Consumer;

/**
 * An arena controls the lifecycle of native memory segments, providing both flexible
 * allocation and timely deallocation.
 * <p>
 * An arena has a {@linkplain MemorySegment.Scope scope} - the <em>arena scope</em>.
 * All the segments allocated by the arena are associated with the arena scope. As such,
 * the arena determines the temporal bounds of all the memory segments allocated by it.
 * <p>
 * Moreover, an arena also determines whether access to memory segments allocated by it
 * should be {@linkplain MemorySegment#isAccessibleBy(Thread) restricted} to specific
 * threads. An arena is a {@link SegmentAllocator} and features several allocation
 * methods that can be used by clients to obtain native segments.
 * <p>
 * The simplest arena is the {@linkplain Arena#global() global arena}. The global arena
 * features an <em>unbounded lifetime</em>. The scope of the global arena is the global
 * scope. As such, native segments allocated with the global arena are always accessible
 * and their backing regions of memory are never deallocated. Moreover, memory segments
 * allocated with the global arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed}
 * from any thread.
 * {@snippet lang = java:
 * MemorySegment segment = Arena.global().allocate(100, 1); // @highlight regex='global()'
 * ...
 * // segment is never deallocated!
 *}
 * <p>
 * Alternatively, clients can obtain an {@linkplain Arena#ofAuto() automatic arena}, that
 * is an arena which features a <em>bounded lifetime</em> that is managed, automatically,
 * by the garbage collector. The scope of an automatic arena is an automatic scope. As
 * such, the regions of memory backing memory segments allocated with the automatic arena
 * are deallocated at some unspecified time <em>after</em> the automatic arena (and all
 * the segments allocated by it) becomes {@linkplain java.lang.ref##reachability unreachable},
 * as shown below:
 * {@snippet lang = java:
 * MemorySegment segment = Arena.ofAuto().allocate(100, 1); // @highlight regex='ofAuto()'
 * ...
 * segment = null; // the segment region becomes available for deallocation after this point
 *}
 * Memory segments allocated with an automatic arena can also be
 * {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread.
 * <p>
 * Rather than leaving deallocation in the hands of the Java runtime, clients will often
 * wish to exercise control over the timing of deallocation for regions of memory that
 * back memory segments. Two kinds of arenas support this, namely {@linkplain #ofConfined() confined}
 * and {@linkplain #ofShared() shared} arenas. They both feature bounded lifetimes that
 * are managed manually. For instance, when a confined arena is {@linkplain #close() closed}
 * successfully, its scope is {@linkplain Scope#isAlive() invalidated}. As a result, all
 * the memory segments allocated by the arena can no longer be accessed, and their
 * regions of memory are deallocated:
 *
 * {@snippet lang = java:
 * MemorySegment segment = null;
 * try (Arena arena = Arena.ofConfined()) { // @highlight regex='ofConfined()'
 *     segment = arena.allocate(100);
 *     ...
 * } // segment region deallocated here
 * segment.get(ValueLayout.JAVA_BYTE, 0); // throws IllegalStateException
 *}
 *
 * Memory segments allocated with a {@linkplain #ofConfined() confined arena} can only be
 * accessed (and closed) by the thread that created the arena. If access to a memory
 * segment from multiple threads is required, clients can allocate segments in a
 * {@linkplain #ofShared() shared arena} instead.
 * <p>
 * The characteristics of the various arenas are summarized in the following table:
 *
 * <blockquote><table class="plain">
 * <caption style="display:none">Arenas characteristics</caption>
 * <thead>
 * <tr>
 *     <th scope="col">Kind</th>
 *     <th scope="col">Bounded lifetime</th>
 *     <th scope="col">Explicitly closeable</th>
 *     <th scope="col">Accessible from multiple threads</th>
 * </tr>
 * </thead>
 * <tbody>
 * <tr><th scope="row" style="font-weight:normal">Global</th>
 *     <td style="text-align:center;">No</td>
 *     <td style="text-align:center;">No</td>
 *     <td style="text-align:center;">Yes</td></tr>
 * <tr><th scope="row" style="font-weight:normal">Automatic</th>
 *     <td style="text-align:center;">Yes</td>
 *     <td style="text-align:center;">No</td>
 *     <td style="text-align:center;">Yes</td></tr>
 * <tr><th scope="row" style="font-weight:normal">Confined</th>
 *     <td style="text-align:center;">Yes</td>
 *     <td style="text-align:center;">Yes</td>
 *     <td style="text-align:center;">No</td></tr>
 * <tr><th scope="row" style="font-weight:normal">Shared</th>
 *     <td style="text-align:center;">Yes</td>
 *     <td style="text-align:center;">Yes</td>
 *     <td style="text-align:center;">Yes</td></tr>
 * </tbody>
 * </table></blockquote>
 *
 * <h2 id = "thread-confinement">Safety and thread-confinement</h2>
 *
 * Arenas provide strong temporal safety guarantees: a memory segment allocated by an
 * arena cannot be accessed <em>after</em> the arena has been closed. The cost of
 * providing this guarantee varies based on the number of threads that have access to the
 * memory segments allocated by the arena. For instance, if an arena is always created
 * and closed by one thread, and the memory segments allocated by the arena are always
 * accessed by that same thread, then ensuring correctness is trivial.
 * <p>
 * Conversely, if an arena allocates segments that can be accessed by multiple threads,
 * or if the arena can be closed by a thread other than the accessing thread, then
 * ensuring correctness is much more complex. For example, a segment allocated with the
 * arena might be accessed <em>while</em> another thread attempts, concurrently, to close
 * the arena. To provide the strong temporal safety guarantee without forcing every
 * client, even simple ones, to incur a performance impact, arenas are divided into
 * <em>thread-confined</em> arenas, and <em>shared</em> arenas.
 * <p>
 * Confined arenas, support strong thread-confinement guarantees. Upon creation, they are
 * assigned an <em>owner thread</em>, typically the thread which initiated the creation
 * operation. The segments created by a confined arena can only be
 * {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by the owner thread.
 * Moreover, any attempt to close the confined arena from a thread other than the owner
 * thread will fail with a {@link WrongThreadException}.
 * <p>
 * Shared arenas, on the other hand, have no owner thread. The segments created by a
 * shared arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by
 * any thread. This might be useful when multiple threads need to access the same memory
 * segment concurrently (e.g. in the case of parallel processing). Moreover, a shared
 * arena can be closed by any thread.
 *
 * <h2 id = "custom-arenas">Custom arenas</h2>
 *
 * Clients can define custom arenas to implement more efficient allocation strategies,
 * or to have better control over when (and by whom) an arena can be closed. As an
 * example, the following code defines a <em>slicing arena</em> that behaves like a
 * confined arena (i.e., single-threaded access), but internally uses a
 * {@linkplain SegmentAllocator#slicingAllocator(MemorySegment) slicing allocator} to
 * respond to allocation requests.
 * When the slicing arena is closed, the underlying confined arena is also closed; this
 * will invalidate all segments allocated with the slicing arena (since the scope of the
 * slicing arena is the same as that of the underlying confined arena):
 *
 * {@snippet lang = java:
 * class SlicingArena implements Arena {
 *     final Arena arena = Arena.ofConfined();
 *     final SegmentAllocator slicingAllocator;
 *
 *     SlicingArena(long size) {
 *         slicingAllocator = SegmentAllocator.slicingAllocator(arena.allocate(size));
 *     }
 *
 *     public MemorySegment allocate(long byteSize, long byteAlignment) {
 *         return slicingAllocator.allocate(byteSize, byteAlignment);
 *     }
 *
 *     public MemorySegment.Scope scope() {
 *         return arena.scope();
 *     }
 *
 *     public void close() {
 *         arena.close();
 *     }
 *
 * }
 * }
 *
 * In other words, a slicing arena provides a vastly more efficient and scalable
 * allocation strategy, while still retaining the timely deallocation guarantee provided
 * by the underlying confined arena:
 *
 * {@snippet lang = java:
 * try (Arena slicingArena = new SlicingArena(1000)) {
 *     for (int i = 0; i < 10; i++) {
 *         MemorySegment s = slicingArena.allocateFrom(JAVA_INT, 1, 2, 3, 4, 5);
 *         ...
 *     }
 * } // all memory allocated is released here
 * }
 *
 * @implSpec
 * Implementations of this interface are thread-safe.
 *
 * @see MemorySegment
 *
 * @since 22
 */
public interface Arena extends SegmentAllocator, AutoCloseable {

    /**
     * Creates a new arena that is managed, automatically, by the garbage collector.
     * Segments allocated with the returned arena can be
     * {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread.
     * Calling {@link #close()} on the returned arena will result in an {@link UnsupportedOperationException}.
     * <p>
     * Memory segments {@linkplain #allocate(long, long) allocated} by the returned arena
     * are zero-initialized.
     *
     * @return a new arena that is managed, automatically, by the garbage collector
     */
    static Arena ofAuto() {
        return MemorySessionImpl.createImplicit(CleanerFactory.cleaner()).asArena();
    }

    /**
     * {@return the global arena} Segments allocated with the global arena can be
     *          {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread.
     *          Calling {@link #close()} on the returned arena will result in
     *          an {@link UnsupportedOperationException}.
     * <p>
     * Memory segments {@linkplain #allocate(long, long) allocated} by the returned arena
     * are zero-initialized.
     */
    static Arena global() {
        class Holder {
            static final Arena GLOBAL = MemorySessionImpl.GLOBAL_SESSION.asArena();
        }
        return Holder.GLOBAL;
    }

    /**
     * {@return a new confined arena} Segments allocated with the confined arena can be
     *          {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by the thread
     *          that created the arena, the arena's <em>owner thread</em>.
     * <p>
     * Memory segments {@linkplain #allocate(long, long) allocated} by the returned arena
     * are zero-initialized.
     */
    static Arena ofConfined() {
        return MemorySessionImpl.createConfined(Thread.currentThread()).asArena();
    }

    /**
     * {@return a new shared arena} Segments allocated with the shared arena can be
     *          {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread.
     * <p>
     * Memory segments {@linkplain #allocate(long, long) allocated} by the returned arena
     * are zero-initialized.
     */
    static Arena ofShared() {
        return MemorySessionImpl.createShared().asArena();
    }

    /**
     * Returns a native memory segment with the given size (in bytes) and alignment
     * constraint (in bytes).
     * The returned segment is associated with this {@linkplain #scope() arena scope}.
     * The segment's {@link MemorySegment#address() address} is the starting address of
     * the allocated off-heap region of memory backing the segment, and the address is
     * aligned according the provided alignment constraint.
     *
     * @implSpec
     * Implementations of this method must return a native segment featuring the
     * requested size, and that is compatible with the provided alignment constraint.
     * Furthermore, for any two segments {@code S1, S2} returned by this method, the
     * following invariant must hold:
     *
     * {@snippet lang = java:
     *     S1.asOverlappingSlice(S2).isEmpty() == true
     * }
     *
     * @param byteSize the size (in bytes) of the off-heap region of memory backing
     *                 the native memory segment
     * @param byteAlignment the alignment constraint (in bytes) of the off-heap region
     *                      of memory backing the native memory segment
     * @return a new native memory segment
     * @throws IllegalArgumentException if {@code bytesSize < 0}, {@code byteAlignment <= 0},
     *         or if {@code byteAlignment} is not a power of 2
     * @throws IllegalStateException if this arena has already been {@linkplain #close() closed}
     * @throws WrongThreadException if this arena is confined, and this method is called
     *         from a thread other than the arena's owner thread
     */
    @Override
    MemorySegment allocate(long byteSize, long byteAlignment);

    /**
     * {@return the arena scope}
     */
    Scope scope();

    /**
     * Closes this arena. If this method completes normally, the arena scope is no longer
     * {@linkplain Scope#isAlive() alive}, and all the memory segments associated with it
     * can no longer be accessed. Furthermore, any off-heap region of memory backing the
     * segments obtained from this arena are also released.
     *
     * @apiNote This operation is not idempotent; that is, closing an already closed arena
     *          <em>always</em> results in an exception being thrown. This reflects a
     *          deliberate design choice: failure to close an arena might reveal a bug
     *          in the underlying application logic.
     *
     * @implSpec If this method completes normally, then
     *           {@code this.scope().isAlive() == false}.
     *           Implementations are allowed to throw {@link UnsupportedOperationException}
     *           if an explicit close operation is not supported.
     *
     * @see Scope#isAlive()
     *
     * @throws IllegalStateException if the arena has already been closed
     * @throws IllegalStateException if a segment associated with this arena is being
     *         accessed concurrently, e.g. by a
     *         {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handle}
     * @throws WrongThreadException if this arena is confined, and this method is called
     *         from a thread other than the arena's owner thread
     * @throws UnsupportedOperationException if this arena cannot be closed explicitly
     * @throws RuntimeException if an exception is thrown while executing a custom cleanup action
     *                          associated with this arena (e.g. as a result of calling
     *                          {@link MemorySegment#reinterpret(long, Arena, Consumer)} or
     *                          {@link MemorySegment#reinterpret(Arena, Consumer)}).
     */
    @Override
    void close();
}
