AsyncDirectoryCleaner
Deletes large staging trees off the critical path.
A mix cycle ends by recursively deleting tens of thousands of expanded pack files. None of that work is needed before the pack can be published — it is pure disk reclamation — but it used to run inline, so /rspm reload did not return until the last file was unlinked.
Rename first, then delete
Handing a live directory to a background thread would race the next mix cycle, which wipes and recreates the same paths. So the directory is first renamed to a uniquely-named .rspm_trash_<nanos> sibling. That is a single metadata operation, and it frees the original path immediately — the next cycle can recreate it while the old contents are still being unlinked, with no possibility of the cleaner deleting files the new cycle just wrote.
If the rename fails — Windows keeps a directory locked while any handle inside it is open — the caller falls back to deleting synchronously, exactly as before. Correctness never depends on the async path succeeding.
Trash directories left behind by a crash or a shutdown mid-delete are swept by sweepTrash at the start of the next mix.