upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/db.cpp
diff options
context:
space:
mode:
authorBitflash <btf01@proton.me>2026-07-31 16:37:10 -0300
committerGitHub <noreply@github.com>2026-07-31 16:37:10 -0300
commit417bfed08ded5c6afaed25f4ef724b5629b13fae (patch)
tree611df88a86d7b73af6b3d65d05647e3f25c682d6 /src/db.cpp
parent59777ee34a5ab6297684b7ab8d5d93facd4ad6f3 (diff)
db: close Berkeley DB cursors instead of leaving them to the handle (#71)
Three functions open a cursor and none of them close it: CTxDB::LoadBlockIndex, CTxDB::ReadOwnerTxes and CWalletDB::LoadWallet. Berkeley DB requires every cursor to be closed before the handle it came from. ~CDB() calls Close(), which calls pdb->close() inside a catch-all that swallows whatever it returns -- so the violation was real and silent, which is this codebase's favourite kind. An open cursor also holds read locks, and that is not theoretical here: the first version of the startup chain repair (#61) opened a write transaction beside the cursor LoadBlockIndex was still holding and waited on it forever, coming up, printing its diagnosis and hanging with nothing in any log. The repair had to be moved to the caller to get clear of it. A scope guard rather than a close call at each return, because all three functions have several exits and the point is to stop relying on anyone remembering. Same fix Bitcoin made in c5c7911da. Verified: node starts, verifies 288 blocks, loads the wallet, runs the spent-flag rescan and syncs the live chain to 4422 with no Berkeley DB error anywhere and db.log still empty.
Diffstat (limited to 'src/db.cpp')
-rw-r--r--src/db.cpp36
1 files changed, 36 insertions, 0 deletions
diff --git a/src/db.cpp b/src/db.cpp
index ce7b875..16f2e91 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -13,6 +13,39 @@
13// CDB 13// CDB
14// 14//
15 15
16// Closes a Berkeley DB cursor however the function exits.
17//
18// Three functions opened one and none of them ever closed it. Berkeley DB
19// requires every cursor to be closed before the handle it came from, and
20// ~CDB() calls Close(), which calls pdb->close() inside a catch-all that
21// swallows whatever it returns. So the violation was real and silent.
22//
23// An open cursor also holds read locks. That is what made the first version of
24// the startup chain repair (#61) hang: it opened a write transaction beside
25// the cursor CTxDB::LoadBlockIndex was still holding, and waited on it forever
26// with nothing in any log. The repair had to be moved to the caller.
27//
28// Each of these functions has several early returns, so a guard is safer than
29// remembering to close on every one of them. Same fix Bitcoin made in
30// c5c7911da, alongside its zombie-socket and double-close work.
31class CAutoCursor
32{
33public:
34 explicit CAutoCursor(Dbc* pcursorIn) : pcursor(pcursorIn) { }
35 ~CAutoCursor()
36 {
37 if (pcursor)
38 {
39 try { pcursor->close(); }
40 catch (...) { } // closing on the way out must not throw
41 }
42 }
43private:
44 Dbc* pcursor;
45 CAutoCursor(const CAutoCursor&);
46 void operator=(const CAutoCursor&);
47};
48
16static CCriticalSection cs_db; 49static CCriticalSection cs_db;
17static bool fDbEnvInit = false; 50static bool fDbEnvInit = false;
18DbEnv dbenv(0u); 51DbEnv dbenv(0u);
@@ -266,6 +299,7 @@ bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector<CTransaction>&
266 Dbc* pcursor = GetCursor(); 299 Dbc* pcursor = GetCursor();
267 if (!pcursor) 300 if (!pcursor)
268 return false; 301 return false;
302 CAutoCursor cursorGuard(pcursor);
269 303
270 unsigned int fFlags = DB_SET_RANGE; 304 unsigned int fFlags = DB_SET_RANGE;
271 loop 305 loop
@@ -375,6 +409,7 @@ bool CTxDB::LoadBlockIndex()
375 Dbc* pcursor = GetCursor(); 409 Dbc* pcursor = GetCursor();
376 if (!pcursor) 410 if (!pcursor)
377 return false; 411 return false;
412 CAutoCursor cursorGuard(pcursor);
378 413
379 unsigned int fFlags = DB_SET_RANGE; 414 unsigned int fFlags = DB_SET_RANGE;
380 loop 415 loop
@@ -544,6 +579,7 @@ bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)
544 Dbc* pcursor = GetCursor(); 579 Dbc* pcursor = GetCursor();
545 if (!pcursor) 580 if (!pcursor)
546 return false; 581 return false;
582 CAutoCursor cursorGuard(pcursor);
547 583
548 loop 584 loop
549 { 585 {