Skip to content

asset.dump_folder: bound the synchronous build/write phase per asset #369

Description

@SSS135

Remainder of #244 (asset.dump_folder per-asset time bound) after its load-phase slice landed.

What #244 shipped: a package that is not in memory is loaded with LoadPackageAsync and the sweep pumps the loader at most 50 ms per tick (AsyncLoadPumpSliceSeconds), publishing currentPhase: "loading"; a load past 120 s is skipped with ASSET_LOAD_TIMEOUT; slowAssets[] lists every asset whose synchronous dump call held the editor 5 s or more, with its slowest phase (loading / building / writing); asset.dump refuses packages in dump-stalled.txt and journals its own call.

Still unbounded, in order:

  1. Build/write phase. After the load, DumpSingleAsset runs BuildAllFilesForAsset (every aspect builder) and AssetDumpWriter::WriteAssetDump as one game-thread call (Handlers/Asset/AssetDumpHandler.cpp, DumpSingleAsset). Split it into per-aspect steps the ticker can interleave across ticks (holding the asset with a strong pointer, re-checking cancellation, publishing the aspect as currentPhase), or give non-yielding aspects a bounded failure path. slowAssets[].phase == "building" is the signal for which assets need it.
  2. Abandoned loads stop release steps. A timed-out load keeps running; TickFolderDumpRelease skips the release step (its FlushAsyncLoading would block on that load) until it completes. A load that never completes therefore stops memory release for the rest of the sweep. Releasing around in-flight loads needs to know which packages they import.
  3. Registry preflight. BuildFolderDumpCandidates calls IAssetRegistry::GetAssets once, synchronously. Page it per sub-path if large trees show it as a long tick.
  4. Pathological fixture. A fixture with a deliberately slow build phase, asserting per-tick latency, progress, system.job_status and cancellation stay responsive. PinWright.asset.dump.AsyncFolderDump.UnloadedPackageLoadsAcrossTicks covers only the load phase.
  5. Stalled list has no verb. A user deletes the dump-stalled.txt line by hand to retry.

Activity

  1. added theissue type on Oct 6, 2026
  2. updated Confidence 0.8 Likely, Effort 3 Large, Reach 1 Rare, RICE priority 4, Impact 2 Wasted time and Severity Low on Oct 6, 2026
  3. SSS135 commented on Oct 6, 2026

    @SSS135
    MemberAuthor

    Review follow-ups from #244 that land here (not fixed in #244):

    • Timeout cascade. A pump advances every queued load, so asset B's slices also spend time on a timed-out load A that is still running. B can then reach its own 120 s of pumping and time out too. Only time spent pumping counts, so a throttled editor no longer times out early. Fix direction: count only progress attributable to the active request, or cap the time spent on abandoned loads per pump.
    • Next sweep finishes an abandoned load synchronously. A sweep started while an earlier sweep's timed-out load is still running finds the package resident (bResident in TickFolderDump). Its DumpSingleAsset LoadObject then finishes the rest of that load in one journaled but unbounded call, and the package is not added to SweepLoadedPackages, so it is never released. Fix direction: treat a package with an in-flight load (GetAsyncLoadPercentage >= 0, or the loader's own state) as not resident and wait on it through the pump.
    • Other flushes finish an abandoned load. Any later FlushAsyncLoading (editor map open, PIE start, other PinWright verbs) finishes it synchronously. This is a stall, not a hang.
    • Unjournaled loading. The editor's own per-frame loading slice (StaticTick, EditorEngine.cpp:1788, 5 ms per frame) advances abandoned loads with no .dump-inflight.tmp marker, so a freeze there journals nothing. The pump journal now lists the active path plus every still-running abandoned path, so a freeze inside a pump quarantines all of them, including possibly innocent ones.
    • Engine-version verification. ProcessAsyncLoadingUntilComplete(TFunctionRef<bool()>, double), LoadPackageAsync(const FString&, FLoadPackageAsyncDelegate, ...), FLoadPackageAsyncDelegate and IsAsyncLoadingSuspended are verified against 5.8 source only. Older engines may take float TimeLimit; passing the constexpr double may raise an MSVC C4305/C4244 warning. asset.dump_folder needs a real per-asset time bound #244 should not be closed until a version-matrix run has covered 5.3-5.7.
    • Final release on headless runs (asset.dump_folder: Final release step never runs, sweep leaves packages resident #370 fix). In automation, the Final release step's collect is aborted (bAbortReleaseGcWait, restoring the cleared RF_Standalone flags), so tests prove the step runs (releaseStepCount, releasedPackageCount) but not that the package is freed. A live-editor check that a swept package is unloaded after the job completes is still needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    status/acceptedReviewed and approved for agents to work

    Fields

    Effort

    3 Large

    RICE priority

    4

    Reach

    1 Rare

    Impact

    2 Wasted time

    Confidence

    0.8 Likely

    Severity

    Low

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions