Skip to content

Fix cancellation of running updates and script-handler CancelUpdate (#776, #777) - #897

Merged
Nox-MSFT merged 2 commits into
developfrom
user/nox-msft/issue-776-777
Jun 24, 2026
Merged

Fix cancellation of running updates and script-handler CancelUpdate (#776, #777)#897
Nox-MSFT merged 2 commits into
developfrom
user/nox-msft/issue-776-777

Conversation

@Nox-MSFT

Copy link
Copy Markdown
Contributor

Summary

Fixes two related cancellation bugs in the agent. Closes #777 and #776.

  • Cancel a running Update not working as expected #777 — Cancel of a running update is ignored. The service delivers a cancel with the internal workflow id "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 polling workflow_is_cancel_requested() never observed the request.
  • CancelUpdate in script handler is never called #776CancelUpdate() in script-handler scripts is never called. ScriptHandlerImpl::Cancel only set the cancel flag and never ran the script's cancel action. In addition, the top-level steps handler's Cancel only set the flag recursively and never dispatched Cancel() to the in-progress child step, so ScriptHandlerImpl::Cancel was never even reached in production. A blocking download/install script cannot observe a flag, so the cancel action 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 to ADUC_Workflow_HandlePropertyUpdate, which routes a Cancel action to the in-progress workflow (workflow_init already skips manifest/signature validation for Cancel actions, so the manifest-less payload parses correctly). A new lock-protected accessor ADUC_Workflow_IsOperationInProgress() is used so the orchestrator callback does not read the OperationInProgress flag without the workflow lock.

#776

  • StepsHandler_Cancel now dispatches Cancel() to the actively-running leaf child step (tracked under a mutex around the child Install/Apply calls), so the script handler's cancel action is actually invoked. It also stops starting new step work once cancellation has been requested.
  • ScriptHandlerImpl::Cancel now runs the script's cancel action (--action-cancel), invoking the author's CancelUpdate(). Because this can run on the main thread while the worker thread is still executing the step's install/apply script, 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 new CHANGELOG.md entry for migration guidance — in short, CancelUpdate() should be idempotent, safe to run concurrently with an in-progress InstallUpdate()/DownloadUpdate(), and return success when there is nothing to cancel. The sample example-installscript.sh already 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 including AducIotAgent, no errors. Relevant unit tests pass:

  • agent_workflow_unit_test — 66 cases (incl. new ADUC_Workflow_IsOperationInProgress test)
  • 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 cases

Note: the ADO e2e pipeline (definitionId=155, adu-linux-client) should be run against this branch to validate the concurrent cancel path end-to-end.

Files changed

  • src/agent/adu_core_interface/src/adu_core_interface.c — conditional "nodeployment" NOOP
  • src/adu_workflow/src/agent_workflow.c, inc/aduc/agent_workflow.hADUC_Workflow_IsOperationInProgress()
  • src/extensions/update_manifest_handlers/steps_handler/src/steps_handler.cpp — active-child tracking, cancel dispatch, loop cancel checks
  • src/extensions/step_handlers/script_handler/src/script_handler.cpp — run script cancel action (non-mutating)
  • tests + CHANGELOG.md

Out 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.

…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
Nox-MSFT requested a review from chgennar June 24, 2026 19:12
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.
@Nox-MSFT
Nox-MSFT merged commit 9b7e8be into develop Jun 24, 2026
22 of 23 checks passed
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cancel a running Update not working as expected

2 participants