1 /* 2 * Hunt - A refined core library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.concurrency.thread.LockSupport; 13 14 import core.atomic; 15 import core.thread; 16 import core.time; 17 18 import hunt.concurrency.thread.ThreadEx; 19 import hunt.Exceptions; 20 import hunt.logging; 21 import hunt.util.DateTime; 22 23 24 /** 25 * Basic thread blocking primitives for creating locks and other 26 * synchronization classes. 27 * 28 * <p>This class associates, with each thread that uses it, a permit 29 * (in the sense of the {@link java.util.concurrent.Semaphore 30 * Semaphore} class). A call to {@code park} will return immediately 31 * if the permit is available, consuming it in the process; otherwise 32 * it <em>may</em> block. A call to {@code unpark} makes the permit 33 * available, if it was not already available. (Unlike with Semaphores 34 * though, permits do not accumulate. There is at most one.) 35 * Reliable usage requires the use of volatile (or atomic) variables 36 * to control when to park or unpark. Orderings of calls to these 37 * methods are maintained with respect to volatile variable accesses, 38 * but not necessarily non-volatile variable accesses. 39 * 40 * <p>Methods {@code park} and {@code unpark} provide efficient 41 * means of blocking and unblocking threads that do not encounter the 42 * problems that cause the deprecated methods {@code Thread.suspend} 43 * and {@code Thread.resume} to be unusable for such purposes: Races 44 * between one thread invoking {@code park} and another thread trying 45 * to {@code unpark} it will preserve liveness, due to the 46 * permit. Additionally, {@code park} will return if the caller's 47 * thread was interrupted, and timeout versions are supported. The 48 * {@code park} method may also return at any other time, for "no 49 * reason", so in general must be invoked within a loop that rechecks 50 * conditions upon return. In this sense {@code park} serves as an 51 * optimization of a "busy wait" that does not waste as much time 52 * spinning, but must be paired with an {@code unpark} to be 53 * effective. 54 * 55 * <p>The three forms of {@code park} each also support a 56 * {@code blocker} object parameter. This object is recorded while 57 * the thread is blocked to permit monitoring and diagnostic tools to 58 * identify the reasons that threads are blocked. (Such tools may 59 * access blockers using method {@link #getBlocker(Thread)}.) 60 * The use of these forms rather than the original forms without this 61 * parameter is strongly encouraged. The normal argument to supply as 62 * a {@code blocker} within a lock implementation is {@code this}. 63 * 64 * <p>These methods are designed to be used as tools for creating 65 * higher-level synchronization utilities, and are not in themselves 66 * useful for most concurrency control applications. The {@code park} 67 * method is designed for use only in constructions of the form: 68 * 69 * <pre> {@code 70 * while (!canProceed()) { 71 * // ensure request to unpark is visible to other threads 72 * ... 73 * LockSupport.park(this); 74 * }}</pre> 75 * 76 * where no actions by the thread publishing a request to unpark, 77 * prior to the call to {@code park}, entail locking or blocking. 78 * Because only one permit is associated with each thread, any 79 * intermediary uses of {@code park}, including implicitly via class 80 * loading, could lead to an unresponsive thread (a "lost unpark"). 81 * 82 * <p><b>Sample Usage.</b> Here is a sketch of a first-in-first-out 83 * non-reentrant lock class: 84 * <pre> {@code 85 * class FIFOMutex { 86 * private final AtomicBoolean locked = new AtomicBoolean(false); 87 * private final Queue!(Thread) waiters 88 * = new ConcurrentLinkedQueue<>(); 89 * 90 * void lock() { 91 * boolean wasInterrupted = false; 92 * // publish current thread for unparkers 93 * waiters.add(Thread.currentThread()); 94 * 95 * // Block while not first in queue or cannot acquire lock 96 * while (waiters.peek() != Thread.currentThread() || 97 * !locked.compareAndSet(false, true)) { 98 * LockSupport.park(this); 99 * // ignore interrupts while waiting 100 * if (Thread.interrupted()) 101 * wasInterrupted = true; 102 * } 103 * 104 * waiters.remove(); 105 * // ensure correct interrupt status on return 106 * if (wasInterrupted) 107 * Thread.currentThread().interrupt(); 108 * } 109 * 110 * void unlock() { 111 * locked.set(false); 112 * LockSupport.unpark(waiters.peek()); 113 * } 114 * 115 * static { 116 * // Reduce the risk of "lost unpark" due to classloading 117 * Class<?> ensureLoaded = LockSupport.class; 118 * } 119 * }}</pre> 120 * 121 */ 122 class LockSupport { 123 private static Parker _parker; 124 private __gshared Parker[Thread] parkers; 125 private shared static bool m_lock; 126 127 private this() {} // Cannot be instantiated. 128 129 static Parker getParker() { 130 if(_parker is null) { 131 Thread t = Thread.getThis(); 132 ThreadEx tx = cast(ThreadEx)t; 133 if(tx !is null) { 134 return tx.parker(); 135 } else { 136 _parker = createParker(t); 137 } 138 } 139 return _parker; 140 } 141 142 static Parker getParker(Thread t) { 143 if(t is Thread.getThis()) 144 return getParker(); 145 146 ThreadEx tx = cast(ThreadEx)t; 147 if(tx !is null) { 148 return tx.parker(); 149 } else { 150 Parker* itemPtr = t in parkers; 151 if(itemPtr is null) { 152 _parker = createParker(t); 153 } 154 155 return *itemPtr; 156 } 157 } 158 159 private static Parker createParker(Thread t) { 160 version(HUNT_DEBUG) info("creating a new parker for " ~ typeid(t).name); 161 Parker p = Parker.allocate(t); 162 163 while(!cas(&m_lock, false, true)) { 164 // waitting... 165 } 166 167 parkers[t] = p; 168 m_lock = false; 169 170 return p; 171 } 172 173 static void removeParker() { 174 removeParker(Thread.getThis); 175 _parker = null; 176 } 177 178 static void removeParker(Thread t) { 179 while(!cas(&m_lock, false, true)) { 180 } 181 parkers.remove(t); 182 m_lock = false; 183 } 184 185 /** 186 * Makes available the permit for the given thread, if it 187 * was not already available. If the thread was blocked on 188 * {@code park} then it will unblock. Otherwise, its next call 189 * to {@code park} is guaranteed not to block. This operation 190 * is not guaranteed to have any effect at all if the given 191 * thread has not been started. 192 * 193 * @param thread the thread to unpark, or {@code null}, in which case 194 * this operation has no effect 195 */ 196 static void unpark(Thread thread) { 197 getParker(thread).unpark(); 198 } 199 200 static void unpark() { 201 getParker().unpark(); 202 } 203 204 205 /** 206 * Disables the current thread for thread scheduling purposes unless the 207 * permit is available. 208 * 209 * <p>If the permit is available then it is consumed and the call 210 * returns immediately; otherwise the current thread becomes disabled 211 * for thread scheduling purposes and lies dormant until one of three 212 * things happens: 213 * 214 * <ul> 215 * 216 * <li>Some other thread invokes {@link #unpark unpark} with the 217 * current thread as the target; or 218 * 219 * <li>Some other thread {@linkplain Thread#interrupt interrupts} 220 * the current thread; or 221 * 222 * <li>The call spuriously (that is, for no reason) returns. 223 * </ul> 224 * 225 * <p>This method does <em>not</em> report which of these caused the 226 * method to return. Callers should re-check the conditions which caused 227 * the thread to park in the first place. Callers may also determine, 228 * for example, the interrupt status of the thread upon return. 229 */ 230 static void park() { 231 getParker().park(Duration.zero); 232 } 233 234 static void park(Duration time) { 235 getParker().park(time); 236 } 237 238 /** 239 * Disables the current thread for thread scheduling purposes unless the 240 * permit is available. 241 * 242 * <p>If the permit is available then it is consumed and the call returns 243 * immediately; otherwise 244 * the current thread becomes disabled for thread scheduling 245 * purposes and lies dormant until one of three things happens: 246 * 247 * <ul> 248 * <li>Some other thread invokes {@link #unpark unpark} with the 249 * current thread as the target; or 250 * 251 * <li>Some other thread {@linkplain Thread#interrupt interrupts} 252 * the current thread; or 253 * 254 * <li>The call spuriously (that is, for no reason) returns. 255 * </ul> 256 * 257 * <p>This method does <em>not</em> report which of these caused the 258 * method to return. Callers should re-check the conditions which caused 259 * the thread to park in the first place. Callers may also determine, 260 * for example, the interrupt status of the thread upon return. 261 * 262 * @param blocker the synchronization object responsible for this 263 * thread parking 264 */ 265 static void park(Object blocker) { 266 park(blocker, Duration.zero); 267 } 268 269 static void park(Object blocker, Duration time) { 270 if (time >= Duration.zero) { 271 Parker p = getParker(); 272 p.setBlocker(blocker); 273 p.park(time); 274 p.setBlocker(null); 275 } else { 276 warning("The time must be greater than 0."); 277 } 278 } 279 280 /** 281 * Disables the current thread for thread scheduling purposes, for up to 282 * the specified waiting time, unless the permit is available. 283 * 284 * <p>If the permit is available then it is consumed and the call 285 * returns immediately; otherwise the current thread becomes disabled 286 * for thread scheduling purposes and lies dormant until one of four 287 * things happens: 288 * 289 * <ul> 290 * <li>Some other thread invokes {@link #unpark unpark} with the 291 * current thread as the target; or 292 * 293 * <li>Some other thread {@linkplain Thread#interrupt interrupts} 294 * the current thread; or 295 * 296 * <li>The specified waiting time elapses; or 297 * 298 * <li>The call spuriously (that is, for no reason) returns. 299 * </ul> 300 * 301 * <p>This method does <em>not</em> report which of these caused the 302 * method to return. Callers should re-check the conditions which caused 303 * the thread to park in the first place. Callers may also determine, 304 * for example, the interrupt status of the thread, or the elapsed time 305 * upon return. 306 * 307 * @param blocker the synchronization object responsible for this 308 * thread parking 309 * @param nanos the maximum number of nanoseconds to wait 310 */ 311 312 deprecated("Using park(Object, Duration) instead.") 313 static void parkNanos(Object blocker, long nanos) { 314 if(nanos > 0) { 315 park(blocker, dur!(TimeUnit.Nanosecond)(nanos)); 316 } 317 } 318 319 /** 320 * Disables the current thread for thread scheduling purposes, for up to 321 * the specified waiting time, unless the permit is available. 322 * 323 * <p>If the permit is available then it is consumed and the call 324 * returns immediately; otherwise the current thread becomes disabled 325 * for thread scheduling purposes and lies dormant until one of four 326 * things happens: 327 * 328 * <ul> 329 * <li>Some other thread invokes {@link #unpark unpark} with the 330 * current thread as the target; or 331 * 332 * <li>Some other thread {@linkplain Thread#interrupt interrupts} 333 * the current thread; or 334 * 335 * <li>The specified waiting time elapses; or 336 * 337 * <li>The call spuriously (that is, for no reason) returns. 338 * </ul> 339 * 340 * <p>This method does <em>not</em> report which of these caused the 341 * method to return. Callers should re-check the conditions which caused 342 * the thread to park in the first place. Callers may also determine, 343 * for example, the interrupt status of the thread, or the elapsed time 344 * upon return. 345 * 346 * @param nanos the maximum number of nanoseconds to wait 347 */ 348 deprecated("Using park(Duration) instead.") 349 static void parkNanos(long nanos) { 350 if (nanos > 0) { 351 getParker().park(nanos.nsecs); 352 } 353 } 354 355 /** 356 * Disables the current thread for thread scheduling purposes, until 357 * the specified deadline, unless the permit is available. 358 * 359 * <p>If the permit is available then it is consumed and the call 360 * returns immediately; otherwise the current thread becomes disabled 361 * for thread scheduling purposes and lies dormant until one of four 362 * things happens: 363 * 364 * <ul> 365 * <li>Some other thread invokes {@link #unpark unpark} with the 366 * current thread as the target; or 367 * 368 * <li>Some other thread {@linkplain Thread#interrupt interrupts} the 369 * current thread; or 370 * 371 * <li>The specified deadline passes; or 372 * 373 * <li>The call spuriously (that is, for no reason) returns. 374 * </ul> 375 * 376 * <p>This method does <em>not</em> report which of these caused the 377 * method to return. Callers should re-check the conditions which caused 378 * the thread to park in the first place. Callers may also determine, 379 * for example, the interrupt status of the thread, or the current time 380 * upon return. 381 * 382 * @param blocker the synchronization object responsible for this 383 * thread parking 384 * @param deadline the absolute time, in milliseconds from the Epoch, 385 * to wait until 386 */ 387 static void parkUntil(Object blocker, MonoTime deadline) { 388 Parker p = getParker(); 389 p.setBlocker(blocker); 390 p.park(deadline); 391 p.setBlocker(null); 392 } 393 394 // deprecated("Using parkUntil(Object, Duration) instead.") 395 // static void parkUntil(Object blocker, long deadline) { 396 // parkUntil(blocker, deadline.msecs); 397 // } 398 399 /** 400 * Disables the current thread for thread scheduling purposes, until 401 * the specified deadline, unless the permit is available. 402 * 403 * <p>If the permit is available then it is consumed and the call 404 * returns immediately; otherwise the current thread becomes disabled 405 * for thread scheduling purposes and lies dormant until one of four 406 * things happens: 407 * 408 * <ul> 409 * <li>Some other thread invokes {@link #unpark unpark} with the 410 * current thread as the target; or 411 * 412 * <li>Some other thread {@linkplain Thread#interrupt interrupts} 413 * the current thread; or 414 * 415 * <li>The specified deadline passes; or 416 * 417 * <li>The call spuriously (that is, for no reason) returns. 418 * </ul> 419 * 420 * <p>This method does <em>not</em> report which of these caused the 421 * method to return. Callers should re-check the conditions which caused 422 * the thread to park in the first place. Callers may also determine, 423 * for example, the interrupt status of the thread, or the current time 424 * upon return. 425 * 426 * @param deadline the absolute time, in milliseconds from the Epoch, 427 * to wait until 428 */ 429 static void parkUntil(MonoTime deadline) { 430 getParker().park(deadline); 431 } 432 433 deprecated("Using parkUntil(Duration) instead.") 434 static void parkUntil(long deadline) { 435 parkUntil(MonoTime(deadline)); 436 } 437 438 439 /** 440 * Returns the blocker object supplied to the most recent 441 * invocation of a park method that has not yet unblocked, or null 442 * if not blocked. The value returned is just a momentary 443 * snapshot -- the thread may have since unblocked or blocked on a 444 * different blocker object. 445 * 446 * @param t the thread 447 * @return the blocker 448 * @throws NullPointerException if argument is null 449 */ 450 static Object getBlocker(Thread t) { 451 return getParker(t).getBlocker(); 452 } 453 454 /** 455 * Returns the pseudo-randomly initialized or updated secondary seed. 456 * Copied from ThreadLocalRandom due to package access restrictions. 457 */ 458 // static final int nextSecondarySeed() { 459 // int r; 460 // Thread t = Thread.currentThread(); 461 // if ((r = U.getInt(t, SECONDARY)) != 0) { 462 // r ^= r << 13; // xorshift 463 // r ^= r >>> 17; 464 // r ^= r << 5; 465 // } 466 // else if ((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) == 0) 467 // r = 1; // avoid zero 468 // U.putInt(t, SECONDARY, r); 469 // return r; 470 // } 471 472 /** 473 * Returns the thread id for the given thread. We must access 474 * this directly rather than via method Thread.getId() because 475 * getId() has been known to be overridden in ways that do not 476 * preserve unique mappings. 477 */ 478 // static final long getThreadId(Thread thread) { 479 // return U.getLong(thread, TID); 480 // } 481 482 }