Fix cancellation of running updates and script-handler CancelUpdate (#776, #777) - #897
Merged
Conversation
…776, #777) Issue #777: The cloud sends a cancel with internal workflowId "nodeployment" both during the pre-deployment delay period and when an operator cancels a running deployment. OrchestratorUpdateCallback treated every such cancel as a NOOP, so in-progress deployments could never be cancelled and step handlers polling workflow_is_cancel_requested() never saw the request. Now the "nodeployment" cancel is only ignored when no operation is in progress; while an operation is in progress it is processed as a real cancel. Adds a lock-protected ADUC_Workflow_IsOperationInProgress() accessor to avoid an unlocked cross-thread read. Issue #776: ScriptHandlerImpl::Cancel only set the cancel flag and never ran the script's cancel action, and the top-level steps handler never dispatched Cancel() to the in-progress child, so the script author's CancelUpdate() was never called. Now StepsHandler_Cancel dispatches Cancel() to the actively-running leaf child step, and the script handler runs the script's 'cancel' action to invoke CancelUpdate(). The script cancel runs concurrently with a possibly-running install/apply on the worker thread, so it reads only immutable workflow data and does not write result/state back to the shared handle, avoiding a data race. StepsHandler_Install also stops starting new step work once cancel is requested. Tests: add ADUC_Workflow_IsOperationInProgress test; assert script handler Cancel records the cancellation request. CHANGELOG documents the behavior change and migration guidance for script-handler authors. Example scripts already support --action-cancel, so no script changes are required.
Nox-MSFT
added a commit
that referenced
this pull request
Jun 24, 2026
…n bump Bump the agent semantic version in CMakeLists.txt to 1.4.0 (MINOR 2->4, PATCH 1->0) and clear the 'private-preview' prerelease tag so default builds and packages report 1.4.0. This is a minor (1.4.0) release rather than a patch because it includes customer-affecting behavior changes: Delivery Optimization is no longer the default downloader (curl is) (#893), and cancellation behavior changes for script-handler updates (#776/#777). Expand the CHANGELOG '## Release 1.4.0' section to comprehensively cover all notable changes since 1.3.0 (cancellation fixes, other bug fixes, dependencies/build, documentation, testing), with behavior-change callouts and migration guidance for script-handler authors. Bump the 'Applies to' agent-version annotations in the agent-reference docs to v1.4.0. Note: the cancellation entries (#776/#777) correspond to PR #897, which must merge before the 1.4.0 release is cut.
The next release is 1.4.0 (minor bump due to behavior changes); align this PR's CHANGELOG section heading accordingly.
chgennar
approved these changes
Jun 24, 2026
Nox-MSFT
added a commit
that referenced
this pull request
Jun 25, 2026
Bump the agent semantic version in CMakeLists.txt to 1.4.0 (MINOR 2->4, PATCH 1->0) and clear the 'private-preview' prerelease tag so default builds and packages report 1.4.0. This is a minor (1.4.0) release rather than a patch because it includes customer-affecting behavior changes: Delivery Optimization is no longer the default downloader (curl is) (#893), and cancellation behavior changes for script-handler updates (#776/#777). Expand the CHANGELOG '## Release 1.4.0' section to comprehensively cover all notable changes since 1.3.0 (cancellation fixes, other bug fixes, dependencies/build, documentation, testing), with behavior-change callouts and migration guidance for script-handler authors. Bump the 'Applies to' agent-version annotations in the agent-reference docs to v1.4.0. Note: the cancellation entries (#776/#777) correspond to PR #897, which must merge before the 1.4.0 release is cut.
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
Fixes two related cancellation bugs in the agent. Closes #777 and #776.
"nodeployment"both during the pre-deployment delay period and when an operator cancels an update that is already running on the device.OrchestratorUpdateCallback(added in handle nodeployment case #705) treated every"nodeployment"cancel as a NOOP, so an in-progress deployment could never be cancelled and a step handler pollingworkflow_is_cancel_requested()never observed the request.CancelUpdate()in script-handler scripts is never called.ScriptHandlerImpl::Cancelonly set the cancel flag and never ran the script'scancelaction. In addition, the top-level steps handler'sCancelonly set the flag recursively and never dispatchedCancel()to the in-progress child step, soScriptHandlerImpl::Cancelwas never even reached in production. A blockingdownload/installscript cannot observe a flag, so thecancelaction must run concurrently to interrupt it.Root cause & fix
#777
The
"nodeployment"cancel is now ignored only when no operation is in progress (the genuine delay-period NOOP). While an operation is in progress, it falls through toADUC_Workflow_HandlePropertyUpdate, which routes aCancelaction to the in-progress workflow (workflow_initalready skips manifest/signature validation forCancelactions, so the manifest-less payload parses correctly). A new lock-protected accessorADUC_Workflow_IsOperationInProgress()is used so the orchestrator callback does not read theOperationInProgressflag without the workflow lock.#776
StepsHandler_Cancelnow dispatchesCancel()to the actively-running leaf child step (tracked under a mutex around the childInstall/Applycalls), so the script handler's cancel action is actually invoked. It also stops starting new step work once cancellation has been requested.ScriptHandlerImpl::Cancelnow runs the script'scancelaction (--action-cancel), invoking the author'sCancelUpdate(). Because this can run on the main thread while the worker thread is still executing the step'sinstall/applyscript, the cancel path uses a non-mutating helper that reads only immutable workflow data and writes to its own dedicated result file — it never writes result/state back to the shared workflow handle, avoiding a data race.Only the script handler gains active-cancel behavior. The other step handlers'
Cancel()(swupdate v2, apt, simulator, reference-steps) are existing flag-set/no-op implementations, so dispatching to them is harmless.Behavior change & migration
This is a behavior change for script-handler authors: a script's
CancelUpdate()function is now actually invoked on cancellation (previously dead code). See the newCHANGELOG.mdentry for migration guidance — in short,CancelUpdate()should be idempotent, safe to run concurrently with an in-progressInstallUpdate()/DownloadUpdate(), and return success when there is nothing to cancel. The sampleexample-installscript.shalready satisfies this, so no example-script changes are required. It cannot break the cancellation outcome and does not affect normal (non-cancel) deployments.Testing
Built locally (Ubuntu 24.04, Ninja, Debug,
--build-unit-tests): 350/350 targets includingAducIotAgent, no errors. Relevant unit tests pass:agent_workflow_unit_test— 66 cases (incl. newADUC_Workflow_IsOperationInProgresstest)script_handler_unit_test— 30 cases (compiles both script + steps handlers; cancel test asserts the cancel-requested flag is recorded)steps_handler_unit_tests— 15 casesFiles changed
src/agent/adu_core_interface/src/adu_core_interface.c— conditional"nodeployment"NOOPsrc/adu_workflow/src/agent_workflow.c,inc/aduc/agent_workflow.h—ADUC_Workflow_IsOperationInProgress()src/extensions/update_manifest_handlers/steps_handler/src/steps_handler.cpp— active-child tracking, cancel dispatch, loop cancel checkssrc/extensions/step_handlers/script_handler/src/script_handler.cpp— run scriptcancelaction (non-mutating)CHANGELOG.mdOut of scope
The SWUpdate v2 handler has the same latent "Cancel only sets the flag" gap; it was left unchanged to keep this PR focused on the reported issues. Can be addressed as a follow-up.