feat(gui): show free disk space on the Downloads and Shared Files panels (#757) - #847
Conversation
The Downloads panel now reports the free space on the filesystem holding the part files beside the queue size, and turns red once that space no longer covers what is left to download. The Shared Files panel reports the free space where finished downloads land. Requested in amule-project#757. Which filesystem, and what to compare against, both follow from how the directories actually work. There is one temp directory for every category -- a category chooses where a file lands when it finishes, not where it downloads -- so the figure is global and the warning is measured against the whole queue rather than the category on screen; a per-category comparison would only fire once it was already too late. The threshold uses the bytes still to download, not the total queue size: the bytes a part file already holds are off the free-space figure already, so what is left is exactly what the disk still has to find room for. Incoming, by contrast, is per category, and the Shared Files panel has no category selector, so it reports the default category's -- and carries no threshold, since nothing there stops when the disk fills. Only the core can answer either question: the GUI may be on another machine entirely, and even where it mounts the same share it can see a different size or quota. Two new stats tags carry the figures over EC. An absent tag means unknown rather than zero, so a daemon older than these tags leaves the field empty instead of reporting a full disk. Both getters are cache-backed, re-sampling at most every ten seconds. statvfs()/GetDiskFreeSpaceEx() blocks on the directory, and on a network mount -- temp and incoming commonly are one -- a slow or stale server blocks it for as long as the mount's timeout. The callers are a per-second GUI refresh and every stats poll from every connected client, so an uncached read would multiply that exposure by the poll rate. A path that cannot be queried at all reports FREE_SPACE_UNKNOWN, which empties the label rather than printing "0 bytes" and never colours it red. The GUI side is gated on what is actually on screen: the timer refreshes only the panel currently displayed, and nothing at all while the window is hidden to the tray or minimized. That matters because the Downloads refresh walks the whole queue to decide whether to warn -- skipped outright when there is no figure to compare against -- and because each label is resolved by name, which is now done once and cached rather than per tick. The Shared Files statistics box grows from four columns to five: the collapse/expand button keeps a narrow column of its own and the size figures get a column beside it, one figure per row, so the total size lines up with the counters and the free space with the gauges. One new translatable string, "Free space: %s", shared by both panels; catalogs regenerated. The separator between the queue size and the free space is built in code so translators are given the figure alone. Builds clean on macOS (amule + amulegui + amuled); clang-format and both clang-tidy tiers clean over the diff.
61e758a to
e24f42f
Compare
CStatistics::GetTempFreeSpace() / GetIncomingFreeSpace() called CPath::GetFreeSpaceAt() inline, cached for ten seconds. Both callers are latency critical: the GUI timer on the main thread, and the EC stats reply on amuled's core thread. statvfs() / GetDiskFreeSpaceEx() blocks on the directory it is asked about. Measured read-only on a live host, 50 samples per path: ext4 0.002 ms median, mergerfs 0.678 ms, nfs4 0.204-0.222 ms, worst case 1.765 ms. So the healthy cost was never the problem, and the interval was not either. What those numbers do not cover is the pathological case: a cold autofs mount takes tens to hundreds of milliseconds, and an unreachable server blocks for timeo x retrans on a soft mount and indefinitely on a hard one. Either directory is commonly a network mount, and that stall landed on the main loop -- freezing the GUI and stalling EC replies. Nothing else in the core reaches those filesystems from the main loop: downloads write through CPartFileWriteThread, uploads read through CUploadDiskIOThread. This probe was the one exception. CFreeSpaceThread is a dedicated joinable worker rather than a task on CThreadScheduler, for the reason CMediaProbeThread was split out (amule-project#280): the scheduler runs one task at a time and owns completion, allocation, hashing, verification and IP filtering, so a probe blocked on a hung mount would not delay that queue but stop it -- and would couple unrelated filesystems, a hung incoming mount blocking the hashing of part files on a healthy local disk. Here a hung mount can only ever delay the next sample: the figure goes stale, the label empties, nothing else notices. The two figures become std::atomic<sint64>, written by the worker and read by the main and EC threads; the getters are plain relaxed loads that cannot block, so no caller changed. The worker wakes every second and samples each path only when its own interval has elapsed -- separately timed, so a slow incoming cannot hold temp back -- and the wake interval is shorter than the sample interval so shutdown does not wait out a sleeping thread. The worker reads no preferences. The temp and incoming paths can change under the preferences dialog, no worker in the tree touches thePrefs, and a wxString read while another thread assigns it is a race whatever the value; so CamuleApp hands the worker mutex-guarded copies, at construction and on each core tick. That also removes any question of teardown ordering against thePrefs. Constructed alongside mediaProbeThread, which is post-fork so the POSIX threads belong to the daemon child (amule-project#849), and torn down beside it in OnExit(). The panel-visibility gate stays, but it now skips pointless work rather than a blocking call: CDownloadListCtrl::UpdateFreeSpace() still walks the queue to decide whether to warn. Refreshing when a panel becomes active as well as on the timer means switching to a panel no longer shows the figure it had when it was last visible until the next tick.
|
Pushed 4224002: the free-space probe now runs on its own thread instead of inline in the getters.
The interval was never the issue. What those numbers don't cover is the case that matters: a cold autofs mount is tens to hundreds of milliseconds, and an unreachable server blocks for
The figures are now One thing not in the brief: the worker reads no preferences. The paths change under the preferences dialog, no worker in the tree touches The panel-visibility gate stays, but now it skips pointless work rather than a blocking call — Rebuilt and retested on macOS: amule, amulegui and amuled, clang-format and both clang-tidy tiers clean. |
Fixes #757.
The Downloads panel now shows the free space on the filesystem holding the part files next to the queue size, red once that space no longer covers what is left to download. The Shared Files panel shows the free space where finished downloads land.
Which filesystem, and what to compare against
Both follow from how the directories actually work, and this is where the design decisions are:
Downloads reports temp, and the warning is measured against the whole queue. There is one temp directory for every category — a category chooses where a file lands when it finishes, not where it downloads — so the figure is inherently global. The displayed queue size stays category-filtered as before, but the red state is driven by every queued file, because they all compete for the same space. Filtering the comparison by category would only warn once it was already too late.
The threshold uses the bytes still to download, not the total queue size. The bytes a part file already holds are off the free-space figure already, so what remains is exactly what the disk still has to find room for. Comparing against the total would warn far earlier than necessary, and more so with sparse part files.
Shared Files reports the default category's incoming, with no threshold. Incoming is per category (
Category_Struct::path) and that panel has no category selector, so there is no single figure covering all of them. Nothing there stops when the disk fills, so a warning colour would have nothing to mean.Getting the figures to the GUI
Only the core can answer either question: the GUI may be on another machine entirely, and even where it mounts the same share it can see a different size or quota. Two new stats tags carry the figures over EC.
An absent tag means unknown, not zero — a daemon older than these tags leaves the field empty rather than reporting a full disk and painting it red.
Both getters are cache-backed and re-sample at most every ten seconds.
statvfs()/GetDiskFreeSpaceEx()blocks on the directory, and on a network mount — temp and incoming commonly are one — a slow or stale server blocks it for as long as the mount's timeout. The callers are a per-second GUI refresh plus every stats poll from every connected client, so an uncached read would multiply that exposure by the poll rate. This doesn't introduce a new failure mode (aMule already reads and writes every part file on that mount) — it just avoids re-entering a blocking call at the poll rate.A path that cannot be queried reports
FREE_SPACE_UNKNOWN, which empties the label instead of printing "0 bytes", and never colours it red.Layout
The Shared Files statistics box goes from four columns to five: the collapse/expand button keeps a narrow column of its own, and the size figures get a column beside it with one figure per row — so the total size lines up with the counters and the free space with the gauges.
Strings
One new translatable string,
"Free space: %s", shared by both panels; catalogs regenerated. Nothing was removed or changed — no existing translation was touched.msgmergepre-filled 32 catalogs with a#, fuzzyguess matched from"Server message: %s"; fuzzy entries are excluded bymsgfmt, so those languages show the English until a translator confirms it. The separator between the queue size and the free space is built in code, so translators are given the figure alone.Verification
Builds clean on macOS (amule + amulegui + amuled), no warnings from project sources.
clang-formatand both clang-tidy tiers clean over the diff. Catalogs verified idempotent against a second regen.Tested interactively on macOS, monolithic and amulegui against a local daemon: both figures, the red threshold, and the panel layouts. Linux and Windows have not had a visual pass yet — the layout change is in shared code, so the Shared Files panel is worth a look on both.