Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions src/Sentry.Maui/Internal/MauiEventsBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -420,21 +420,19 @@ internal void StartUiTransaction(string name)
if (CurrentUiTx is not null)
{
// Idle timer will clean up any previous UI transaction, but we don't want any more child spans on it
_hub.ConfigureScope(scope => scope.ResetTransaction(CurrentUiTx));
_hub.ConfigureScope(static (scope, tx) => scope.ResetTransaction(tx), CurrentUiTx);
}
CurrentUiTx = null;

var context = new TransactionContext(name, UserInteractionClickOp)
{
NameSource = TransactionNameSource.Component
};
CurrentUiTx = _hub is IHubInternal internalHub
? internalHub.StartTransaction(context, _options.AutoTransactionIdleTimeout)
// never called in practice... all our hubs implement IHubInternal
: _hub.StartTransaction(context);

// Only bind to scope if there is no other transaction already there (user-created or SDK-owned navigation).
_hub.ConfigureScope(scope => scope.Transaction ??= CurrentUiTx);
// Bind to scope only if no other transaction is already there (user-installed or SDK-owned navigation).
_hub.ConfigureScope(static (scope, tx) => scope.Transaction ??= tx, CurrentUiTx);
}

// Application Events
Expand Down Expand Up @@ -472,13 +470,13 @@ private void OnWindowOnStopped(object? sender, EventArgs _)
if (uiTx.Spans.Count > 0)
{
// Only finish UI transactions with child spans.
// Childless transactions will be discarded by the idle timeout.
// Childless transactions get discarded by the idle timeout.
uiTx.Finish(SpanStatus.Ok);
}
else
{
// No child spans, so just detach from scope and let idle timeout handle it.
_hub.ConfigureScope(scope => scope.ResetTransaction(CurrentUiTx));
// No child spans, so just detach from scope and let idle timeout handle it
_hub.ConfigureScope(static (scope, tx) => scope.ResetTransaction(tx), uiTx);
}
}
CurrentUiTx = null;
Expand Down
25 changes: 23 additions & 2 deletions src/Sentry/SpanTracer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,32 @@ public sealed class SpanTracer : IBaseTracer, ISpan
/// <inheritdoc />
public DateTimeOffset StartTimestamp { get; internal set; }

private DateTimeOffset? _endTimestamp;
private bool _isFinished;

/// <inheritdoc />
public DateTimeOffset? EndTimestamp { get; internal set; }
public DateTimeOffset? EndTimestamp
{
get => Volatile.Read(ref _isFinished) ? _endTimestamp : null;
internal set
{
// Ordering is load-bearing: the gate-flip is release-ordered against the data write,
// so lock-free readers gating on `_isFinished` see a consistent (timestamp, finished) pair.
if (value is null)
{
Volatile.Write(ref _isFinished, false);
_endTimestamp = null;
}
else
{
_endTimestamp = value;
Volatile.Write(ref _isFinished, true);
}
}
}

/// <inheritdoc />
public bool IsFinished => EndTimestamp is not null;
public bool IsFinished => Volatile.Read(ref _isFinished);

// Not readonly because of deserialization
internal Dictionary<string, Measurement>? InternalMeasurements { get; private set; }
Expand Down
Loading
Loading