Add documentation on the Windows Runtime event infrastructure#2450
Open
Sergio0694 wants to merge 8 commits into
Open
Add documentation on the Windows Runtime event infrastructure#2450Sergio0694 wants to merge 8 commits into
Sergio0694 wants to merge 8 commits into
Conversation
Add comprehensive documentation for CsWinRT's event infrastructure under docs/event-infrastructure.md. Covers core types (EventRegistrationToken, EventSource<T>, EventSourceState<T>, EventSourceCache, EventRegistrationTokenTable<T>), RCW (managed→native) and CCW (native→managed) event flows, lifetime and GC semantics (including reference tracking and cleanup), and an in-depth analysis of static event lifetime issues and potential fixes. Intended to help maintainers and contributors understand event registration, marshalling, and cross-context pitfalls.
Deduplicate the repeated 'static event source field' paragraph in the static event analysis, and make the GC cleanup description precise about how EventSourceState<T>.OnDispose() captures the saved native pointer before clearing the field so the EventSourceCache entry is actually removed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instance events store their EventSource<T> in a per-instance field on the projected runtime class (the common path), not in a ConditionalWeakTable as the doc implied. The ConditionalWeakTable keyed on the runtime class instance is only a fallback for certain interfaces (and the mechanism used for static events). Update the subscribe flow, object diagram, lifetime tables, and the instance-vs-static contrast to reflect this. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The lazy static factory object reference is produced by WindowsRuntimeObjectReference.GetActivationFactory(...), not WindowsRuntimeActivationFactory.GetActivationFactory(...) (that type only exposes GetActivationFactoryUnsafe, which returns a raw pointer). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The 'alternative cache key strategies' analysis claimed a stable key or static event source field would make native add/remove calls go through a stale, wrong-context object reference. That is inaccurate: context-aware object references marshal cross-context calls correctly (GetThisPtrUnsafe routes through GetThisPtrWithContextUnsafe), as the proposed-fix section already notes. Reframe the trade-off around the real cost (lifetime: a stable key or static field would pin the event source alive forever, unlike a per-instance field bounded by the runtime class instance). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s doc Document the locking model (EventSource<T> instance lock, EventSourceCache ReaderWriterLockSlim, EventRegistrationTokenTable<T> dictionary lock) and note that the projected observable collection types reuse the same event source Subscribe/Unsubscribe path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tighten the instance-event lifetime phrasing (the per-instance field has no key) and note that context-aware cross-context pointer resolution is best-effort (it falls back to the original pointer if the agile/context resolution fails, e.g. the original apartment was torn down). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The ConditionalWeakTable keyed on the WindowsRuntimeObject is not just a fast-ABI fallback: it is the mechanism used whenever an event-bearing interface is reached through a dynamic interface cast (a [DynamicInterfaceCastableImplementation] proxy), since the proxy interface has no per-instance fields. This covers the mapped interfaces (INotifyPropertyChanged, INotifyDataErrorInfo, ...), the WindowsRuntimeInspectable fallback, and generated IDIC proxies. Reframe the instance-event storage as: per-instance field for a concrete projected runtime class, vs a ConditionalWeakTable keyed on the object for a dynamic interface cast. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add a maintainer-focused deep-dive document (
docs/event-infrastructure.md) covering how CsWinRT's Windows Runtime event infrastructure works inWinRT.Runtime2: event sources, event states, the event source cache, registration tokens, the RCW and CCW event flows, lifetime/GC semantics, and the behavior of static events across COM context switches.Motivation
The event infrastructure is one of the more intricate parts of
WinRT.Runtime2— it spansEventSource<T>/EventSourceState<T>/EventSourceCache/EventRegistrationTokenTable<T>, COM reference counting, reference-tracker integration, dynamic interface casting, and context-aware object references. That makes it easy to get subtly wrong when modifying or debugging, and there was no single reference explaining how the pieces fit together. This document provides that reference, with each claim verified against the current code.It supersedes #2320: the content was re-verified end-to-end against the implementation, and several inaccuracies in the original draft were corrected (see Changes).
Changes
docs/event-infrastructure.md(new): a deep-dive reference for theWinRT.Runtime2event infrastructure. It covers the core types (EventRegistrationToken,EventSource<T>and its per-delegate-shape subclasses,EventSourceState<T>,EventSourceCache, andEventRegistrationTokenTable<T>including its token bit layout), the RCW (managed → native) subscribe/unsubscribe flows with object diagrams, the CCW (native → managed) flow, lifetime/GC semantics (what keeps what alive, theEventInvokedelegate / CCW relationship, reference-tracker integration, cleanup on GC, and a thread-safety summary), and an in-depth analysis of the static-event lifetime issue across COM context switches.Key points the document clarifies
_eventSource_<name>field on a concrete projected runtime class, or in aConditionalWeakTable<WindowsRuntimeObject, EventSource<T>>keyed on the object when the interface is reached through a dynamic interface cast ([DynamicInterfaceCastableImplementation]proxies — the mapped interfaces, theWindowsRuntimeInspectablefallback, and generated IDIC proxies). Either way the event source's lifetime is bounded by theWindowsRuntimeObject.ConditionalWeakTableon the activation-factory object reference, which is re-fetched on context switches — an unstable key that can orphan an active subscription. The document analyzes the root cause and a possible fix, noting that context-aware object references already marshal nativeadd/removecalls to the current context (best-effort), so the real trade-off of a stable key is lifetime, not call correctness.Corrections relative to the original draft (#2320)
ConditionalWeakTable(dynamic interface cast), rather than aConditionalWeakTable<WindowsRuntimeObject, EventSource>being the single mechanism.WindowsRuntimeObjectReference.GetActivationFactory(...)(corrected fromWindowsRuntimeActivationFactory, which only exposesGetActivationFactoryUnsafe).EventSourceState<T>.OnDispose(), which captures the saved native pointer before clearing the field so theEventSourceCacheentry is actually removed (addresses the Add docs on Windows Runtime events infrastructure #2320 review comments).