001/*
002 * ============================================================================
003 *  Copyright © 2002-2026 by Thomas Thrien.
004 *  All Rights Reserved.
005 * ============================================================================
006 *  Licensed to the public under the agreements of the GNU Lesser General Public
007 *  License, version 3.0 (the "License"). You may obtain a copy of the License at
008 *
009 *       http://www.gnu.org/licenses/lgpl.html
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014 *  License for the specific language governing permissions and limitations
015 *  under the License.
016 */
017
018package org.tquadrat.foundation.lang;
019
020import static org.apiguardian.api.API.Status.STABLE;
021
022import java.time.Duration;
023import java.util.concurrent.Semaphore;
024
025import org.apiguardian.api.API;
026import org.tquadrat.foundation.annotation.ClassVersion;
027import org.tquadrat.foundation.lang.internal.AutoSemaphoreImpl;
028import org.tquadrat.foundation.lang.internal.TimeoutSemaphoreImpl;
029
030/**
031 *  <p>{@summary An implementation of
032 *  {@link Semaphore}
033 *  that allows to be used with {@code try-with-resources}.}</p>
034 *  <p>Use this class like this:</p>
035 *  <div class="source-container"><pre>
036 *  final static final int MAX_AVAILABLE = …
037 *  final AutoSemaphore semaphore = AutoSemaphore.of( MAX_AVAILABLE );
038 *  …
039 *  try( final var token = semaphore.acquireToken() )
040 *  {
041 *      // Do something …
042 *  }
043 *  catch( final InterruptedException e )
044 *  {
045 *      // Handle the exception …
046 *  }
047 *  </pre></div>
048 *  <p>A call to
049 *  {@link #of(int,Duration)}
050 *  or
051 *  {@link #of(int,boolean,Duration)}
052 *  creates a {@code AutoSemaphore} instance whose permits will be released
053 *  automatically after the given period of time. Do not acquire permits from
054 *  the instance returned by a call to
055 *  {@link #getSemaphore()}
056 *  on such an instance, as it may behave unexpectedly.</p>
057 *
058 *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
059 *  @version $Id: AutoSemaphore.java 1258 2026-06-04 18:33:06Z tquadrat $
060 *  @since 0.25.2
061 *
062 *  @UMLGraph.link
063 */
064@ClassVersion( sourceVersion = "$Id: AutoSemaphore.java 1258 2026-06-04 18:33:06Z tquadrat $" )
065@API( status = STABLE, since = "0.25.2" )
066public sealed interface AutoSemaphore
067    permits AutoSemaphoreImpl, TimeoutSemaphoreImpl
068{
069        /*---------------*\
070    ====** Inner Classes **====================================================
071        \*---------------*/
072    /**
073     *  <p>{@summary The definition of a token that holds the permits to be
074     *  released when a {@code try-with-resources} block is left.}</p>
075     *
076     *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
077     *  @version $Id: AutoSemaphore.java 1258 2026-06-04 18:33:06Z tquadrat $
078     *  @since 0.25.2
079     *
080     *  @UMLGraph.link
081     */
082    @SuppressWarnings( "NewClassNamingConvention" )
083    @ClassVersion( sourceVersion = "$Id: AutoSemaphore.java 1258 2026-06-04 18:33:06Z tquadrat $" )
084    @API( status = STABLE, since = "0.25.2" )
085    public sealed interface Token extends AutoCloseable
086        permits AutoSemaphoreImpl.TokenImpl, TimeoutSemaphoreImpl.TokenImpl
087    {
088            /*---------*\
089        ====** Methods **======================================================
090            \*---------*/
091        /**
092         *  {@inheritDoc}
093         */
094        @Override
095        public void close();
096
097        /**
098         *  Returns the semaphore that is connected to this token instance.
099         *
100         *  @return The semaphore.
101         */
102        public Semaphore getSemaphore();
103    }
104    //  interface Token
105
106        /*---------*\
107    ====** Methods **==========================================================
108        \*---------*/
109    /**
110     *  <p>{@summary Acquires a permit from this semaphore, blocking until one
111     *  is available, or the thread is
112     *  {@linkplain Thread#interrupt interrupted},
113     *  and returns a token object that is used for
114     *  {@code try-with-resources}.}</p>
115     *  <p>Acquires a permit, if one is available and returns immediately,
116     *  reducing the number of available permits by one.</p>
117     *  <p>If no permit is available then the current thread becomes disabled
118     *  for thread scheduling purposes and lies dormant until one of two things
119     *  happens:</p>
120     *  <ul>
121     *    <li>Some other thread invokes the
122     *    {@link Semaphore#release() release()}
123     *    method for this semaphore and the current thread is next to be
124     *    assigned a permit; or</li>
125     *    <li>Some other thread
126     *    {@linkplain Thread#interrupt interrupts}
127     *    the current thread.</li>
128     *  </ul>
129     *  <p>If the current thread:
130     *  <ul>
131     *    <li>has its interrupted status set on entry to this method; or</li>
132     *    <li>is
133     *    {@linkplain Thread#interrupt interrupted}
134     *    while waiting for a permit,
135     *  </ul>
136     *  then
137     *  {@link InterruptedException}
138     *  is thrown and the current thread's interrupted status is cleared.
139     *
140     *  @return The token.
141     *  @throws InterruptedException    The current thread was interrupted.
142     */
143    public default Token acquireToken() throws InterruptedException { return acquireToken( 1 ); }
144
145    /**
146     *  <p>{@summary Acquires the given number of permits from this semaphore,
147     *  blocking until all are available, or the thread is
148     *  {@linkplain Thread#interrupt interrupted},
149     *  and returns a token object that is used for
150     *  {@code try-with-resources}.}</p>
151     *   <p>Acquires the given number of permits, if they are available, and
152     *   returns immediately, reducing the number of available permits by the
153     *   given amount. This method atomically acquires the permits all at
154     *   once.</p>
155     *   <p>If insufficient permits are available then the current thread
156     *   becomes disabled for thread scheduling purposes and lies dormant until
157     *   one of two things happens:</p>
158     *   <ul>
159     *       <li>Some other thread invokes one of the
160     *       {@link Semaphore#release() release()}
161     *       methods for this semaphore and the current thread is next to be
162     *       assigned permits and the number of available permits satisfies
163     *       this request; or</li>
164     *       <li>Some other thread
165     *       {@linkplain Thread#interrupt interrupts}
166     *       the current thread.</li>
167     *  </ul>
168     *  <p>If the current thread:
169     *  <ul>
170     *      <li>has its interrupted status set on entry to this method; or</li>
171     *      <li>is
172     *      {@linkplain Thread#interrupt interrupted}
173     *      while waiting for a permit,
174     *  </ul>
175     *  <p>then an
176     *  {@link InterruptedException}
177     *  is thrown and the current thread's interrupted status is cleared. Any
178     *  permits that were to be assigned to this thread are instead assigned to
179     *  other threads trying to acquire permits, as if permits had been made
180     *  available by a call to
181     *  {@link Semaphore#release() release()}.</p>
182     *
183     *  @param  permits The number of permits to acquire.
184     *  @return The token.
185     *  @throws InterruptedException    The current thread is interrupted.
186     *  @throws IllegalArgumentException    The given number of permits to
187     *      acquire is negative.
188     */
189    public Token acquireToken( final int permits ) throws InterruptedException, IllegalArgumentException;
190
191    /**
192     *  <p>{@summary Acquires a permit from this semaphore, blocking until one
193     *  is available, and returns a token object that is used for
194     *  {@code try-with-resources}.}</p>
195     *  <p>Acquires a permit, if one is available and returns immediately,
196     *  reducing the number of available permits by one.</p>
197     *  <p>If no permit is available then the current thread becomes disabled
198     *  for thread scheduling purposes and lies dormant until some other thread
199     *  invokes the
200     *  {@link Semaphore#release() release()}
201     *  method for this semaphore and the current thread is next to be assigned
202     *  a permit.</p>
203     *  <p>If the current thread is
204     *  {@linkplain Thread#interrupt interrupted}
205     *  while waiting for a permit then it will continue to wait, but the time
206     *  at which the thread is assigned a permit may change compared to the
207     *  time it would have received the permit had no interruption occurred.
208     *  When the thread does return from this method its interrupt status will
209     *  be set.</p>
210     *
211     *  @return The token.
212     */
213    public default Token acquireTokenUninterruptibly() { return acquireTokenUninterruptibly( 1 ); }
214
215    /**
216     *  <p>{@summary Acquires the given number of permits from this semaphore,
217     *  blocking until all are available, and returns a token object that is
218     *  used for {@code try-with-resources}.}</p>
219     *  <p>Acquires the given number of permits, if they are available, and
220     *  returns immediately, reducing the number of available permits by the
221     *  given amount. This method atomically acquires the permits all at
222     *  once.</p>
223     *  <p>If insufficient permits are available then the current thread
224     *  becomes disabled for thread scheduling purposes and lies dormant until
225     *  some other thread invokes one of the
226     *  {@link Semaphore#release() release()}
227     *  methods for this semaphore and the current thread is next to be
228     *  assigned permits and the number of available permits satisfies this
229     *  request.</p>
230     *  <p>If the current thread is
231     *  {@linkplain Thread#interrupt interrupted}
232     *  while waiting for permits then it will continue to wait and its
233     *  position in the queue is not affected.  When the thread does return
234     *  from this method its interrupt status will be set.
235     *
236     *  @param  permits The number of permits to acquire.
237     *  @return The token.
238     *  @throws IllegalArgumentException    The given number of permits to
239     *      acquire is negative.
240     */
241    public Token acquireTokenUninterruptibly( final int permits ) throws IllegalArgumentException;
242
243    /**
244     *  Returns a reference to the raw semaphore.
245     *
246     *  @return The semaphore.
247     */
248    public Semaphore getSemaphore();
249
250    /**
251     *  Creates an {@code AutoSemaphore} instance with the given number of
252     *  permits and non-fair fairness setting.
253     *
254     *  @param  permits The initial number of permits available. This value may
255     *      be negative, in which case releases must occur before any acquires
256     *      will be granted.
257     *  @return The new {@code AutoSemaphore} instance.
258     */
259    public static AutoSemaphore of( final int permits ) { return new AutoSemaphoreImpl( permits ); }
260
261    /**
262     *  Creates an {@code AutoSemaphore} instance with the given number of
263     *  permits and the given fairness setting.
264     *
265     *  @param  permits The initial number of permits available. This value may
266     *      be negative, in which case releases must occur before any acquires
267     *      will be granted.
268     *  @param  fair    {@true} if this semaphore will guarantee first-in
269     *      first-out granting of permits under contention, else {@false}.
270     *  @return The new {@code AutoSemaphore} instance.
271     */
272    public static AutoSemaphore of( final int permits, final boolean fair ) { return new AutoSemaphoreImpl( permits, fair ); }
273
274    /**
275     *  Creates an {@code AutoSemaphore} instance with the given number of
276     *  permits and non-fair fairness setting.
277     *
278     *  @param  permits The initial number of permits available. This value may
279     *      be negative, in which case releases must occur before any acquires
280     *      will be granted.
281     *  @param  duration    The timeout for a permit; after the given period of
282     *      time, an acquired permit will be released automatically.
283     *  @return The new {@code AutoSemaphore} instance.
284     */
285    public static AutoSemaphore of( final int permits, final Duration duration ) { return new TimeoutSemaphoreImpl( permits, duration ); }
286
287    /**
288     *  Creates an {@code AutoSemaphore} instance with the given number of
289     *  permits and the given fairness setting.
290     *
291     *  @param  permits The initial number of permits available. This value may
292     *      be negative, in which case releases must occur before any acquires
293     *      will be granted.
294     *  @param  fair    {@true} if this semaphore will guarantee first-in
295     *      first-out granting of permits under contention, else {@false}.
296     *  @param  duration    The timeout for a permit; after the given period of
297     *      time, an acquired permit will be released automatically.
298     *  @return The new {@code AutoSemaphore} instance.
299     */
300    public static AutoSemaphore of( final int permits, final boolean fair, final Duration duration ) { return new TimeoutSemaphoreImpl( permits, fair, duration ); }
301}
302//  class AutoSemaphore
303
304/*
305 *  End of File
306 */