package com.atlassian.bitbucket.throttle;

import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;

/**
 * A ticket to access a {@link #getResourceName named} resource. When the resource is no longer needed, the ticket
 * holder should {@link #close() close} their ticket, allowing another request to acquire it. The preferred approach
 * for this is to use a try-with-resources block:
 * <pre><code>
 *     try (Ticket ticket = throttleService.acquireTicket("scm-command")) {
 *         //Do work, knowing the ticket will always be closed
 *     }</code></pre>
 * <p>
 * Note: The maximum duration for a ticket is scoped to the surrounding operation (where "operation" may be an HTTP
 * request, an executed task or some other arbitrary unit of work). The system <i>forcibly releases</i> any tickets
 * which are still held at the end of that bounding operation, where possible. This is done to help prevent resource
 * starvation due to "lost" tickets, which are never otherwise released by their holding code.
 * <p>
 * <b>Warning:</b> Tickets are not thread-safe, and should never be passed across threads. <i>Acquiring a ticket on one
 * thread and releasing it on another thread will fail.</i>
 * <p>
 * If a thread submits a {@code Runnable} on the {@code ExecutorService} exposed by the system or on any of the
 * delegating {@code ExecutorService}s created by SAL's {@code ThreadLocalDelegateExecutorFactory} while holding a ticket,
 * then the executor thread will implicitly inherit an internal "stub" of the calling thread's ticket. This "stub"
 * ticket will <i>act</i> like a real ticket for the purposes of subsequent calls to the {@link ThrottleService}
 * (preventing multiple tickets from being acquired in nested calls to {@link ThrottleService#acquireTicket(String)}
 * for example), but will not actually count towards the named resource and will not affect the calling thread's ticket
 * in any way when released. A thread created in any other way (for example, with
 * {@link java.util.concurrent.ThreadFactory} or by submitting a {@code Runnable} to a
 * {@link java.util.concurrent.Executor}) does <i>not</i> inherit any ticket state from the calling thread, and must
 * acquire its own ticket independently by calling {@link ThrottleService#acquireTicket(String)}) if it wants to
 * throttle access to the same resource. Any such tickets acquired in this way <i>must</i> be released with
 * {@link #close()} after the resource-intensive work has been done, to ensure no tickets are ever "lost".
 *
 * @see ThrottleService
 */
@NotThreadSafe
public interface Ticket extends AutoCloseable {

    /**
     * Releases the ticket, indicating the named resource is now available to service another request.
     * <p>
     * The system performs <i>best effort</i> detection of "lost" tickets and attempts to release them. However, it is
     * better for ticketholders to release their own tickets after the guarded operation has been performed. This both
     * reduces the duration during which the ticket is locked, allowing other code to acquire the ticket more readily,
     * and helps ensure all tickets are eventually released.
     */
    @Override
    void close();

    /**
     * Retrieves the name of the resource to which this ticket grants access.
     *
     * @return the resource name
     */
    @Nonnull
    String getResourceName();
}
