28
28
@ Slf4j
29
29
public class ObjectLocker {
30
30
31
+
32
+
31
33
public static BiPredicate <StackTraceElement , AtomicInteger > summaryBiPredicate =
32
34
(e , count ) -> {
33
35
boolean match = e .getClassName ().startsWith ("nl.vpro" ) &&
@@ -205,7 +207,7 @@ public static <K extends Serializable> LockHolderCloser<Serializable> acquireLoc
205
207
206
208
207
209
@ SuppressWarnings ("SynchronizationOnLocalVariableOrMethodParameter" )
208
- public static <K extends Serializable > LockHolderCloser <K > acquireLock (
210
+ private static <K extends Serializable > LockHolderCloser <K > acquireLock (
209
211
final K key ,
210
212
final @ NonNull String reason ,
211
213
final @ NonNull Map <K , LockHolder <K >> locks ,
@@ -227,7 +229,7 @@ public static <K extends Serializable> LockHolderCloser<K> acquireLock(
227
229
locks .remove (key );
228
230
continue ;
229
231
}
230
- closer = new LockHolderCloser <>(nanoStart , locks , holder , true );
232
+ closer = new LockHolderCloser <>(nanoStart , locks , holder , comparable );
231
233
if (holder .lock .isLocked () && !holder .lock .isHeldByCurrentThread ()) {
232
234
log .debug ("There are already threads ({}) for {}, waiting" , holder .lock .getQueueLength (), key );
233
235
alreadyWaiting = true ;
@@ -324,31 +326,29 @@ private static <K extends Serializable> LockHolder<K> computeLock(
324
326
@ SuppressWarnings ("SynchronizationOnLocalVariableOrMethodParameter" )
325
327
private static <K extends Serializable > void releaseLock (
326
328
final long nanoStart ,
327
- final @ NonNull K key ,
328
- final @ NonNull String reason ,
329
329
final @ NonNull Map <K , LockHolder <K >> locks ,
330
- final @ NonNull LockHolder <K > lock ,
331
- final boolean checkIfCurrentThread ) {
330
+ final @ NonNull LockHolder <K > lock ) {
332
331
synchronized (locks ) {
333
332
if (lock .lock .getHoldCount () == 1 ) {
334
333
if (!lock .lock .hasQueuedThreads ()) {
335
- log .trace ("Removed {}" , key );
336
- locks .remove (key );
334
+ log .trace ("Removed {}" , lock . key );
335
+ locks .remove (lock . key );
337
336
}
338
337
final Duration duration = Duration .ofNanos (System .nanoTime () - nanoStart );
339
338
for (Listener listener : LISTENERS ) {
340
339
listener .unlock (lock , duration );
341
340
}
342
341
343
342
Slf4jHelper .log (log , duration .compareTo (lock .warnTime )> 0 ? Level .WARN : Level .DEBUG ,
344
- "Released lock for {} ({}) in {}" , key , reason , Duration .ofNanos (System .nanoTime () - nanoStart ));
343
+ "Released lock for {} ({}) in {}" , lock . key , lock . reason , Duration .ofNanos (System .nanoTime () - nanoStart ));
345
344
}
346
- if (! checkIfCurrentThread || lock .lock .isHeldByCurrentThread ()) { // MSE-4946
345
+ if (lock .lock .isHeldByCurrentThread ()) { // MSE-4946
347
346
HOLDS .get ().remove (lock );
348
347
lock .lock .unlock ();
349
348
} else {
350
349
// can happen if 'continuing without lock'
351
- log .warn ("Current lock {} not hold by current thread {} but by {}" , lock , Thread .currentThread ().getName (), Optional .ofNullable (lock .thread .get ()).map (Thread ::getName ).orElse (null ));
350
+ Thread currentThread = Thread .currentThread ();
351
+ log .warn ("Current lock {} not hold by current thread {} ({}) but by {} ({})" , lock , currentThread .getName (), currentThread , Optional .ofNullable (lock .thread .get ()).map (Thread ::getName ).orElse (null ), lock .thread .get (), new Exception ());
352
352
}
353
353
354
354
locks .notifyAll ();
@@ -383,6 +383,7 @@ public static class LockHolder<K> {
383
383
@ Setter
384
384
private Duration warnTime = ObjectLocker .defaultWarnTime ;
385
385
386
+
386
387
LockHolder (K k , String reason , ReentrantLock lock ) {
387
388
this .key = k ;
388
389
this .lock = lock ;
@@ -391,6 +392,8 @@ public static class LockHolder<K> {
391
392
this .reason = reason ;
392
393
}
393
394
395
+
396
+
394
397
@ SuppressWarnings ("rawtypes" )
395
398
@ Override
396
399
public boolean equals (Object o ) {
@@ -457,25 +460,83 @@ public static class LockHolderCloser<K extends Serializable> implements AutoClos
457
460
458
461
final long nanoStart ;
459
462
460
- @ With
461
- final boolean checkIfCurrentThread ;
462
-
463
463
final @ NonNull Map <K , LockHolder <K >> locks ;
464
464
465
+ final BiPredicate <Serializable , K > comparable ;
466
+ boolean closed = false ;
467
+
465
468
private LockHolderCloser (
466
469
final long nanoStart ,
467
470
@ NonNull Map <K , LockHolder <K >> locks ,
468
471
@ NonNull LockHolder <K > lockHolder ,
469
- final boolean checkIfCurrentThread ) {
472
+ BiPredicate <Serializable , K > comparable
473
+ ) {
470
474
this .nanoStart = nanoStart ;
471
475
this .locks = locks ;
472
476
this .lockHolder = lockHolder ;
473
- this .checkIfCurrentThread = checkIfCurrentThread ;
477
+ this .comparable = comparable ;
478
+ }
479
+
480
+ public CompletableFuture <LockHolderCloser <K >> forTransfer () {
481
+ close ();
482
+ CompletableFuture <LockHolderCloser <K >> future = new CompletableFuture <>();
483
+
484
+
485
+ future .thenAccept ((o ) -> {
486
+ if (log .isDebugEnabled ()) {
487
+ log .debug ("Completed transfer {} ({}) -> {} ({})" , LockHolderCloser .this , LockHolderCloser .this .lockHolder .thread .get (), o , o .lockHolder .thread .get ());
488
+ }
489
+ });
490
+ log .debug ("Future to use {}" , future );
491
+ return future ;
492
+ }
493
+
494
+ public LockHolderCloser <K > transfer (CompletableFuture <LockHolderCloser <K >> future ) {
495
+ synchronized (locks ) {
496
+ try {
497
+
498
+ LockHolderCloser <K > closer = acquireLock (lockHolder .key , lockHolder .reason , locks , comparable );
499
+ future .complete (closer );
500
+ return closer ;
501
+ } catch (Exception e ) {
502
+ return null ;
503
+ }
504
+ }
505
+ }
506
+ public CompletableFuture <Void > delayedClose (Duration duration ) {
507
+ CompletableFuture <Void > ready = new CompletableFuture <>();
508
+ final CompletableFuture <LockHolderCloser <K >> future = forTransfer ();
509
+ ForkJoinPool .commonPool ().execute (() -> {
510
+
511
+ try (var closer = transfer (future )) {
512
+ log .debug ("{} Starting delay" , lockHolder .key );
513
+ Thread .sleep (duration .toMillis ());
514
+ log .info ("{} Ready after delay {}" , lockHolder .key , duration );
515
+ ready .complete (null );
516
+ } catch (InterruptedException e ) {
517
+ log .warn (e .getMessage (), e );
518
+ } finally {
519
+
520
+ }
521
+ });
522
+ return ready ;
474
523
}
475
524
476
525
@ Override
477
526
public void close () {
478
- releaseLock (nanoStart , lockHolder .key , lockHolder .reason , locks , lockHolder , checkIfCurrentThread );
527
+ synchronized (locks ) {
528
+ if (!closed ) {
529
+ releaseLock (nanoStart , locks , lockHolder );
530
+ } else {
531
+ log .debug ("Closed already" );
532
+ }
533
+ closed = true ;
534
+ }
535
+ }
536
+
537
+ @ Override
538
+ public String toString () {
539
+ return lockHolder + (closed ? " (closed)" : "" );
479
540
}
480
541
}
481
542
0 commit comments