001/* 002 * ============================================================================ 003 * Copyright © 2002-2024 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.util; 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.util.internal.AutoSemaphoreImpl; 028import org.tquadrat.foundation.util.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 * <pre><code> 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 * </code></pre> 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 1136 2024-05-30 18:25:38Z tquadrat $ 060 * @since 0.4.8 061 * 062 * @UMLGraph.link 063 */ 064@ClassVersion( sourceVersion = "$Id: AutoSemaphore.java 1136 2024-05-30 18:25:38Z tquadrat $" ) 065@API( status = STABLE, since = "0.4.8" ) 066public sealed interface AutoSemaphore 067 permits AutoSemaphoreImpl, TimeoutSemaphoreImpl 068{ 069 /*---------*\ 070 ====** Methods **========================================================== 071 \*---------*/ 072 /** 073 * <p>{@summary Acquires a permit from this semaphore, blocking until one 074 * is available, or the thread is 075 * {@linkplain Thread#interrupt interrupted}, 076 * and returns a token object that is used for 077 * {@code try-with-resources}.}</p> 078 * <p>Acquires a permit, if one is available and returns immediately, 079 * reducing the number of available permits by one.</p> 080 * <p>If no permit is available then the current thread becomes disabled 081 * for thread scheduling purposes and lies dormant until one of two things 082 * happens:</p> 083 * <ul> 084 * <li>Some other thread invokes the 085 * {@link Semaphore#release() release()} 086 * method for this semaphore and the current thread is next to be 087 * assigned a permit; or</li> 088 * <li>Some other thread 089 * {@linkplain Thread#interrupt interrupts} 090 * the current thread.</li> 091 * </ul> 092 * <p>If the current thread: 093 * <ul> 094 * <li>has its interrupted status set on entry to this method; or</li> 095 * <li>is 096 * {@linkplain Thread#interrupt interrupted} 097 * while waiting for a permit, 098 * </ul> 099 * then 100 * {@link InterruptedException} 101 * is thrown and the current thread's interrupted status is cleared. 102 * 103 * @return The token. 104 * @throws InterruptedException The current thread was interrupted. 105 */ 106 public default AutoCloseable acquireToken() throws InterruptedException { return acquireToken( 1 ); } 107 108 /** 109 * <p>{@summary Acquires the given number of permits from this semaphore, 110 * blocking until all are available, or the thread is 111 * {@linkplain Thread#interrupt interrupted}, 112 * and returns a token object that is used for 113 * {@code try-with-resources}.}</p> 114 * <p>Acquires the given number of permits, if they are available, and 115 * returns immediately, reducing the number of available permits by the 116 * given amount. This method atomically acquires the permits all at 117 * once.</p> 118 * <p>If insufficient permits are available then the current thread 119 * becomes disabled for thread scheduling purposes and lies dormant until 120 * one of two things happens:</p> 121 * <ul> 122 * <li>Some other thread invokes one of the 123 * {@link Semaphore#release() release()} 124 * methods for this semaphore and the current thread is next to be 125 * assigned permits and the number of available permits satisfies 126 * this request; or</li> 127 * <li>Some other thread 128 * {@linkplain Thread#interrupt interrupts} 129 * the current thread.</li> 130 * </ul> 131 * <p>If the current thread: 132 * <ul> 133 * <li>has its interrupted status set on entry to this method; or</li> 134 * <li>is 135 * {@linkplain Thread#interrupt interrupted} 136 * while waiting for a permit, 137 * </ul> 138 * <p>then an 139 * {@link InterruptedException} 140 * is thrown and the current thread's interrupted status is cleared. Any 141 * permits that were to be assigned to this thread are instead assigned to 142 * other threads trying to acquire permits, as if permits had been made 143 * available by a call to 144 * {@link Semaphore#release() release()}.</p> 145 * 146 * @param permits The number of permits to acquire. 147 * @return The token. 148 * @throws InterruptedException The current thread is interrupted. 149 * @throws IllegalArgumentException The given number of permits to 150 * acquire is negative. 151 */ 152 public AutoCloseable acquireToken( final int permits ) throws InterruptedException, IllegalArgumentException; 153 154 /** 155 * <p>{@summary Acquires a permit from this semaphore, blocking until one 156 * is available, and returns a token object that is used for 157 * {@code try-with-resources}.}</p> 158 * <p>Acquires a permit, if one is available and returns immediately, 159 * reducing the number of available permits by one.</p> 160 * <p>If no permit is available then the current thread becomes disabled 161 * for thread scheduling purposes and lies dormant until some other thread 162 * invokes the 163 * {@link Semaphore#release() release()} 164 * method for this semaphore and the current thread is next to be assigned 165 * a permit.</p> 166 * <p>If the current thread is 167 * {@linkplain Thread#interrupt interrupted} 168 * while waiting for a permit then it will continue to wait, but the time 169 * at which the thread is assigned a permit may change compared to the 170 * time it would have received the permit had no interruption occurred. 171 * When the thread does return from this method its interrupt status will 172 * be set.</p> 173 * 174 * @return The token. 175 */ 176 public default AutoCloseable acquireTokenUninterruptibly() { return acquireTokenUninterruptibly( 1 ); } 177 178 /** 179 * <p>{@summary Acquires the given number of permits from this semaphore, 180 * blocking until all are available, and returns a token object that is 181 * used for {@code try-with-resources}.}</p> 182 * <p>Acquires the given number of permits, if they are available, and 183 * returns immediately, reducing the number of available permits by the 184 * given amount. This method atomically acquires the permits all at 185 * once.</p> 186 * <p>If insufficient permits are available then the current thread 187 * becomes disabled for thread scheduling purposes and lies dormant until 188 * some other thread invokes one of the 189 * {@link Semaphore#release() release()} 190 * methods for this semaphore and the current thread is next to be 191 * assigned permits and the number of available permits satisfies this 192 * request.</p> 193 * <p>If the current thread is 194 * {@linkplain Thread#interrupt interrupted} 195 * while waiting for permits then it will continue to wait and its 196 * position in the queue is not affected. When the thread does return 197 * from this method its interrupt status will be set. 198 * 199 * @param permits The number of permits to acquire. 200 * @return The token. 201 * @throws IllegalArgumentException The given number of permits to 202 * acquire is negative. 203 */ 204 public AutoCloseable acquireTokenUninterruptibly( final int permits ) throws IllegalArgumentException; 205 206 /** 207 * Returns a reference to the raw semaphore. 208 * 209 * @return The semaphore. 210 */ 211 public Semaphore getSemaphore(); 212 213 /** 214 * Creates an {@code AutoSemaphore} instance with the given number of 215 * permits and non-fair fairness setting. 216 * 217 * @param permits The initial number of permits available. This value may 218 * be negative, in which case releases must occur before any acquires 219 * will be granted. 220 * @return The new {@code AutoSemaphore} instance. 221 */ 222 public static AutoSemaphore of( final int permits ) { return new AutoSemaphoreImpl( permits ); } 223 224 /** 225 * Creates an {@code AutoSemaphore} instance with the given number of 226 * permits and the given fairness setting. 227 * 228 * @param permits The initial number of permits available. This value may 229 * be negative, in which case releases must occur before any acquires 230 * will be granted. 231 * @param fair {@code true} if this semaphore will guarantee first-in 232 * first-out granting of permits under contention, else {@code false}. 233 * @return The new {@code AutoSemaphore} instance. 234 */ 235 public static AutoSemaphore of( final int permits, final boolean fair ) { return new AutoSemaphoreImpl( permits, fair ); } 236 237 /** 238 * Creates an {@code AutoSemaphore} instance with the given number of 239 * permits and non-fair fairness setting. 240 * 241 * @param permits The initial number of permits available. This value may 242 * be negative, in which case releases must occur before any acquires 243 * will be granted. 244 * @param duration The timeout for a permit; after the given period of 245 * time, an acquired permit will be released automatically. 246 * @return The new {@code AutoSemaphore} instance. 247 */ 248 public static AutoSemaphore of( final int permits, final Duration duration ) { return new TimeoutSemaphoreImpl( permits, duration ); } 249 250 /** 251 * Creates an {@code AutoSemaphore} instance with the given number of 252 * permits and the given 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 * @param fair {@code true} if this semaphore will guarantee first-in 258 * first-out granting of permits under contention, else {@code false}. 259 * @param duration The timeout for a permit; after the given period of 260 * time, an acquired permit will be released automatically. 261 * @return The new {@code AutoSemaphore} instance. 262 */ 263 public static AutoSemaphore of( final int permits, final boolean fair, final Duration duration ) { return new TimeoutSemaphoreImpl( permits, fair, duration ); } 264} 265// class AutoSemaphore 266 267/* 268 * End of File 269 */