Table of Contents

Class TimeoutDictionary<TKey, TValue>

Namespace
VpnHood.Net.Toolkit.Collections
Assembly
VpnHood.Net.Toolkit.dll

A concurrent dictionary whose values expire after Timeout of no use and are disposed by the dictionary when evicted, replaced or drained. Ordinary operations take no dictionary-wide application lock (the gated cleanup sweep is the only locked path). Expiry is checked on access and by the sweeps the operations try, with no timer of its own, against a cached clock of about one second's precision. Ownership contract: a value the dictionary adds (or a factory creates) is owned and disposed by the dictionary. One TryAdd returns false for stays the caller's, as it came; one AddOrUpdate rejects because the dictionary is already disposed stays the caller's. A factory may run more than once under contention and must return a fresh instance each time; losing instances are disposed. A returned value may be disposed by a concurrent eviction right after retrieval, so callers must tolerate IsDisposed values and value Dispose must be idempotent. A value's Dispose should not throw: one that does is logged as an error, and the dictionary goes on with the rest.

public sealed class TimeoutDictionary<TKey, TValue> : IDisposable where TKey : notnull where TValue : class, ITimeoutItem

Type Parameters

TKey

The type of the keys.

TValue

The type of the values.

Inheritance
TimeoutDictionary<TKey, TValue>
Implements
Inherited Members
Extension Methods

Constructors

TimeoutDictionary(TimeSpan?)

A concurrent dictionary whose values expire after Timeout of no use and are disposed by the dictionary when evicted, replaced or drained. Ordinary operations take no dictionary-wide application lock (the gated cleanup sweep is the only locked path). Expiry is checked on access and by the sweeps the operations try, with no timer of its own, against a cached clock of about one second's precision. Ownership contract: a value the dictionary adds (or a factory creates) is owned and disposed by the dictionary. One TryAdd returns false for stays the caller's, as it came; one AddOrUpdate rejects because the dictionary is already disposed stays the caller's. A factory may run more than once under contention and must return a fresh instance each time; losing instances are disposed. A returned value may be disposed by a concurrent eviction right after retrieval, so callers must tolerate IsDisposed values and value Dispose must be idempotent. A value's Dispose should not throw: one that does is logged as an error, and the dictionary goes on with the rest.

public TimeoutDictionary(TimeSpan? timeout = null)

Parameters

timeout TimeSpan?

How long a value may go unused before it expires, or null for no expiry by disuse; a disposed value still counts as expired on access.

Properties

AutoCleanup

Gets a value indicating whether an add or a lookup first tries a sweep of the expired values, which runs at most once per a third of Timeout; removals and disposal do not. The default is true.

public bool AutoCleanup { get; init; }

Property Value

bool

Count

Gets the number of values, expired ones not swept out yet included; with AutoCleanup, it tries a sweep first.

public int Count { get; }

Property Value

int

Timeout

Gets how long a value may go unused before it expires, or null for no expiry by disuse; a disposed value still counts as expired on access.

public TimeSpan? Timeout { get; init; }

Property Value

TimeSpan?

Methods

AddOrUpdate(TKey, TValue)

Adds a value, or replaces a key's value and disposes the replaced one.

public TValue AddOrUpdate(TKey key, TValue value)

Parameters

key TKey

The key.

value TValue

The value, which the dictionary owns once added; when the dictionary is already disposed, it throws and the caller keeps the value, and a disposal during the add disposes it.

Returns

TValue

The value.

Exceptions

ObjectDisposedException

The dictionary is disposed.

ArgumentNullException

key is null.

Cleanup(bool)

Removes and disposes the expired values. It does nothing without a Timeout, or when the dictionary is disposed.

public void Cleanup(bool force = false)

Parameters

force bool

true to sweep now; false to skip the sweep when the last one was within a third of Timeout.

Dispose()

Removes and disposes every value; later adds throw or fail. Calling it again does nothing.

public void Dispose()

GetOrAdd(TKey, Func<TKey, TValue>)

Gets a key's live value, refreshing its last-used time, or adds the value a factory creates, in place of an expired one, which is disposed.

public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)

Parameters

key TKey

The key.

valueFactory Func<TKey, TValue>

Creates the value to add.

Returns

TValue

The key's value, which the dictionary owns.

Remarks

Under contention the factory may run more than once; it must return a new instance each time, and the instances that lose are disposed.

Exceptions

ObjectDisposedException

The dictionary is disposed.

ArgumentNullException

key is null.

GetOrAdd(TKey, Func<TKey, TValue>, out bool)

Gets a key's live value, refreshing its last-used time, or adds the value a factory creates, in place of an expired one, which is disposed; says which it did.

public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory, out bool added)

Parameters

key TKey

The key.

valueFactory Func<TKey, TValue>

Creates the value to add.

added bool

true if this call added the value, in place of an expired one too; false if the key had a live value.

Returns

TValue

The key's value, which the dictionary owns.

Remarks

Under contention the factory may run more than once; it must return a new instance each time, and the instances that lose are disposed. Code that acts once per new key reads added: a flag set in the factory is set by a losing instance too, and a factory that captures one makes an object on every call.

Exceptions

ObjectDisposedException

The dictionary is disposed.

ArgumentNullException

key is null.

RemoveAll()

Removes and disposes the values, in one pass; a value added or replaced during the pass may stay.

public void RemoveAll()

TryAdd(TKey, TValue)

Adds a value unless the key has a live one; an expired one is replaced and disposed.

public bool TryAdd(TKey key, TValue value)

Parameters

key TKey

The key.

value TValue

The value, which the dictionary owns when it is added, with its last-used time set then.

Returns

bool

true if the value was added; false if the key has a live value or the dictionary is disposed, and the caller keeps the value.

Exceptions

ArgumentNullException

key is null.

TryGetValue(TKey, out TValue)

Gets a key's live value, refreshing its last-used time. An expired value is removed and disposed.

public bool TryGetValue(TKey key, out TValue value)

Parameters

key TKey

The key.

value TValue

The value, when found; it stays the dictionary's.

Returns

bool

true if the key has a live value; otherwise, false.

Exceptions

ArgumentNullException

key is null.

TryRemove(TKey, out TValue?)

Removes a key's value and disposes it.

public bool TryRemove(TKey key, out TValue? value)

Parameters

key TKey

The key.

value TValue

The removed value, already disposed, when found.

Returns

bool

true if a value was removed; otherwise, false.

Exceptions

ArgumentNullException

key is null.