diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-23 11:56:31 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-23 11:56:31 +0000 |
| commit | 991853fa55a749d28d6f9e507ce72b289df92785 (patch) | |
| tree | 97fa0b3fe2d4140392653a05398ae620eac1b881 | |
| parent | 111fb886c2a38c8ab76fd064c9dbb1f403fde7c1 (diff) | |
payout: qualify the single-writer claim for pool_status.json
The comment justifying the absence of a version guard said RefreshStats
"runs only on the event loop". That was true when it was written and is
no longer: the payout-ledger self-test drives RefreshStats directly, to
check that no payout mutex is held across the write.
The conclusion still holds -- the test calls it from one thread at a
time with the pool not running, so pool_status.json still has a single
writer -- but the reason a reader would check is now stated, along with
what would invalidate it. A claim in a comment that has quietly stopped
being true is how 33f2e8a came to assert that no thread waits on I/O
holding gPayoutLedgerMutex on the commit that made it do so.
Property: comment only.
| -rw-r--r-- | src/rpc.cpp | 17 |
1 files changed, 12 insertions, 5 deletions
diff --git a/src/rpc.cpp b/src/rpc.cpp index 79947df..7a6b6a4 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp | |||
| @@ -2429,11 +2429,18 @@ static std::string SerializePoolStatusLocked() | |||
| 2429 | // Publish the operator status file. Call with every payout mutex released. | 2429 | // Publish the operator status file. Call with every payout mutex released. |
| 2430 | // | 2430 | // |
| 2431 | // Still no version guard and no write mutex, deliberately, and the split does | 2431 | // Still no version guard and no write mutex, deliberately, and the split does |
| 2432 | // not change the argument: RefreshStats remains the only caller, it still runs | 2432 | // not change the argument: RefreshStats remains the only caller of either |
| 2433 | // only on the event loop, and it now calls this after leaving the scope that | 2433 | // half, in production it runs only on the event loop, and it now calls this |
| 2434 | // serialised -- so the serialise and the write stay strictly ordered on one | 2434 | // after leaving the scope that serialised -- so the serialise and the write |
| 2435 | // thread and pool_status.json still has a single writer by construction. It | 2435 | // stay strictly ordered on one thread and pool_status.json still has a single |
| 2436 | // shares WriteFileAtomic so the durability rules stay in one place. | 2436 | // writer by construction. It shares WriteFileAtomic so the durability rules |
| 2437 | // stay in one place. | ||
| 2438 | // | ||
| 2439 | // "In production" is doing real work in that sentence. The payout-ledger | ||
| 2440 | // self-test also calls RefreshStats, from one thread at a time and with the | ||
| 2441 | // pool not running, which is why it is still a single writer there too. A | ||
| 2442 | // second concurrent caller anywhere would invalidate this paragraph and want | ||
| 2443 | // the same versioned publish the ledgers use. | ||
| 2437 | static void PublishPoolStatus(const std::string& blob) | 2444 | static void PublishPoolStatus(const std::string& blob) |
| 2438 | { | 2445 | { |
| 2439 | WriteFileAtomic(PoolStatusFilePath(), blob, "pool status"); | 2446 | WriteFileAtomic(PoolStatusFilePath(), blob, "pool status"); |