@@ -397,25 +397,15 @@ public CacheFileRegion get(KeyType cacheKey, long fileLength, int region) {
397
397
final Integer freeSlot = freeRegions .poll ();
398
398
if (freeSlot != null ) {
399
399
// no need to evict an item, just add
400
- assert regionOwners [freeSlot ].compareAndSet (null , entry .chunk );
401
- synchronized (this ) {
402
- pushEntryToBack (entry );
403
- // assign sharedBytesPos only when chunk is ready for use. Under lock to avoid concurrent tryEvict.
404
- entry .chunk .sharedBytesPos = freeSlot ;
405
- }
400
+ assignToSlot (entry , freeSlot );
406
401
} else {
407
402
// need to evict something
408
403
synchronized (this ) {
409
404
maybeEvict ();
410
405
}
411
406
final Integer freeSlotRetry = freeRegions .poll ();
412
407
if (freeSlotRetry != null ) {
413
- assert regionOwners [freeSlotRetry ].compareAndSet (null , entry .chunk );
414
- synchronized (this ) {
415
- pushEntryToBack (entry );
416
- // assign sharedBytesPos only when chunk is ready for use. Under lock to avoid concurrent tryEvict.
417
- entry .chunk .sharedBytesPos = freeSlotRetry ;
418
- }
408
+ assignToSlot (entry , freeSlotRetry );
419
409
} else {
420
410
boolean removed = keyMapping .remove (regionKey , entry );
421
411
assert removed ;
@@ -431,7 +421,7 @@ public CacheFileRegion get(KeyType cacheKey, long fileLength, int region) {
431
421
432
422
// existing item, check if we need to promote item
433
423
synchronized (this ) {
434
- if (now - entry .lastAccessed >= minTimeDelta && entry .freq + 1 < maxFreq ) {
424
+ if (now - entry .lastAccessed >= minTimeDelta && entry .freq + 1 < maxFreq && entry . chunk . isEvicted () == false ) {
435
425
unlink (entry );
436
426
entry .freq ++;
437
427
entry .lastAccessed = now ;
@@ -442,6 +432,21 @@ public CacheFileRegion get(KeyType cacheKey, long fileLength, int region) {
442
432
return entry .chunk ;
443
433
}
444
434
435
+ private void assignToSlot (Entry <CacheFileRegion > entry , int freeSlot ) {
436
+ assert regionOwners [freeSlot ].compareAndSet (null , entry .chunk );
437
+ synchronized (this ) {
438
+ if (entry .chunk .isEvicted ()) {
439
+ assert regionOwners [freeSlot ].compareAndSet (entry .chunk , null );
440
+ freeRegions .add (freeSlot );
441
+ keyMapping .remove (entry .chunk .regionKey , entry );
442
+ throw new AlreadyClosedException ("evicted during free region allocation" );
443
+ }
444
+ pushEntryToBack (entry );
445
+ // assign sharedBytesPos only when chunk is ready for use. Under lock to avoid concurrent tryEvict.
446
+ entry .chunk .sharedBytesPos = freeSlot ;
447
+ }
448
+ }
449
+
445
450
private void assertChunkActiveOrEvicted (Entry <CacheFileRegion > entry ) {
446
451
if (Assertions .ENABLED ) {
447
452
synchronized (this ) {
@@ -454,8 +459,11 @@ private void assertChunkActiveOrEvicted(Entry<CacheFileRegion> entry) {
454
459
}
455
460
456
461
public void onClose (CacheFileRegion chunk ) {
457
- assert regionOwners [chunk .sharedBytesPos ].compareAndSet (chunk , null );
458
- freeRegions .add (chunk .sharedBytesPos );
462
+ // we held the "this" lock when this was evicted, hence if sharedBytesPos is not filled in, chunk will never be registered.
463
+ if (chunk .sharedBytesPos != -1 ) {
464
+ assert regionOwners [chunk .sharedBytesPos ].compareAndSet (chunk , null );
465
+ freeRegions .add (chunk .sharedBytesPos );
466
+ }
459
467
}
460
468
461
469
// used by tests
@@ -510,7 +518,7 @@ private void maybeEvict() {
510
518
for (int i = 0 ; i < maxFreq ; i ++) {
511
519
for (Entry <CacheFileRegion > entry = freqs [i ]; entry != null ; entry = entry .next ) {
512
520
boolean evicted = entry .chunk .tryEvict ();
513
- if (evicted ) {
521
+ if (evicted && entry . chunk . sharedBytesPos != - 1 ) {
514
522
unlink (entry );
515
523
keyMapping .remove (entry .chunk .regionKey , entry );
516
524
return ;
@@ -603,7 +611,7 @@ public void forceEvict(Predicate<KeyType> cacheKeyPredicate) {
603
611
synchronized (this ) {
604
612
for (Entry <CacheFileRegion > entry : matchingEntries ) {
605
613
boolean evicted = entry .chunk .forceEvict ();
606
- if (evicted ) {
614
+ if (evicted && entry . chunk . sharedBytesPos != - 1 ) {
607
615
unlink (entry );
608
616
keyMapping .remove (entry .chunk .regionKey , entry );
609
617
}
@@ -693,7 +701,9 @@ public long physicalEndOffset() {
693
701
private final AtomicBoolean evicted = new AtomicBoolean (false );
694
702
695
703
// tries to evict this chunk if noone is holding onto its resources anymore
696
- public boolean tryEvict () {
704
+ // visible for tests.
705
+ boolean tryEvict () {
706
+ assert Thread .holdsLock (SharedBlobCacheService .this ) : "must hold lock when evicting" ;
697
707
if (refCount () <= 1 && evicted .compareAndSet (false , true )) {
698
708
logger .trace ("evicted {} with channel offset {}" , regionKey , physicalStartOffset ());
699
709
evictCount .increment ();
@@ -704,6 +714,7 @@ public boolean tryEvict() {
704
714
}
705
715
706
716
public boolean forceEvict () {
717
+ assert Thread .holdsLock (SharedBlobCacheService .this ) : "must hold lock when evicting" ;
707
718
if (evicted .compareAndSet (false , true )) {
708
719
logger .trace ("force evicted {} with channel offset {}" , regionKey , physicalStartOffset ());
709
720
evictCount .increment ();
0 commit comments