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.lang.internal; 019 020import static java.lang.Thread.MAX_PRIORITY; 021import static java.lang.Thread.MIN_PRIORITY; 022import static java.lang.Thread.UncaughtExceptionHandler; 023import static java.lang.Thread.ofPlatform; 024import static java.lang.Thread.ofVirtual; 025import static org.apiguardian.api.API.Status.INTERNAL; 026import static org.tquadrat.foundation.lang.Objects.nonNull; 027import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 028 029import java.util.StringJoiner; 030import java.util.concurrent.ThreadFactory; 031import java.util.concurrent.atomic.AtomicInteger; 032import java.util.function.IntFunction; 033 034import org.apiguardian.api.API; 035import org.tquadrat.foundation.annotation.ClassVersion; 036import org.tquadrat.foundation.exception.ValidationException; 037import org.tquadrat.foundation.lang.Objects; 038import org.tquadrat.foundation.lang.ThreadFactoryBuilder; 039 040/** 041 * The implementation of 042 * {@link ThreadFactoryBuilder}. 043 * 044 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 045 * @version $Id: ThreadFactoryBuilderImpl.java 1118 2024-03-15 16:14:15Z tquadrat $ 046 * @since 0.1.0 047 * 048 * @UMLGraph.link 049 */ 050@ClassVersion( sourceVersion = "$Id: ThreadFactoryBuilderImpl.java 1118 2024-03-15 16:14:15Z tquadrat $" ) 051@API( status = INTERNAL, since = "0.1.0" ) 052public final class ThreadFactoryBuilderImpl implements ThreadFactoryBuilder 053{ 054 /*---------------*\ 055 ====** Inner Classes **==================================================== 056 \*---------------*/ 057 /** 058 * The implementation of 059 * {@link ThreadFactory} 060 * that is returned by 061 * {@link ThreadFactoryBuilder#build()}. 062 * 063 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 064 * @version $Id: ThreadFactoryBuilderImpl.java 1118 2024-03-15 16:14:15Z tquadrat $ 065 * @since 0.1.0 066 * 067 * @UMLGraph.link 068 */ 069 @ClassVersion( sourceVersion = "$Id: ThreadFactoryBuilderImpl.java 1118 2024-03-15 16:14:15Z tquadrat $" ) 070 @API( status = INTERNAL, since = "0.1.0" ) 071 private static final class ThreadFactoryImpl implements ThreadFactory 072 { 073 /*------------*\ 074 ====** Attributes **=================================================== 075 \*------------*/ 076 /** 077 * The thread builder. 078 */ 079 private final Thread.Builder m_Builder; 080 081 /** 082 * The context 083 * {@link ClassLoader} 084 * for the new threads. Can be {@code null}. 085 */ 086 private final ClassLoader m_ContextClassLoader; 087 088 /** 089 * The counter for the thread name. 090 */ 091 private final AtomicInteger m_NameCounter = new AtomicInteger( 0 ); 092 093 /** 094 * The factory method for the thread names. 095 */ 096 private final IntFunction<String> m_NameFactory; 097 098 /*--------------*\ 099 ====** Constructors **================================================= 100 \*--------------*/ 101 /** 102 * Creates a new instance of {@code ThreadFactoryImpl}. 103 * 104 * @param nameFactory The factory method for the thread names. 105 * @param threadGroup The thread group for the new thread; can be 106 * {@code null}. 107 * @param stackSize The desired stack size for the new threads, or 108 * zero to indicate that this parameter is to be ignored. 109 * @param inheritThreadLocals If {@code true}, inherit initial values 110 * for inheritable thread-locals from the constructing thread, 111 * otherwise no initial values are inherited. 112 * @param contextClassLoader The context class loader for the new 113 * threads; can be {@code null}. 114 * @param isDaemon {@code true} if the new threads are daemon 115 * threads, {@code false} otherwise. 116 * @param priority The priority for the new threads. A value of -1 117 * indicates that no priority will be set explicitly. 118 * @param uncaughtExceptionHandler The handler for uncaught 119 * exceptions for the new threads; can be {@code null}. 120 * @param isVirtual {@code true} if this factory creates virtual 121 * threads, {@code false} if it creates platform threads. 122 */ 123 @SuppressWarnings( {"BooleanParameter", "ConstructorWithTooManyParameters"} ) 124 public ThreadFactoryImpl( final IntFunction<String> nameFactory, final ThreadGroup threadGroup, final long stackSize, final boolean inheritThreadLocals, final ClassLoader contextClassLoader, final boolean isDaemon, final int priority, final UncaughtExceptionHandler uncaughtExceptionHandler, final boolean isVirtual ) 125 { 126 m_NameFactory = requireNonNullArgument( nameFactory, "nameFactory" ); 127 m_ContextClassLoader = contextClassLoader; 128 129 m_Builder = isVirtual ? ofVirtual() : ofPlatform(); 130 m_Builder.uncaughtExceptionHandler( uncaughtExceptionHandler ) 131 .inheritInheritableThreadLocals( inheritThreadLocals ); 132 133 if( !isVirtual ) 134 { 135 final var builder = (Thread.Builder.OfPlatform) m_Builder; 136 builder.daemon( isDaemon ) 137 .stackSize( stackSize ); 138 if( nonNull( threadGroup ) ) builder.group( threadGroup ); 139 if( (MIN_PRIORITY <= priority) && (priority <= MAX_PRIORITY) ) builder.priority( priority ); 140 } 141 } // ThreadFactoryImpl() 142 143 /*---------*\ 144 ====** Methods **====================================================== 145 \*---------*/ 146 /** 147 * Returns the name for the new thread. 148 * 149 * @return The new thread's name. 150 */ 151 private final String generateName() { return m_NameFactory.apply( m_NameCounter.incrementAndGet() ); } 152 153 /** 154 * {@inheritDoc} 155 */ 156 @Override 157 public final Thread newThread( final Runnable target ) 158 { 159 m_Builder.name( generateName() ); 160 161 final var retValue = m_Builder.unstarted( target ); 162 if( nonNull( m_ContextClassLoader ) ) retValue.setContextClassLoader( m_ContextClassLoader ); 163 164 //---* Done *------------------------------------------------------ 165 return retValue; 166 } // newThread() 167 } 168 // class ThreadFactoryImpl 169 170 /*------------*\ 171 ====** Attributes **======================================================= 172 \*------------*/ 173 /** 174 * The context 175 * {@link ClassLoader} 176 * for the new threads. The default is {@code null}. 177 */ 178 private ClassLoader m_ContextClassLoader = null; 179 180 /** 181 * If {@code true}, inherit initial values for inheritable 182 * thread-locals from the constructing thread, otherwise no initial 183 * values are inherited. The default is {@code true}. 184 */ 185 private boolean m_InheritThreadLocals = true; 186 187 /** 188 * {@code true} if the new threads are daemon thread, {@code false} 189 * otherwise. The default is {@code false}. 190 */ 191 private boolean m_IsDaemon = false; 192 193 /** 194 * Flag that determines whether this thread factory creates virtual 195 * threads. 196 * 197 * @since 0.4.5 198 */ 199 private boolean m_IsVirtual = false; 200 201 /** 202 * <p>{@summary The factory method for the thread names.} The default 203 * returns "{@code Thread-#}", where "#" stands for a 204 * counter.</p> 205 */ 206 private IntFunction<String> m_NameFactory = "Thread-%d"::formatted; 207 208 /** 209 * <p>{@summary The priority for the new threads.} The default value of -1 210 * indicates that no priority will be set explicitly.</p> 211 */ 212 private int m_Priority = -1; 213 214 /** 215 * The desired stack size for the new threads. The default value of zero 216 * indicates that this parameter is to be ignored. 217 */ 218 private long m_StackSize = 0; 219 220 /** 221 * The thread group for the new threads. The default is {@code null}. 222 */ 223 private ThreadGroup m_ThreadGroup = null; 224 225 /** 226 * The uncaught exception handler for the new threads. The default is 227 * {@code null}. 228 */ 229 private UncaughtExceptionHandler m_UncaughtExceptionHandler = null; 230 231 /*--------------*\ 232 ====** Constructors **===================================================== 233 \*--------------*/ 234 /** 235 * Creates a new instance of {@code ThreadFactoryBuilderImpl}. 236 */ 237 public ThreadFactoryBuilderImpl() { /* Just exists */ } 238 239 /*---------*\ 240 ====** Methods **========================================================== 241 \*---------*/ 242 /** 243 * {@inheritDoc} 244 */ 245 @Override 246 public final ThreadFactory build() 247 { 248 final var retValue = new ThreadFactoryImpl( m_NameFactory, m_ThreadGroup, m_StackSize, m_InheritThreadLocals, m_ContextClassLoader, m_IsDaemon, m_Priority, m_UncaughtExceptionHandler, m_IsVirtual ); 249 250 //---* Done *---------------------------------------------------------- 251 return retValue; 252 } // build() 253 254 /** 255 * {@inheritDoc} 256 */ 257 @Override 258 public final ThreadFactoryBuilderImpl setContextClassLoader( final ClassLoader contextClassLoader ) 259 { 260 m_ContextClassLoader = requireNonNullArgument( contextClassLoader, "contextClassLoader" ); 261 262 //---* Done *---------------------------------------------------------- 263 return this; 264 } // setContextClassLoader() 265 266 /** 267 * {@inheritDoc} 268 */ 269 @Override 270 public final ThreadFactoryBuilderImpl setInheritThreadLocals( final boolean flag ) 271 { 272 m_InheritThreadLocals = flag; 273 274 //---* Done *---------------------------------------------------------- 275 return this; 276 } // setInheritThreadLocals() 277 278 /** 279 * {@inheritDoc} 280 */ 281 @Override 282 public final ThreadFactoryBuilderImpl setDaemon( final boolean flag ) 283 { 284 m_IsDaemon = flag; 285 286 //---* Done *---------------------------------------------------------- 287 return this; 288 } // setDaemon() 289 290 /** 291 * {@inheritDoc} 292 */ 293 @Override 294 public final ThreadFactoryBuilderImpl setNameFactory( final IntFunction<String> nameFactory ) 295 { 296 m_NameFactory = requireNonNullArgument( nameFactory, "nameFactory" ); 297 298 //---* Done *---------------------------------------------------------- 299 return this; 300 } // setNameFactory() 301 302 /** 303 * {@inheritDoc} 304 */ 305 @Override 306 public final ThreadFactoryBuilderImpl setPriority( final int priority ) throws ValidationException 307 { 308 if( (priority != -1) && !((MIN_PRIORITY <= priority) && (priority <= MAX_PRIORITY)) ) 309 { 310 throw new ValidationException( "Priority value '%3$d' is invalid; it has to be -1, or in the range from %1$d to %2$d, included" 311 .formatted( MIN_PRIORITY, MAX_PRIORITY, priority ) ); 312 } 313 m_Priority = priority; 314 315 //---* Done *---------------------------------------------------------- 316 return this; 317 } // setPriority() 318 319 /** 320 * {@inheritDoc} 321 */ 322 @Override 323 public final ThreadFactoryBuilderImpl setStackSize( final long stackSize ) 324 { 325 if( stackSize < 0 ) throw new ValidationException( "stackSize is less than zero" ); 326 m_StackSize = stackSize; 327 328 //---* Done *---------------------------------------------------------- 329 return this; 330 } // setStackSize() 331 332 /** 333 * {@inheritDoc} 334 */ 335 @Override 336 public final ThreadFactoryBuilderImpl setThreadGroup( final ThreadGroup threadGroup ) 337 { 338 m_ThreadGroup = requireNonNullArgument( threadGroup, "threadGroup" ); 339 340 //---* Done *---------------------------------------------------------- 341 return this; 342 } // setThreadGroup() 343 344 /** 345 * {@inheritDoc} 346 */ 347 @Override 348 public final ThreadFactoryBuilderImpl setUncaughtExceptionHandler( final UncaughtExceptionHandler uncaughtExceptionHandler ) 349 { 350 m_UncaughtExceptionHandler = requireNonNullArgument( uncaughtExceptionHandler, "uncaughtExceptionHandler" ); 351 352 //---* Done *---------------------------------------------------------- 353 return this; 354 } // setUncaughtExceptionHandler() 355 356 /** 357 * {@inheritDoc} 358 */ 359 @API( status = INTERNAL, since = "0.4.5" ) 360 @Override 361 public final ThreadFactoryBuilderImpl setVirtual( final boolean flag ) 362 { 363 m_IsVirtual = flag; 364 365 //---* Done *---------------------------------------------------------- 366 return this; 367 } // setVirtual() 368 369 /** 370 * {@inheritDoc} 371 */ 372 @Override 373 public final String toString() 374 { 375 final var buffer = new StringJoiner( ", ", "%s[".formatted( getClass().getName() ), "]" ) 376 .add( "ContextClassLoader='%s'".formatted( Objects.toString( m_ContextClassLoader ) ) ) 377 .add( "InheritThreadLocals=%b".formatted( m_InheritThreadLocals ) ) 378 .add( "IsDaemon=%b".formatted( m_IsDaemon ) ) 379 .add( "NameFactory='%s' (returns \"%s\")".formatted( Objects.toString( m_NameFactory ), m_NameFactory.apply( 1 ) ) ) 380 .add( "Priority=%d".formatted( m_Priority ) ) 381 .add( "StackSize=%d byte".formatted( m_StackSize ) ) 382 .add( "ThreadGroup='%s' (name: %s)".formatted( Objects.toString( m_ThreadGroup ), nonNull( m_ThreadGroup ) ? m_ThreadGroup.getName() : "n/a" ) ) 383 .add( "UncaughtExceptionHandler='%s'".formatted( Objects.toString( m_UncaughtExceptionHandler ) ) ); 384 385 //---* Compose the return value *-------------------------------------- 386 final var retValue = buffer.toString(); 387 388 //---* Done *---------------------------------------------------------- 389 return retValue; 390 } // toString() 391} 392// class ThreadFactoryBuilderImpl 393 394/* 395 * End of File 396 */