Esempio n. 1
0
            public void SetStreamBlock(StreamBlock newBlock)
            {
                // The new block is already rented from shared memory,
                // this object owns the reference on behalf of the current process.
                // Proxy usage is tracked by _rc field.

                lock (this)
                {
                    if (AtomicCounter.GetIsDisposed(ref _rc))
                    {
                        // because we inside the lock that protects from concurrent call to dispose,
                        // the proxy could be disposed by now and the new block is not needed
#pragma warning disable 618
                        newBlock.DisposeFree();
#pragma warning restore 618
                    }
                    else
                    {
                        var previous = Block;
                        Block = newBlock;
                        if (previous.IsValid)
                        {
#pragma warning disable 618
                            previous.DisposeFree();
#pragma warning restore 618
                        }
                    }
                }
            }
Esempio n. 2
0
            public void Dispose(bool disposing)
            {
                if (!_isSharedMemory)
                {
                    DoDispose();
                }
                else
                {
                    lock (this)
                    {
                        DoDispose();
                    }
                }

                void DoDispose()
                {
                    if (!disposing)
                    {
                        // Cache has a weak reference, so finalizer could start running while a handle is still in the cache
                        // As if _rc was 1 and we called DecrementIfOne - if successful, no resurrect is possible
                        // because Retain uses IncrementIfRetained.

                        var current  = Volatile.Read(ref _rc);
                        var existing = Interlocked.CompareExchange(ref _rc, 0, current);
                        if (existing != current)
                        {
                            // Resurrected while we tried to set rc to zero.
                            // What if rc was wrong and not 1? At some point all new users will
                            // dispose the proxy and it will be in the cache with
                            // positive rc but without GC root, then it will be
                            // collected and finalized and will return to this
                            // place where we will try to set rc to 0 again.
                            // TODO trace this condition, it indicates dropped proxies
                            // From user code it could be possible only when manually using cursors
                            // and forgetting to dispose them, so all blame is on users, but we should not fail.

                            ThrowHelper.AssertFailFast(existing > 1, "existing > 1 when resurrected");
                            return;
                        }
                    }
                    else
                    {
                        var remaining = AtomicCounter.Decrement(ref _rc);
                        if (remaining > 1)
                        {
                            return;
                        }

                        if (AtomicCounter.DecrementIfOne(ref _rc) != 0)
                        {
                            return;
                        }
                    }

                    ThrowHelper.AssertFailFast(_rc == 0, "_rc must be 0 to proceed with proxy disposal");

                    try
                    {
                        // remove self from cache
                        _cache._blocks.TryRemove(_key, out var handle);
                        if (handle.IsAllocated)
                        {
                            handle.Free();
                        }
                    }
                    finally
                    {
                        // If we are shutting down, e.g. unhandled exception in other threads
                        // increase the chances we do release shared memory ref.

#pragma warning disable 618
                        // ReSharper disable once InconsistentlySynchronizedField
                        Block.DisposeFree();
#pragma warning restore 618
                    }

                    // Do not pool finalized objects.
                    // TODO (review) proxy does not have ref type fields,
                    // probably could add to pool without thinking about GC/finalization order.
                    // However, this case should be very rare (e.g. unhandled exception)
                    // and we care about releasing RC of shared memory above all.
                    if (disposing)
                    {
                        GC.SuppressFinalize(this);
                        // ReSharper disable once InconsistentlySynchronizedField
                        Block = default;
                        _key  = default;
                        AtomicCounter.Dispose(ref _rc);
                        _isSharedMemory = default;
                    }
                }
            }