Skip to content

Commit 015b13f

Browse files
docs(audience): plain-language comment pass
Rewrites jargon-heavy comments (polyfill, atomic reference, release/ acquire, drain-lock, serialise) into plain language. No code changes. All 181 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6880de6 commit 015b13f

3 files changed

Lines changed: 22 additions & 16 deletions

File tree

src/Packages/Audience/Runtime/Core/ConsentState.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22

33
namespace System.Runtime.CompilerServices
44
{
5-
// Polyfill: record { init } properties compile to init-only setters that
6-
// reference IsExternalInit. .NET Standard 2.1 (Unity) doesn't ship it.
5+
// Unity's .NET runtime doesn't include this type, but the C# compiler
6+
// needs it to exist to build the ConsentState record below. Declaring
7+
// an empty one here gives the compiler what it looks for.
78
internal static class IsExternalInit { }
89
}
910

1011
namespace Immutable.Audience
1112
{
12-
// Immutable consent + userId pair. Stored as a volatile reference so
13-
// readers observe both fields atomically — a SetConsent(Full → Anonymous)
14-
// swap cannot be observed as Anonymous+oldUserId.
13+
// Pairs the consent level with the user id so the two always move
14+
// together. Updates swap the whole pair at once — a reader never sees
15+
// the new consent level alongside a leftover user id.
1516
internal sealed record ConsentState(ConsentLevel Level, string? UserId)
1617
{
1718
internal static readonly ConsentState None = new(ConsentLevel.None, null);

src/Packages/Audience/Runtime/ImmutableAudience.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ namespace Immutable.Audience
1212
// Entry point for the Immutable Audience SDK.
1313
public static class ImmutableAudience
1414
{
15-
// Reference fields are written inside _initLock; readers fence off the volatile _initialized load.
16-
// _state (consent + userId) is volatile for release/acquire visibility; writes
17-
// are serialised under _initLock (Identify / Reset also take it to keep the pair atomic).
15+
// Reference fields are written inside _initLock; readers check the
16+
// `volatile _initialized` flag first so they never see a half-initialised state.
17+
// _state (consent + userId) is `volatile` so a write on one thread is
18+
// visible on any other. Writes happen under _initLock (Identify / Reset
19+
// also take it) so the two fields always move together.
1820
private static AudienceConfig? _config;
1921
private static DiskStore? _store;
2022
private static EventQueue? _queue;
@@ -200,7 +202,8 @@ public static void Identify(string userId, string identityType, Dictionary<strin
200202

201203
AudienceConfig? config;
202204
ConsentLevel level;
203-
// Update _state under _initLock so (consent, userId) stays a consistent pair.
205+
// Update consent + userId under the init lock so they always move
206+
// together — another thread reading _state never sees one half-updated.
204207
lock (_initLock)
205208
{
206209
if (!_initialized) return;
@@ -592,8 +595,9 @@ private static bool CanTrack()
592595
private static Dictionary<string, object>? SnapshotCallerDict(Dictionary<string, object>? src) =>
593596
src != null ? new Dictionary<string, object>(src) : null;
594597

595-
// Re-reads _state under _drainLock to close the Track-races-SetConsent
596-
// window: drops on downgrade to None, strips userId on downgrade to Anonymous.
598+
// Checks the current consent inside the drain lock. If consent has
599+
// since dropped to None the message is discarded. If it dropped to
600+
// Anonymous the userId is stripped.
597601
private static void EnqueueTrack(Dictionary<string, object>? msg)
598602
{
599603
_queue?.EnqueueChecked(msg, m =>

src/Packages/Audience/Runtime/Transport/EventQueue.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,12 @@ internal void Enqueue(Dictionary<string, object>? msg)
6464
_flushGate.Set();
6565
}
6666

67-
// Enqueues under _drainLock, giving the caller a transform callback
68-
// that runs inside the lock. The transform returns the (possibly
69-
// mutated) message or null to drop. Serialises the decision against
70-
// PurgeAll / ApplyAnonymousDowngrade so consent-race leaks and stale
71-
// userIds can be dropped or stripped atomically.
67+
// Queues the message under the drain lock. The caller supplies a
68+
// transform that runs while the lock is held — it can edit the
69+
// message or return null to drop it. Running under the lock means
70+
// PurgeAll and ApplyAnonymousDowngrade can't slip in mid-decision, so
71+
// a Track that races a consent downgrade gets its userId stripped or
72+
// the message dropped before it reaches the queue.
7273
internal void EnqueueChecked(
7374
Dictionary<string, object>? msg,
7475
Func<Dictionary<string, object>, Dictionary<string, object>?>? transform)

0 commit comments

Comments
 (0)