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 */