Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 64 additions & 92 deletions Orm/Xtensive.Orm/Caching/WeakCache.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// Copyright (C) 2003-2010 Xtensive LLC.
// All rights reserved.
// For conditions of distribution and use, see license.
// Copyright (C) 2003-2021 Xtensive LLC.
// This code is distributed under MIT license terms.
// See the License.txt file in the project root for more information.
// Created by: Alex Ustinov
// Created: 2007.05.28

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using Xtensive.Core;
Expand All @@ -34,7 +35,6 @@ public class WeakCache<TKey, TItem> :
/// </summary>
protected const int NoGcCount = 1024;

private const int GcOperationCost = 2;
private readonly bool trackResurrection;
private readonly Converter<TItem, TKey> keyExtractor;
private Dictionary<TKey, GCHandle> items;
Expand All @@ -43,48 +43,44 @@ public class WeakCache<TKey, TItem> :
#region Properites: KeyExtractor, ChainedCache, TrackResurrection, EfficiencyFactor, Count, Size

/// <inheritdoc/>
public Converter<TItem, TKey> KeyExtractor {
public Converter<TItem, TKey> KeyExtractor
{
[DebuggerStepThrough]
get { return keyExtractor; }
get => keyExtractor;
}

/// <summary>
/// Gets a value indicating whether this cache tracks resurrection.
/// </summary>
public bool TrackResurrection {
public bool TrackResurrection
{
[DebuggerStepThrough]
get { return trackResurrection; }
get => trackResurrection;
}

/// <inheritdoc/>
public int Count {
public int Count
{
[DebuggerStepThrough]
get { return items.Count; }
get => items?.Count ?? 0;
}

#endregion

/// <inheritdoc/>
public TItem this[TKey key, bool markAsHit] {
get {
TItem item;
if (TryGetItem(key, markAsHit, out item))
return item;
else
return null;
}
}
public TItem this[TKey key, bool markAsHit] => TryGetItem(key, markAsHit, out var item) ? item : null;

/// <inheritdoc/>
[SecuritySafeCritical]
public virtual bool TryGetItem(TKey key, bool markAsHit, out TItem item)
{
RegisterOperation(1);
GCHandle cached;
if (items.TryGetValue(key, out cached)) {
item = (TItem) cached.Target;
if (item!=null)
if (items != null && items.TryGetValue(key, out var cached)) {
item = ExtractTarget(cached);
if (item != null) {
return true;
}

items.Remove(key);
cached.Free();
return false;
Expand All @@ -94,113 +90,103 @@ public virtual bool TryGetItem(TKey key, bool markAsHit, out TItem item)
}

/// <inheritdoc/>
public bool Contains(TItem item)
{
return ContainsKey(KeyExtractor(item));
}
public bool Contains(TItem item) => ContainsKey(KeyExtractor(item));

/// <inheritdoc/>
public bool ContainsKey(TKey key)
{
TItem item;
return TryGetItem(key, false, out item);
}
public bool ContainsKey(TKey key) => TryGetItem(key, false, out var _);

#region Modification methods: Add, Remove, Clear

/// <inheritdoc/>
public void Add(TItem item)
{
Add(item, true);
}
public void Add(TItem item) => Add(item, true);

/// <inheritdoc/>
[SecuritySafeCritical]
public virtual TItem Add(TItem item, bool replaceIfExists)
{
ArgumentValidator.EnsureArgumentNotNull(item, "item");
ArgumentValidator.EnsureArgumentNotNull(item, nameof(item));
RegisterOperation(2);
var key = KeyExtractor(item);
GCHandle cached;
if (items.TryGetValue(key, out cached)) {
if (!replaceIfExists) {
var cachedItem = (TItem) cached.Target;
if (cachedItem!=null)
return cachedItem;
if (items == null) {
items = CreateDictionary();
}
else if (replaceIfExists) {
if (items.Remove(key, out var cached)) {
cached.Free();
}
}
else if (items.TryGetValue(key, out var cached)) {
if (ExtractTarget(cached) is TItem cachedItem) {
return cachedItem;
}
items.Remove(key);
cached.Free();
}
items[key] = GCHandle.Alloc(item,
trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak);
items[key] = GCHandle.Alloc(item, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak);
return item;
}

/// <inheritdoc/>
public void Remove(TItem item)
{
ArgumentValidator.EnsureArgumentNotNull(item, "item");
ArgumentValidator.EnsureArgumentNotNull(item, nameof(item));
RemoveKey(KeyExtractor(item));
}

/// <inheritdoc/>
[SecuritySafeCritical]
public virtual void RemoveKey(TKey key)
{
GCHandle cached;
if (items.TryGetValue(key, out cached)) {
items.Remove(key);
if (items != null && items.Remove(key, out var cached) == true) {
cached.Free();
}
}

/// <inheritdoc/>
public void RemoveKey(TKey key, bool removeCompletely)
{
RemoveKey(key);
}
public void RemoveKey(TKey key, bool removeCompletely) => RemoveKey(key);

/// <inheritdoc/>
[SecuritySafeCritical]
public virtual void Clear()
{
if (items == null) {
return;
}
try {
foreach (var pair in items)
foreach (var pair in items) {
try {
pair.Value.Free();
}
catch {}
catch { }
}
}
finally {
items = new Dictionary<TKey, GCHandle>();
items = null;
time = 0;
}
}

/// <inheritdoc/>
public void Invalidate()
{
Clear();
}
public void Invalidate() => Clear();

/// <inheritdoc/>
[SecuritySafeCritical]
public virtual void CollectGarbage()
{
int count = items.Count;
if (count<=NoGcCount)
var count = items?.Count ?? 0;
if (count <= NoGcCount) {
return;
}

Exception error = null;
int removedCount = 0;
try {
// Filtering
var newItems = new Dictionary<TKey, GCHandle>();
foreach (var pair in items) {
var cached = pair.Value;
var newItems = CreateDictionary();
foreach (var (key, cached) in items) {
var item = cached.Target;
if (item!=null)
newItems.Add(pair.Key, cached);
if (item != null)
newItems.Add(key, cached);
else
cached.Free();
}
Expand All @@ -217,7 +203,7 @@ public virtual void CollectGarbage()
// Logging
if (CoreLog.IsLogged(LogLevel.Debug)) {
CoreLog.Debug("WeakCache.CollectGarbage: removed: {0} from {1}", removedCount, count);
if (error!=null)
if (error != null)
CoreLog.Debug(error, "Caught at WeakCache.CollectGarbage");
}
}
Expand All @@ -229,39 +215,33 @@ public virtual void CollectGarbage()

/// <inheritdoc/>
[DebuggerStepThrough]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

/// <inheritdoc/>
public virtual IEnumerator<TItem> GetEnumerator()
{
foreach (var pair in items) {
var item = ExtractTarget(pair.Value);
if (item!=null)
foreach (var pair in items ?? Enumerable.Empty<KeyValuePair<TKey, GCHandle>>()) {
if (ExtractTarget(pair.Value) is TItem item)
yield return item;
}
}

[SecuritySafeCritical]
private static TItem ExtractTarget(GCHandle handle)
{
return (TItem) handle.Target;
}
private static TItem ExtractTarget(GCHandle handle) => (TItem) handle.Target;

#endregion

#region Private / internal methods

private static Dictionary<TKey, GCHandle> CreateDictionary() => new Dictionary<TKey, GCHandle>();

private void RegisterOperation(int weight)
{
time += weight;
var count = items.Count;
if (count <= NoGcCount)
return;
if (time > ((count << 1) + count))
var count = items?.Count ?? 0;
if (count > NoGcCount && time > (count << 1) + count) {
CollectGarbage();
}
}

#endregion
Expand All @@ -279,7 +259,6 @@ public WeakCache(bool trackResurrection, Converter<TItem, TKey> keyExtractor)
ArgumentValidator.EnsureArgumentNotNull(keyExtractor, "keyExtractor");
this.trackResurrection = trackResurrection;
this.keyExtractor = keyExtractor;
items = new Dictionary<TKey, GCHandle>(1024);
}

// Dispose pattern
Expand All @@ -290,14 +269,7 @@ public WeakCache(bool trackResurrection, Converter<TItem, TKey> keyExtractor)
[SecuritySafeCritical]
protected virtual void Dispose(bool disposing)
{
if (items!=null) {
try {
Clear();
}
finally {
items = null;
}
}
Clear();
}

/// <summary>
Expand Down
Loading