Skip to content

Add documentation on the Windows Runtime event infrastructure#2450

Open
Sergio0694 wants to merge 8 commits into
staging/3.0from
user/sergiopedri/event-infrastructure-docs
Open

Add documentation on the Windows Runtime event infrastructure#2450
Sergio0694 wants to merge 8 commits into
staging/3.0from
user/sergiopedri/event-infrastructure-docs

Conversation

@Sergio0694

Copy link
Copy Markdown
Member

Summary

Add a maintainer-focused deep-dive document (docs/event-infrastructure.md) covering how CsWinRT's Windows Runtime event infrastructure works in WinRT.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 spans EventSource<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 the WinRT.Runtime2 event infrastructure. It covers the core types (EventRegistrationToken, EventSource<T> and its per-delegate-shape subclasses, EventSourceState<T>, EventSourceCache, and EventRegistrationTokenTable<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, the EventInvoke delegate / 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

  • Instance-event sources are stored either in a per-instance _eventSource_<name> field on a concrete projected runtime class, or in a ConditionalWeakTable<WindowsRuntimeObject, EventSource<T>> keyed on the object when the interface is reached through a dynamic interface cast ([DynamicInterfaceCastableImplementation] proxies — the mapped interfaces, the WindowsRuntimeInspectable fallback, and generated IDIC proxies). Either way the event source's lifetime is bounded by the WindowsRuntimeObject.
  • Static events have no such bound: they key a ConditionalWeakTable on 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 native add/remove calls 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)

  • Instance events are stored in a per-instance field or a per-object ConditionalWeakTable (dynamic interface cast), rather than a ConditionalWeakTable<WindowsRuntimeObject, EventSource> being the single mechanism.
  • The lazy static factory object reference is produced by WindowsRuntimeObjectReference.GetActivationFactory(...) (corrected from WindowsRuntimeActivationFactory, which only exposes GetActivationFactoryUnsafe).
  • The cross-context analysis was corrected to remove an internal contradiction: context-aware object references marshal native calls to the current context, so a stable cache key's real cost is lifetime (a leak), not call correctness.
  • The GC-cleanup description now matches the current EventSourceState<T>.OnDispose(), which captures the saved native pointer before clearing the field so the EventSourceCache entry is actually removed (addresses the Add docs on Windows Runtime events infrastructure #2320 review comments).

Sergio0694 and others added 8 commits June 17, 2026 14:49
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>
@Sergio0694 Sergio0694 added documentation Improvements or additions to documentation CsWinRT 3.0 labels Jun 17, 2026
@Sergio0694 Sergio0694 requested a review from manodasanW June 17, 2026 23:02
@Sergio0694 Sergio0694 enabled auto-merge (squash) June 17, 2026 23:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CsWinRT 3.0 documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant