@@ -16,7 +16,7 @@ namespace BitFaster.Caching
1616 public sealed class Scoped < T > : IScoped < T > , IDisposable where T : IDisposable
1717 {
1818 private ReferenceCount < T > refCount ;
19- private bool isDisposed ;
19+ private int disposed = 0 ;
2020
2121 /// <summary>
2222 /// Initializes a new Scoped value.
@@ -30,7 +30,7 @@ public Scoped(T value)
3030 /// <summary>
3131 /// Gets a value indicating whether the scope is disposed.
3232 /// </summary>
33- public bool IsDisposed => isDisposed ;
33+ public bool IsDisposed => Volatile . Read ( ref this . disposed ) == 1 ;
3434
3535 /// <summary>
3636 /// Attempts to create a lifetime for the scoped value. The lifetime guarantees the value is alive until
@@ -45,7 +45,7 @@ public bool TryCreateLifetime(out Lifetime<T> lifetime)
4545 var oldRefCount = this . refCount ;
4646
4747 // If old ref count is 0, the scoped object has been disposed and there was a race.
48- if ( this . isDisposed || oldRefCount . Count == 0 )
48+ if ( IsDisposed || oldRefCount . Count == 0 )
4949 {
5050 lifetime = default ;
5151 return false ;
@@ -82,9 +82,11 @@ private void DecrementReferenceCount()
8282
8383 if ( oldRefCount == Interlocked . CompareExchange ( ref this . refCount , oldRefCount . DecrementCopy ( ) , oldRefCount ) )
8484 {
85- if ( this . refCount . Count == 0 )
85+ // Note this.refCount may be stale.
86+ // Old count == 1, thus new ref count is 0, dispose the value.
87+ if ( oldRefCount . Count == 1 )
8688 {
87- this . refCount . Value ? . Dispose ( ) ;
89+ oldRefCount . Value ? . Dispose ( ) ;
8890 }
8991
9092 break ;
@@ -98,10 +100,10 @@ private void DecrementReferenceCount()
98100 /// </summary>
99101 public void Dispose ( )
100102 {
101- if ( ! this . isDisposed )
103+ // Dispose exactly once, decrement via dispose exactly once
104+ if ( Interlocked . CompareExchange ( ref this . disposed , 1 , 0 ) == 0 )
102105 {
103- this . DecrementReferenceCount ( ) ;
104- this . isDisposed = true ;
106+ DecrementReferenceCount ( ) ;
105107 }
106108 }
107109
0 commit comments