Repository navigation
Consistently apply optional cancellation tokens - #5725
Merged
Merged
Conversation
johnsimons
force-pushed
the
john/cancel_part1
branch
from
August 11, 2026 02:36
308f899 to
0984525
Compare
rbev
approved these changes
Aug 11, 2026
This change addresses the cancellation analyzer debt by standardizing `CancellationToken` parameter naming to `cancellationToken` and making them optional with `= default` where appropriate. This allows for the removal of cancellation-related `Particular.Analyzers` suppressions from several `.editorconfig` files. Pragmas are introduced for cases where implementing existing interfaces prevents adding cancellation tokens.
johnsimons
force-pushed
the
john/cancel_part1
branch
from
August 11, 2026 07:17
0984525 to
7d0f0cc
Compare
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.
Why
Second step in threading
CancellationTokenthrough the platform: the five leaf libraries every hostproject depends on (
ServiceControl.Infrastructure,ServiceControl.Api,ServiceControl.Hosting,ServiceControl.DomainEvents,ServiceControl.Infrastructure.Metrics). These sit underneathServiceControl,ServiceControl.Audit,ServiceControl.Monitoringand the transports/licensingprojects in the reference graph, so getting their signatures settled first means later work is a
one-line change at each call site instead of a redesign.
What this does
Standardizes
CancellationTokenparameters to thePS0003/PS0004/PS0017convention alreadyenforced elsewhere in the solution: non-private members take
CancellationToken cancellationToken = default,private ones take it required, named
cancellationTokeneverywhere (nottoken).Retires 28 of 30 tracked diagnostics; deletes the debt block from four
.editorconfigfiles outright(
Api,Hosting,DomainEvents,Infrastructure.Metrics) and narrowsInfrastructure's to a single file.Two categories of exception:
ServiceControl.Hosting'sPermissionPolicyProviderandRolesClaimsTransformationimplement ASP.NET Core interfaces (IAuthorizationPolicyProvider,IClaimsTransformation) that declare noCancellationTokenparameter.PS0018is unsatisfiable there;suppressed at the site with justification, not fixed.
MessageActionAuditLogExtensions.AuditedOperationtakes aFunc<Task>callbackthat needs to become
Func<CancellationToken, Task>, which touches the lambda at a dozen controller callsites that don't have a token yet. Rather than thread
CancellationToken.Nonethrough as a placeholder,it's deferred to the controllers PR;
ServiceControl.Infrastructure/.editorconfigis narrowed to[Auth/MessageActionAuditLogExtensions.cs]to track just that.Bugs fixed along the way
MetricsReporterHostedService.StopAsynccaughtOperationCanceledExceptionon its own token, butMetricsReporter.Stop()took no token, so nothing could ever throw one — the catch was dead code, andshutdown was effectively unbounded.
Stopnow takes a token and awaits the reporter task viaWaitAsync.DomainEvents.Raiselogged shutdown-triggered cancellation as"Unexpected error publishing domain event"at
Errorlevel. It now rethrowsOperationCanceledExceptionbefore the generic handler, so a normalshutdown doesn't read as an application error.
Notes for reviewers
PS0020catch filters readcancellationTokenSource.IsCancellationRequested, which theanalyzer doesn't recognise (it wants the token, not the source). Fixed as
.Token.IsCancellationRequested, except inAsyncTimer.Stopwhere the source is already disposed bythat point — there the filter uses the caller's token instead, which is also the semantically correct
one since the timer already swallows its own cancellation internally.
Watchdog's startup loop legitimately passes two tokens into the sametry(shutdown, plus a linked15s startup timeout), so
PS0021is suppressed there with an explanation rather than restructured.