turbo-tasks-backend: improve print_cache_item_size instrumentation (#91742)
### What?
Fixes a compilation hang that occurred when the `print_cache_item_size`
debug feature was enabled,
and cleans up the surrounding instrumentation code.
**Root cause of the hang (double lock / deadlock):**
During `persist_snapshot`, the storage iterates over all modified tasks.
For each task it calls:
```rust
let inner = self.shard.storage.map.get(&task_id).unwrap();
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// acquires a READ lock on the DashMap shard for task_id
let item = (self.shard.process)(task_id, &inner, &mut self.buffer);
```
The `process` closure receives `&TaskStorage` while that **read lock is
still held**. Inside the
closure, the old code called:
```rust
self.get_task_name(task_id, turbo_tasks)
```
`get_task_name` creates an `ExecuteContext`, then calls
`ctx.task(task_id, TaskDataCategory::Data)`,
which calls `storage.access_mut(task_id)`, which calls
`self.map.entry(key)`. That `entry()` call
tries to acquire a **write lock on the same DashMap shard** — which is
already read-locked on the
same thread. `parking_lot::RwLock` is not reentrant, so the thread
blocks forever waiting for its
own read lock to be released.
**Fix:** the `&TaskStorage` reference (`inner`) is already in scope
inside the closure. Call
`inner.get_persistent_task_type()` directly instead of going through
`get_task_name`, which avoids
any additional lock acquisition.
**Additional changes in this PR:**
1. **Make compressed-size reporting opt-in** — split
`print_cache_item_size` into two Cargo features.
The base feature now shows uncompressed sizes only (no lz4 overhead). A
new
`print_cache_item_size_with_compressed` feature re-enables
compressed-size reporting for when
you need it.
2. **Extract helpers to eliminate duplication** — the output block
repeated `#[cfg]`-gated
`FormatSizes { … }` struct literals six times and duplicated the
`task_name` computation twice.
Extract:
- `FormatSizes` struct + `impl Display` (with `#[cfg]` isolated inside)
- `TaskCacheStats::task_name(storage)` — single source for the grouping
key
- `TaskCacheStats::sort_key()` — encapsulates the `#[cfg]`-switched
primary sort field
- `TaskCacheStats::format_total/data/avg_data/meta/avg_meta()` — each
returning `FormatSizes`
### Why?
The compilation was hanging and unusable. The lock ordering bug was
non-obvious because the re-entrant
acquisition happened across an abstraction boundary (`get_task_name`
hiding the `access_mut` call).
### How?
Avoid the redundant lock by using the `&TaskStorage` already provided to
the closure.
Pure refactor for everything else — no behaviour change for any given
feature combination.
---------
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>