This update introduces a new function, GetThreadLockWaitTime, which computes the total wait time for locks held by a specific thread. The function aggregates wait times across all locks and updates the tooltip to display lock wait time and count, enhancing the profiling capabilities of the timeline item.
The timeout option stops the connection if it has now finished after the
specified time, even if data transfer is underway. Switch to using a low
speed timeout instead, which stops the connection only if no data is being
transferred.
A typical case where this was hit was a slow-responding LLM generating
a large amount of text.
The high timeout value is still needed to account for long prefill times.
Two staleness gaps a cold agent would hit: get_sections()'s
documented {start, end, text} shape didn't match the actual
return value once category was added, and is_background_done()
-- introduced to close the stats-read race exercised while
testing the lock fix -- wasn't mentioned anywhere, so a cold
agent had no way to discover it short of dir(ctx).
Smoke-testing the lock fix against a real 143M-zone capture
surfaced a second concurrent-mutation source: file loads spawn a
background thread that finishes populating zone/symbol statistics
after Worker construction returns, so get_all_zone_stats() et al.
could silently come back empty or partial with no way to tell
"not built yet" from "genuinely empty".
Adds is_background_done (a relaxed atomic, same as is_connected,
so no locked() wrapper needed) and surfaces it as background_done
in list_instances, with load_capture's docstring pointing callers
at it.
Every Worker-reading Python binding except save_worker() read
m_data's zones/plots/threads/etc. without taking
Worker::ObtainLockForMainThread(), racing the live receive thread
that mutates the same structures. This matches
alandtse/tracy#2: crashes only ever hit live instances, never
file-loaded ones (no concurrent writer there), and cluster right
after a big save_trace when the receive thread is catching up on
backlog.
Routes every Worker accessor through a new locked() wrapper using
the same cooperative lock TracyView.cpp already takes once per
frame, so reads can't race live writes without introducing new
stalls on the profiled client.
D3D11_QUERY_DATA_TIMESTAMP_DISJOINT can report Frequency == 0 even
when Disjoint == FALSE. Neither the initial CPU/GPU calibration
loop nor the per-frame Collect() path guarded against this, so
`timestamp * (1000000000 / disjoint.Frequency)` divides by zero
and crashes the profiled application with
EXCEPTION_INT_DIVIDE_BY_ZERO inside client instrumentation code.
Treats a zero frequency the same as an existing guard already does
for a disjoint result: skip/retry in calibration, and in Collect()
advance the checkpoint and drop the batch via TracyD3D11Panic,
matching the existing disjoint-timestamp handling immediately
above it.