dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.91k stars 4.63k forks source link

[API Proposal]: A built-in collection for weak references #99308

Open huoyaoyuan opened 6 months ago

huoyaoyuan commented 6 months ago

Background and motivation

Weak references are widely used for tracking objects meanwhile allowing them to be collected by GC. In current API, WeakReference<T> provides decent support for tracking individual objects. There is also need for weakly tracking a collection of objects effectively. Sample usages include:

The current workarounds for managing collections of weak references including:

To simplify weak reference management and reduce potential overhead, collections of weak references are desired. BCL built-in support also provides more confidence of correctness and efficiency.

API Proposal

namespace System
{
    public class WeakCollection<T> : IEnumerable<T>
        where T : notnull // or class instead?
    {
        public void Add(T item);
        public void Trim();
        public bool Contains(T item);
        public bool Remove(T item);

        public Enumerator GetEnumerator();

        public struct Enumerator : IEnumerator<T>
        {
            public bool MoveNext();
            public T Current { get; }
        }
    }
}

API Usage

class BindableObject
{
    private readonly WeakList<BindableObject> bounded;
    void InvokeValueChanged(string newValue)
    {
        foreach (BindableObject o in bounded)
        {
            if (bounded != this)
                bounded.NotifyValueChanged(newValue);
        }
    }
}

Alternative Designs

Risks

The complexity and correctness when providing more integrated support from GC.

ghost commented 6 months ago

Tagging subscribers to this area: @dotnet/area-system-runtime See info in area-owners.md if you want to be subscribed.

Issue Details
### Background and motivation Weak references are widely used for tracking objects meanwhile allowing them to be collected by GC. In current API, `WeakReference` provides decent support for tracking *individual* objects. There is also need for weakly tracking a *collection* of objects effectively. Sample usages include: - Weak event pattern, register events and automatically deregister when the handler object is collected. - Data binding, an object listening changes to data source, or multiple-way binding of more objects, allowing each bound objects to be collected separately. The current workarounds for managing collections of weak references including: - Use `ConditionalWeakTable`. The implementation of `ArrayPool.Shared` uses the key of `ConditionalWeakTable` to weakly track references. The value slots are not used and always set to `null`. This can lead to more overhead for the value field storing. - Manipulating `WeakReference` together with `ConditionalWeakTable`. Example: `WeakEventManager` of WPF. - Manually manipulate lists of `WeakReference`. When invoked, manually check each weak reference and remove collected ones. To simplify weak reference management and reduce potential overhead, collections of weak references are desired. BCL built-in support also provides more confidence of correctness and efficiency. ### API Proposal ```csharp namespace System { public class WeakCollection : IEnumerable where T : notnull // or class instead? { public void Add(T item); public void Trim(); public bool Contains(T item); public bool Remove(T item); public Enumerator GetEnumerator(); public struct Enumerator : IEnumerator { public bool MoveNext(); public T Current { get; } } } } ``` ### API Usage ```csharp class BindableObject { private readonly WeakList bounded; void InvokeValueChanged(string newValue) { foreach (BindableObject o in bounded) { if (bounded != this) bounded.NotifyValueChanged(newValue); } } } ``` ### Alternative Designs - Implement `ICollection`, `IList`, or providing indexer ? Count and indexing are not clearly defined when some objects are collected by GC. - Make the collection aware of GC, or make GC aware of the collection? Ideally, the self-cleanup should run when GC happens. For maximum memory efficiency, GC may specially recognize this collection and mark the object references inside it as "weak". When GC moves the storage of the collection, it can also compact it. This would require on-heap weak reference support, which can be very challenging. Making the collection **aware of GC** should be considerably simpler. It can use `WeakReference` or `GCHandle` to track the objects, and skips collected objects during enumeration. Implicit trimming can be triggered for add, remove, or GC collecting events. ### Risks The complexity and correctness when providing more integrated support from GC.
Author: huoyaoyuan
Assignees: -
Labels: `api-suggestion`, `area-System.Runtime`, `untriaged`
Milestone: -
teo-tsirpanis commented 6 months ago

Instead of teaching the GC about this list, we can do what CWT does and clean-up the dead handles every time we grow the list's internal buffer.