upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/fuzz_script.cpp
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-08-23 11:31:46 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-08-23 11:31:46 +0000
commitd273a04ff89233f0f01431bd6e3c18d5261818b0 (patch)
tree934bf3a125082673a2e308d0a124cae1dccc34b5 /src/fuzz_script.cpp
parent6c0ed67a2f6b39b630c5748ecf214f86e9d26a5c (diff)
payout: build pool_status.json under the locks, write it after releasing them
RefreshStats runs on the event loop -- the one thread that reads and writes every miner's socket -- and took gStatsMutex and then gPayoutLedgerMutex at function scope, then called WritePoolStatusJsonLocked() with both still held. Since 86b39b9 that write ends in fsync() on the file and fsync() on its directory. So every ten seconds the event loop performed two synchronous disk flushes holding the mutex the payout thread must take to record that a miner has been paid, and the payout thread waited behind them. Measured here on ext4/LVM/SSD: 3.78 ms mean per publish. On the pool's shared virtual disk it is higher and it has no upper bound under load. That is worse than the code the merge replaced, which wrote the same file under the same locks but buffered and without fsync. It also made 33f2e8a's headline claim -- "no thread waits on I/O holding gPayoutLedgerMutex, gPayoutExecMutex or gPoolRoundsMutex" -- false on the commit that asserted it. Split the same way as the three ledgers: SerializePoolStatusLocked() returns the image, PublishPoolStatus() writes it, and RefreshStats calls the second one after leaving the scope that holds the first. The ledger lock is also scoped to the loop that actually needs it, which is the gPayoutViews rebuild; the serialiser never needed it, since those rows were already copied. No version guard is added and none is needed: RefreshStats is still the only caller of either half, it still runs only on the event loop, and it now calls them in sequence on that one thread, so pool_status.json still has a single writer by construction. The comment saying so has been rechecked against the new shape rather than carried over. The claim is now tested rather than asserted. The self-test points strPoolStatusFile at a path whose ".tmp" is a FIFO, so the publish parks in open(2) until the test chooses to read, and every mutex the write must not be holding is required to be acquirable while it is parked. Against the previous code that test cannot pass: the writer sits inside fopen() holding gStatsMutex and gPayoutLedgerMutex, and nothing releases them until the write is allowed to finish. Verified by reintroducing the defect -- two assertions fail, deterministically. Property: pool_status.json is serialised under the locks and written with all four released, so no payout mutex is ever held across I/O.
Diffstat (limited to 'src/fuzz_script.cpp')
0 files changed, 0 insertions, 0 deletions