diff --git a/README.md b/README.md index 91b38d3d7..38ac14641 100644 --- a/README.md +++ b/README.md @@ -25,3 +25,4 @@ This repository contains the PanelSwWix4: A custom WiX Toolset codebase - Support sending custom messages on embedded pipe - Best effort to log premature termination of companion process - Monitor UX folder and re-extract any UX payloads that were deleted for any reason +- Reorder cache actions: moves non-executing package caching to the end of the plan. This optimizes run time as packages that are cached but not executed will not stand in the way of executing packages. \ No newline at end of file diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp index 26fb398bd..5ef3e6293 100644 --- a/src/burn/engine/apply.cpp +++ b/src/burn/engine/apply.cpp @@ -662,6 +662,10 @@ extern "C" HRESULT ApplyCache( } break; + case BURN_CACHE_ACTION_TYPE_DELAYABLE_START: + case BURN_CACHE_ACTION_TYPE_DELAYABLE_END: + break; + default: AssertSz(FALSE, "Unknown cache action."); break; @@ -2407,7 +2411,6 @@ static void DoRollbackCache( __in DWORD dwCheckpoint ) { - HRESULT hr = S_OK; BURN_PACKAGE* pPackage = NULL; DWORD dwLastCheckpoint = 0; @@ -2432,7 +2435,7 @@ static void DoRollbackCache( { if (dwLastCheckpoint <= dwCheckpoint) // only rollback when it was attempted to be cached. { - hr = CleanPackage(pPlan->pCache, hPipe, pPackage); + CleanPackage(pPlan->pCache, hPipe, pPackage); } } else if (pPackage->fCanAffectRegistration) diff --git a/src/burn/engine/engine.mc b/src/burn/engine/engine.mc index 0cd4cfffe..e7a12b328 100644 --- a/src/burn/engine/engine.mc +++ b/src/burn/engine/engine.mc @@ -1321,5 +1321,5 @@ MessageId=707 Severity=Success SymbolicName=MSG_REORDERING_PACKAGE Language=English -Moving packages '%1!ls!' to the end of the queue since it is not planned to execute. +Moving packages '%1!ls!' caching to the end of the queue since it is not planned to execute. . diff --git a/src/burn/engine/package.cpp b/src/burn/engine/package.cpp index 3af5ee877..06496d5d5 100644 --- a/src/burn/engine/package.cpp +++ b/src/burn/engine/package.cpp @@ -142,8 +142,6 @@ extern "C" HRESULT PackagesParseFromXml( { BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; - pPackage->dwPackageIndex = i; - hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); ExitOnFailure(hr, "Failed to get next node."); diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h index bf62e690f..3a17e1f0f 100644 --- a/src/burn/engine/package.h +++ b/src/burn/engine/package.h @@ -285,8 +285,6 @@ typedef struct _BURN_PACKAGE DWORD64 qwInstallSize; DWORD64 qwSize; - DWORD dwPackageIndex; - BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryForward; // used during install and repair. BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryBackward; // used during uninstall. diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp index cd116c1e2..f10a7e75e 100644 --- a/src/burn/engine/plan.cpp +++ b/src/burn/engine/plan.cpp @@ -42,14 +42,6 @@ static HRESULT PlanPackagesHelper( __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables ); -static void PlanReorderPackages( - __in BURN_PACKAGE* rgPackages, - __in DWORD cPackages - ); -static void PlanRestorePackagesOrder( - __in BURN_PACKAGE* rgPackages, - __in DWORD cPackages - ); static HRESULT InitializePackage( __in BURN_PLAN* pPlan, __in BURN_USER_EXPERIENCE* pUX, @@ -154,6 +146,12 @@ static void FinalizePatchActions( __in BURN_EXECUTE_ACTION* rgActions, __in DWORD cActions ); +static void FinalizeCacheActions( + __in BURN_CACHE_ACTION* rgCacheActions, + __in DWORD cCacheActions, + __in BURN_EXECUTE_ACTION* rgActions, + __in DWORD cActions + ); static void CalculateExpectedRegistrationStates( __in BURN_PACKAGE* rgPackages, __in DWORD cPackages @@ -295,7 +293,6 @@ extern "C" void PlanReset( { ResetPlannedPackageState(&pPackages->rgPackages[i]); } - PlanRestorePackagesOrder(pPackages->rgPackages, pPackages->cPackages); } ResetPlannedPayloadGroupState(pLayoutPayloads); @@ -883,12 +880,6 @@ static HRESULT PlanPackagesHelper( } } - // Best effort to reorder the packages: Executing packages first. This will ensure cache-only packages will not block executing packages until they get cached - if (!fReverseOrder) - { - PlanReorderPackages(rgPackages, cPackages); - } - // Plan the packages. for (DWORD i = 0; i < cPackages; ++i) { @@ -1067,9 +1058,27 @@ static HRESULT ProcessPackage( { if (ForceCache(pPlan, pPackage)) { + BURN_CACHE_ACTION* pCacheAction = NULL; + + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to plan cache action."); + pCacheAction->type = BURN_CACHE_ACTION_TYPE_DELAYABLE_START; + + hr = AppendRollbackCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to plan cache action."); + pCacheAction->type = BURN_CACHE_ACTION_TYPE_DELAYABLE_START; + hr = AddCachePackage(pPlan, pPackage, TRUE); ExitOnFailure(hr, "Failed to plan cache package."); + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to plan cache action."); + pCacheAction->type = BURN_CACHE_ACTION_TYPE_DELAYABLE_END; + + hr = AppendRollbackCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to plan cache action."); + pCacheAction->type = BURN_CACHE_ACTION_TYPE_DELAYABLE_END; + if (pPackage->fPerMachine) { pPlan->fPerMachine = TRUE; @@ -1294,8 +1303,34 @@ extern "C" HRESULT PlanExecutePackage( if (BURN_CACHE_PACKAGE_TYPE_NONE != pPackage->executeCacheType || BURN_CACHE_PACKAGE_TYPE_NONE != pPackage->rollbackCacheType) { + if ((pPackage->execute == BOOTSTRAPPER_ACTION_STATE_NONE) && (pPackage->rollback == BOOTSTRAPPER_ACTION_STATE_NONE)) + { + BURN_CACHE_ACTION* pCacheAction = NULL; + + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to plan cache action."); + pCacheAction->type = BURN_CACHE_ACTION_TYPE_DELAYABLE_START; + + hr = AppendRollbackCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to plan cache action."); + pCacheAction->type = BURN_CACHE_ACTION_TYPE_DELAYABLE_START; + } + hr = AddCachePackage(pPlan, pPackage, BURN_CACHE_PACKAGE_TYPE_REQUIRED == pPackage->executeCacheType); ExitOnFailure(hr, "Failed to plan cache package."); + + if ((pPackage->execute == BOOTSTRAPPER_ACTION_STATE_NONE) && (pPackage->rollback == BOOTSTRAPPER_ACTION_STATE_NONE)) + { + BURN_CACHE_ACTION* pCacheAction = NULL; + + hr = AppendCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to plan cache action."); + pCacheAction->type = BURN_CACHE_ACTION_TYPE_DELAYABLE_END; + + hr = AppendRollbackCacheAction(pPlan, &pCacheAction); + ExitOnFailure(hr, "Failed to plan cache action."); + pCacheAction->type = BURN_CACHE_ACTION_TYPE_DELAYABLE_END; + } } // Add execute actions. @@ -1849,6 +1884,10 @@ extern "C" HRESULT PlanFinalizeActions( RemoveUnnecessaryActions(FALSE, pPlan->rgRollbackActions, pPlan->cRollbackActions); + FinalizeCacheActions(pPlan->rgCacheActions, pPlan->cCacheActions, pPlan->rgExecuteActions, pPlan->cExecuteActions); + + FinalizeCacheActions(pPlan->rgRollbackCacheActions, pPlan->cRollbackCacheActions, pPlan->rgRollbackActions, pPlan->cRollbackActions); + return hr; } @@ -2801,6 +2840,164 @@ static void FinalizePatchActions( } } +static void FinalizeCacheActions( + __in BURN_CACHE_ACTION* rgCacheActions, + __in DWORD cCacheActions, + __in BURN_EXECUTE_ACTION* rgActions, + __in DWORD cActions + ) +{ + HRESULT hr = S_OK; + DWORD cDelayableCacheActions = 0; + DWORD cDelayableExecuteActions = 0; + DWORD iNextAction = 0; + DWORD iNextDelayableAction = 0; + DWORD iFirstDelayedCacheAction = 0; + DWORD iFirstDelayedExecuteAction = 0; + BURN_CACHE_ACTION* rgReorderedCacheActions = NULL; + BURN_EXECUTE_ACTION* rgReorderedActions = NULL; + BOOL fDelayable = FALSE; + + for (DWORD i = 0; i < cCacheActions; ++i) + { + BURN_CACHE_ACTION* pCacheAction = rgCacheActions + i; + + if (pCacheAction->type == BURN_CACHE_ACTION_TYPE_DELAYABLE_START) + { + ExitOnNull(!fDelayable, hr, E_UNEXPECTED, "Unexpected start delayable cache action"); + fDelayable = TRUE; + } + + if (fDelayable) + { + ++cDelayableCacheActions; + + if (pCacheAction->type == BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT) + { + cDelayableExecuteActions += 2; // Checkpoint + syncpoint + } + } + + if (pCacheAction->type == BURN_CACHE_ACTION_TYPE_DELAYABLE_END) + { + ExitOnNull(fDelayable, hr, E_UNEXPECTED, "Unexpected end delayable cache action"); + fDelayable = FALSE; + } + } + + if ((cDelayableCacheActions == 0) || (cDelayableCacheActions == cCacheActions)) + { + ExitFunction(); + } + + iFirstDelayedCacheAction = cCacheActions - cDelayableCacheActions; + iFirstDelayedExecuteAction = cActions - cDelayableExecuteActions; + + rgReorderedCacheActions = (BURN_CACHE_ACTION*)MemAlloc(sizeof(BURN_CACHE_ACTION) * cCacheActions, FALSE); + ExitOnNull(rgReorderedCacheActions, hr, E_OUTOFMEMORY, "Failed to allocate memory"); + + rgReorderedActions = (BURN_EXECUTE_ACTION*)MemAlloc(sizeof(BURN_EXECUTE_ACTION) * cActions, FALSE); + ExitOnNull(rgReorderedActions, hr, E_OUTOFMEMORY, "Failed to allocate memory"); + + fDelayable = FALSE; + iNextAction = 0; + iNextDelayableAction = iFirstDelayedCacheAction; + for (DWORD i = 0; i < cCacheActions; ++i) + { + BURN_CACHE_ACTION* pSourceAction = rgCacheActions + i; + BURN_CACHE_ACTION* pDestAction = NULL; + + if (pSourceAction->type == BURN_CACHE_ACTION_TYPE_DELAYABLE_START) + { + fDelayable = TRUE; + } + + if (fDelayable) + { + if (pSourceAction->type == BURN_CACHE_ACTION_TYPE_PACKAGE) + { + LogId(REPORT_STANDARD, MSG_REORDERING_PACKAGE, pSourceAction->package.pPackage->sczId); + } + + pDestAction = rgReorderedCacheActions + iNextDelayableAction; + ++iNextDelayableAction; + } + else + { + pDestAction = rgReorderedCacheActions + iNextAction; + ++iNextAction; + } + + if (pSourceAction->type == BURN_CACHE_ACTION_TYPE_DELAYABLE_END) + { + fDelayable = FALSE; + } + + memcpy_s(pDestAction, sizeof(BURN_CACHE_ACTION), pSourceAction, sizeof(BURN_CACHE_ACTION)); + } + ExitOnNull((iNextDelayableAction == cCacheActions), hr, E_UNEXPECTED, "Unexpected last delayable cache location: %u instead of %u", iNextDelayableAction, cCacheActions); + + // Delay cache sync actions in the execute sequence + iNextAction = 0; + iNextDelayableAction = iFirstDelayedExecuteAction; + for (DWORD i = 0; i < cActions; ++i) + { + BURN_EXECUTE_ACTION* pSourceAction = rgActions + i; + BURN_EXECUTE_ACTION* pDestAction = NULL; + fDelayable = FALSE; + + if ((i < (cActions - 1)) && (pSourceAction->type == BURN_EXECUTE_ACTION_TYPE_CHECKPOINT) && (rgActions[i + 1].type == BURN_EXECUTE_ACTION_TYPE_WAIT_CACHE_PACKAGE)) + { + for (DWORD j = iFirstDelayedCacheAction; j < cCacheActions; ++j) + { + BURN_CACHE_ACTION* pCacheAction = rgReorderedCacheActions + j; + if ((pCacheAction->type == BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT) && (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pCacheAction->syncpoint.pPackage->sczId, -1, rgActions[i + 1].waitCachePackage.pPackage->sczId, -1))) + { + fDelayable = TRUE; + break; + } + } + } + + if (fDelayable) + { + // When we're delaying, we move both actions to the back + pDestAction = rgReorderedActions + iNextDelayableAction; + memcpy_s(pDestAction, 2 * sizeof(BURN_EXECUTE_ACTION), pSourceAction, 2 * sizeof(BURN_EXECUTE_ACTION)); + + iNextDelayableAction += 2; + ++i; + } + else + { + pDestAction = rgReorderedActions + iNextAction; + memcpy_s(pDestAction, sizeof(BURN_EXECUTE_ACTION), pSourceAction, sizeof(BURN_EXECUTE_ACTION)); + + ++iNextAction; + } + } + ExitOnNull((iNextDelayableAction == cActions), hr, E_UNEXPECTED, "Unexpected last delayable execute location: %u instead of %u", iNextDelayableAction, cActions); + + // Nullify rollback boundaries in delayed execute checkpoints. Otherwise we might rollback past boudaries + for (DWORD i = 0; i < cActions; ++i) + { + BURN_EXECUTE_ACTION* pAction = rgActions + i; + + if (pAction->type == BURN_EXECUTE_ACTION_TYPE_CHECKPOINT) + { + //TODO: Need to set new checkpoint numbers? + pAction->checkpoint.pActiveRollbackBoundary = NULL; + } + } + + memcpy_s(rgCacheActions, cCacheActions * sizeof(BURN_CACHE_ACTION), rgReorderedCacheActions, cCacheActions * sizeof(BURN_CACHE_ACTION)); + memcpy_s(rgActions, cActions * sizeof(BURN_EXECUTE_ACTION), rgReorderedActions, cActions * sizeof(BURN_EXECUTE_ACTION)); + +LExit: + ReleaseMem(rgReorderedCacheActions); + ReleaseMem(rgReorderedActions); +} + static void CalculateExpectedRegistrationStates( __in BURN_PACKAGE* rgPackages, __in DWORD cPackages @@ -3055,6 +3252,14 @@ static void CacheActionLog( LogStringLine(PlanDumpLevel, "%ls action[%u]: SIGNAL_SYNCPOINT package id: %ls, event handle: 0x%p", wzBase, iAction, pAction->syncpoint.pPackage->sczId, pAction->syncpoint.pPackage->hCacheEvent); break; + case BURN_CACHE_ACTION_TYPE_DELAYABLE_START: + LogStringLine(PlanDumpLevel, "%ls action[%u]: delayable start", wzBase, iAction); + break; + + case BURN_CACHE_ACTION_TYPE_DELAYABLE_END: + LogStringLine(PlanDumpLevel, "%ls action[%u]: delayable end", wzBase, iAction); + break; + default: AssertSz(FALSE, "Unknown cache action type."); break; @@ -3278,87 +3483,3 @@ extern "C" void PlanDump( LogStringLine(PlanDumpLevel, "--- End plan dump ---"); } - -// Reorder the packages: Executing packages first. This will ensure cache-only packages will not block executing packages until they get cached -static void PlanReorderPackages( - __in BURN_PACKAGE* rgPackages, - __in DWORD cPackages -) -{ - HRESULT hr = S_OK; - BURN_PACKAGE* rgOrderedPackages = NULL; - DWORD iNextExecutingLocation = 0; - DWORD iNextNonExecutingLocation = 0; - DWORD cExecutingPackages = 0; - - if (cPackages < 2) - { - ExitFunction(); - } - - rgOrderedPackages = (BURN_PACKAGE*)MemAlloc(cPackages * sizeof(BURN_PACKAGE), TRUE); - ExitOnNull(rgOrderedPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory to reorder packages"); - - // Count the packages that can be moved to the end - for (DWORD i = 0; i < cPackages; ++i) - { - BOOL fExecuting = rgPackages[i].compatiblePackage.fRequested || (BOOTSTRAPPER_REQUEST_STATE_NONE != rgPackages[i].requested && BOOTSTRAPPER_REQUEST_STATE_CACHE != rgPackages[i].requested); - if (fExecuting) - { - ++cExecutingPackages; - } - } - if ((cExecutingPackages == 0) || (cExecutingPackages == cPackages)) - { - ExitFunction(); - } - - // Reorder the packages on the temporary array - iNextExecutingLocation = 0; - iNextNonExecutingLocation = cExecutingPackages; - for (DWORD i = 0; i < cPackages; ++i) - { - BOOL fExecuting = rgPackages[i].compatiblePackage.fRequested || (BOOTSTRAPPER_REQUEST_STATE_NONE != rgPackages[i].requested && BOOTSTRAPPER_REQUEST_STATE_CACHE != rgPackages[i].requested); - if (fExecuting) - { - memcpy_s(&rgOrderedPackages[iNextExecutingLocation], sizeof(BURN_PACKAGE), &rgPackages[i], sizeof(BURN_PACKAGE)); - ++iNextExecutingLocation; - } - else - { - LogId(REPORT_STANDARD, MSG_REORDERING_PACKAGE, rgPackages[i].sczId); - - memcpy_s(&rgOrderedPackages[iNextNonExecutingLocation], sizeof(BURN_PACKAGE), &rgPackages[i], sizeof(BURN_PACKAGE)); - ++iNextNonExecutingLocation; - } - } - - // Copy temp array to original - memcpy_s(rgPackages, cPackages * sizeof(BURN_PACKAGE), rgOrderedPackages, cPackages * sizeof(BURN_PACKAGE)); - -LExit: - ReleaseMem(rgOrderedPackages); -} - -// Unlike PlanReorderPackages, this must succeed. Otherwise a wrong order may be planned on a re-plan -static void PlanRestorePackagesOrder( - __in BURN_PACKAGE* rgPackages, - __in DWORD cPackages -) -{ - for (DWORD i = 0; i < cPackages; ++i) - { - // https://en.wikipedia.org/wiki/100_prisoners_problem - // In the worst case scenario, this loop would take cPackages cycles, after which all packages would be in place - // In the common case, each time this loop would take just a few cycles which would order some of the packages - // The entire for+while loops can take no longer than 2*cPackages cycles - while (rgPackages[i].dwPackageIndex != i) - { - BURN_PACKAGE tmpPackage = {}; - - memcpy_s(&tmpPackage, sizeof(BURN_PACKAGE), &rgPackages[i], sizeof(BURN_PACKAGE)); - memcpy_s(&rgPackages[i], sizeof(BURN_PACKAGE), &rgPackages[tmpPackage.dwPackageIndex], sizeof(BURN_PACKAGE)); - memcpy_s(&rgPackages[tmpPackage.dwPackageIndex], sizeof(BURN_PACKAGE), &tmpPackage, sizeof(BURN_PACKAGE)); - } - } -} diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h index 60bfabbd9..1cdcb8349 100644 --- a/src/burn/engine/plan.h +++ b/src/burn/engine/plan.h @@ -35,6 +35,8 @@ enum BURN_CACHE_ACTION_TYPE BURN_CACHE_ACTION_TYPE_ROLLBACK_PACKAGE, BURN_CACHE_ACTION_TYPE_SIGNAL_SYNCPOINT, BURN_CACHE_ACTION_TYPE_CONTAINER, + BURN_CACHE_ACTION_TYPE_DELAYABLE_START, + BURN_CACHE_ACTION_TYPE_DELAYABLE_END, }; enum BURN_EXECUTE_ACTION_TYPE diff --git a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj index c86c1e483..6554fd208 100644 --- a/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj +++ b/src/burn/test/BurnUnitTest/BurnUnitTest.vcxproj @@ -91,6 +91,7 @@ + diff --git a/src/burn/test/BurnUnitTest/PlanTest.cpp b/src/burn/test/BurnUnitTest/PlanTest.cpp index 3564fa36f..afa4e0c34 100644 --- a/src/burn/test/BurnUnitTest/PlanTest.cpp +++ b/src/burn/test/BurnUnitTest/PlanTest.cpp @@ -18,6 +18,7 @@ static LPCWSTR wzSingleMsiManifestFileName = L"BasicFunctionality_BundleA_manife static LPCWSTR wzSingleMsuManifestFileName = L"MsuPackageFixture_manifest.xml"; static LPCWSTR wzSlipstreamManifestFileName = L"Slipstream_BundleA_manifest.xml"; static LPCWSTR wzSlipstreamModifiedManifestFileName = L"Slipstream_BundleA_modified_manifest.xml"; +static LPCWSTR wzCacheReorderManifestFileName = L"CacheReorder_BundleAv1_manifest.xml"; static BOOL vfUsePackageRequestState = FALSE; static BOOTSTRAPPER_REQUEST_STATE vPackageRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; @@ -659,6 +660,159 @@ namespace Bootstrapper ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); } + [Fact] + void CacheReorderInstallTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzCacheReorderManifestFileName, pEngineState); + DetectPackagesAsAbsent(pEngineState); + DetectPackageAsPresentAndCached(&pEngineState->packages.rgPackages[1]); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + NativeAssert::StringEqual(L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", pPlan->wzBundleId); + NativeAssert::StringEqual(L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", pPlan->wzBundleProviderKey); + Assert::Equal(FALSE, pPlan->fEnabledForwardCompatibleBundle); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(TRUE, pPlan->fCanAffectMachineState); + Assert::Equal(FALSE, pPlan->fDisableRollback); + Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); + Assert::Equal(BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE | BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_PROVIDER_KEY, pPlan->dwRegistrationOperations); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + ValidateDependentRegistrationAction(pPlan, fRollback, dwIndex++, TRUE, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}"); + Assert::Equal(dwIndex, pPlan->cRegistrationActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateDependentRegistrationAction(pPlan, fRollback, dwIndex++, FALSE, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}"); + Assert::Equal(dwIndex, pPlan->cRollbackRegistrationActions); + + fRollback = FALSE; + dwIndex = 0; + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA", TRUE, BURN_CACHE_PACKAGE_TYPE_REQUIRED, BURN_CACHE_PACKAGE_TYPE_NONE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 12); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageC", TRUE, BURN_CACHE_PACKAGE_TYPE_REQUIRED, BURN_CACHE_PACKAGE_TYPE_NONE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"PackageC"); + ValidateCacheDelayableStart(pPlan, fRollback, dwIndex++); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 7); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageB", TRUE, BURN_CACHE_PACKAGE_TYPE_REQUIRED, BURN_CACHE_PACKAGE_TYPE_NONE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"PackageB"); + ValidateCacheDelayableEnd(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageC"); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 12); + ValidateCacheDelayableStart(pPlan, fRollback, dwIndex++); + ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageB"); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 7); + ValidateCacheDelayableEnd(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(522548ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + DWORD dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", registerActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", registerActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + dwExecuteCheckpointId += 2; // skip delayed package cache + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", registerActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", registerActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"PackageC"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", registerActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_INSTALL, BURN_MSI_PROPERTY_INSTALL, INSTALLUILEVEL_NONE, FALSE, BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", registerActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++); + dwExecuteCheckpointId = 8; // Delayed cache checkpoint + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + dwExecuteCheckpointId = 2; + ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", unregisterActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageA", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", unregisterActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageB"); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageB", unregisterActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageB", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", unregisterActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteUncachePackage(pPlan, fRollback, dwIndex++, L"PackageC"); + dwExecuteCheckpointId += 1; // cache checkpoints + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageC", unregisterActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteMsiPackage(pPlan, fRollback, dwIndex++, L"PackageC", BOOTSTRAPPER_ACTION_STATE_UNINSTALL, BURN_MSI_PROPERTY_UNINSTALL, INSTALLUILEVEL_NONE, FALSE, BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER, 0); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageC", L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", unregisterActions1, 1); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++); + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(2ul, pPlan->cExecutePackagesTotal); + Assert::Equal(5ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{E6469F05-BDC8-4EB8-B218-67412543EFAA}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(3ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[1], L"PackageB", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_PRESENT); + } + [Fact] void MsiTransactionUninstallTest() { @@ -1686,15 +1840,19 @@ namespace Bootstrapper fRollback = FALSE; dwIndex = 0; + ValidateCacheDelayableStart(pPlan, fRollback, dwIndex++); ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA", TRUE, BURN_CACHE_PACKAGE_TYPE_REQUIRED, BURN_CACHE_PACKAGE_TYPE_NONE); ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheDelayableEnd(pPlan, fRollback, dwIndex++); Assert::Equal(dwIndex, pPlan->cCacheActions); fRollback = TRUE; dwIndex = 0; + ValidateCacheDelayableStart(pPlan, fRollback, dwIndex++); ValidateCacheRollbackPackage(pPlan, fRollback, dwIndex++, L"PackageA"); ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCacheDelayableEnd(pPlan, fRollback, dwIndex++); Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(168715ull, pPlan->qwCacheSizeTotal); @@ -2757,33 +2915,34 @@ namespace Bootstrapper fRollback = FALSE; dwIndex = 0; - ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); - ValidateCachePackage(pPlan, fRollback, dwIndex++, L"NetFx48Web", TRUE, BURN_CACHE_PACKAGE_TYPE_REQUIRED, BURN_CACHE_PACKAGE_TYPE_NONE); - ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"NetFx48Web"); ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 3); ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PatchA", TRUE, BURN_CACHE_PACKAGE_TYPE_REQUIRED, BURN_CACHE_PACKAGE_TYPE_NONE); ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"PatchA"); ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 5); ValidateCachePackage(pPlan, fRollback, dwIndex++, L"PackageA", TRUE, BURN_CACHE_PACKAGE_TYPE_REQUIRED, BURN_CACHE_PACKAGE_TYPE_NONE); ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"PackageA"); + ValidateCacheDelayableStart(pPlan, fRollback, dwIndex++); + ValidateCacheCheckpoint(pPlan, fRollback, dwIndex++, 1); + ValidateCachePackage(pPlan, fRollback, dwIndex++, L"NetFx48Web", TRUE, BURN_CACHE_PACKAGE_TYPE_REQUIRED, BURN_CACHE_PACKAGE_TYPE_NONE); + ValidateCacheSignalSyncpoint(pPlan, fRollback, dwIndex++, L"NetFx48Web"); + ValidateCacheDelayableEnd(pPlan, fRollback, dwIndex++); Assert::Equal(dwIndex, pPlan->cCacheActions); fRollback = TRUE; dwIndex = 0; + ValidateCacheDelayableStart(pPlan, fRollback, dwIndex++); + ValidateCacheDelayableEnd(pPlan, fRollback, dwIndex++); Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); Assert::Equal(6130592ull, pPlan->qwCacheSizeTotal); fRollback = FALSE; dwIndex = 0; - DWORD dwExecuteCheckpointId = 2; + DWORD dwExecuteCheckpointId = 4; BURN_EXECUTE_ACTION* pExecuteAction = NULL; ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); dwExecuteCheckpointId += 1; // cache checkpoints - ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"NetFx48Web"); - ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); - dwExecuteCheckpointId += 1; // cache checkpoints ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"PatchA"); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"PackageA"); @@ -2803,6 +2962,9 @@ namespace Bootstrapper ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++); + dwExecuteCheckpointId = 2; // Delayed cache checkpoint + ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++); + ValidateExecuteWaitCachePackage(pPlan, fRollback, dwIndex++, L"NetFx48Web"); Assert::Equal(dwIndex, pPlan->cExecuteActions); fRollback = TRUE; @@ -3535,6 +3697,26 @@ namespace Bootstrapper return (fRollback ? pPlan->rgRollbackCacheActions : pPlan->rgCacheActions) + dwIndex; } + void ValidateCacheDelayableStart( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_DELAYABLE_START, pAction->type); + } + + void ValidateCacheDelayableEnd( + __in BURN_PLAN* pPlan, + __in BOOL fRollback, + __in DWORD dwIndex + ) + { + BURN_CACHE_ACTION* pAction = ValidateCacheActionExists(pPlan, fRollback, dwIndex); + Assert::Equal(BURN_CACHE_ACTION_TYPE_DELAYABLE_END, pAction->type); + } + void ValidateCacheCheckpoint( __in BURN_PLAN* pPlan, __in BOOL fRollback, diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/CacheReorder_BundleAv1_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/CacheReorder_BundleAv1_manifest.xml new file mode 100644 index 000000000..75351e2ee --- /dev/null +++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/CacheReorder_BundleAv1_manifest.xml @@ -0,0 +1 @@ +