Skip to content

Commit f6a7877

Browse files
committed
Scraper enhanced thread safety
This implements qualifiers required for advanced static thread safety analysis by Clang and other advanced compilers. Note that a few minor thread safety issues were caught and fixed preparing this commit. Also, thread safety was implemented around the scraper globals and the AppCache reads. Even though these are seldom changed and there is little chance of an issue, they should be completely thread-safe as they are accessed by multiple threads. Note that some lambdas were used to do quick locked reads against the scraper globals in some of the scraper functions to facilitate use of the global in existing if statements without further change. This is a little ugly and some of the lambdas are repeated. This will be cleaned up when the scraper is restructured into classes later.
1 parent 3eac822 commit f6a7877

File tree

10 files changed

+881
-542
lines changed

10 files changed

+881
-542
lines changed

src/gridcoin/gridcoin.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
using namespace GRC;
2222

23+
extern CCriticalSection cs_ScraperGlobals;
2324
extern bool fExplorer;
2425
extern unsigned int nScraperSleep;
2526
extern unsigned int nActiveBeforeSB;
@@ -276,10 +277,14 @@ void ThreadScraperSubscriber(void* parg)
276277
//!
277278
void InitializeScraper(ThreadHandlerPtr threads)
278279
{
279-
// Default to 300 sec (5 min), clamp to 60 minimum, 600 maximum - converted to milliseconds.
280-
nScraperSleep = std::clamp<int64_t>(gArgs.GetArg("-scrapersleep", 300), 60, 600) * 1000;
281-
// Default to 14400 sec (4 hrs), clamp to 300 minimum, 86400 maximum (meaning active all of the time).
282-
nActiveBeforeSB = std::clamp<int64_t>(gArgs.GetArg("-activebeforesb", 14400), 300, 86400);
280+
{
281+
LOCK(cs_ScraperGlobals);
282+
283+
// Default to 300 sec (5 min), clamp to 60 minimum, 600 maximum - converted to milliseconds.
284+
nScraperSleep = std::clamp<int64_t>(gArgs.GetArg("-scrapersleep", 300), 60, 600) * 1000;
285+
// Default to 14400 sec (4 hrs), clamp to 300 minimum, 86400 maximum (meaning active all of the time).
286+
nActiveBeforeSB = std::clamp<int64_t>(gArgs.GetArg("-activebeforesb", 14400), 300, 86400);
287+
}
283288

284289
// Run the scraper or subscriber housekeeping thread, but not both. The
285290
// subscriber housekeeping thread checks if the flag for the scraper thread
@@ -311,6 +316,8 @@ void InitializeScraper(ThreadHandlerPtr threads)
311316
//!
312317
void InitializeExplorerFeatures()
313318
{
319+
LOCK(cs_ScraperGlobals);
320+
314321
fExplorer = gArgs.GetBoolArg("-scraper", false) && gArgs.GetBoolArg("-explorer", false);
315322
}
316323

src/gridcoin/quorum.cpp

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Superblock ScraperGetSuperblockContract(
2929
bool bContractDirectFromStatsUpdate = false,
3030
bool bFromHousekeeping = false);
3131

32+
extern CCriticalSection cs_ScraperGlobals;
3233
extern CCriticalSection cs_ConvergedScraperStatsCache;
3334
extern ConvergedScraperStats ConvergedScraperStatsCache;
3435

@@ -609,8 +610,12 @@ class SuperblockValidator
609610
return Result::UNKNOWN;
610611
}
611612

612-
if (m_superblock.Age(GetAdjustedTime()) > SCRAPER_CMANIFEST_RETENTION_TIME) {
613-
return Result::HISTORICAL;
613+
{
614+
LOCK(cs_ScraperGlobals);
615+
616+
if (m_superblock.Age(GetAdjustedTime()) > SCRAPER_CMANIFEST_RETENTION_TIME) {
617+
return Result::HISTORICAL;
618+
}
614619
}
615620

616621
if (use_cache) {
@@ -1037,12 +1042,19 @@ class SuperblockValidator
10371042
// If the manifest for the beacon list is now empty, we cannot
10381043
// proceed, but ProjectResolver should always select manifests
10391044
// with a beacon list part:
1040-
if (manifest->vParts.empty()) {
1041-
LogPrintf("ValidateSuperblock(): beacon list part missing.");
1042-
return;
1043-
}
10441045

1045-
convergence.AddPart("BeaconList", manifest->vParts[0]);
1046+
// Note using fine-grained locking here to avoid lock-order issues and
1047+
// also to deal with Clang potential false positive below.
1048+
{
1049+
LOCK(manifest->cs_manifest);
1050+
1051+
if (manifest->vParts.empty()) {
1052+
LogPrintf("ValidateSuperblock(): beacon list part missing.");
1053+
return;
1054+
}
1055+
1056+
convergence.AddPart("BeaconList", manifest->vParts[0]);
1057+
}
10461058

10471059
// Find the offset of the verified beacons project part. Typically
10481060
// this exists at vParts offset 1 when a scraper verified at least
@@ -1051,10 +1063,14 @@ class SuperblockValidator
10511063
const auto verified_beacons_entry_iter = std::find_if(
10521064
manifest->projects.begin(),
10531065
manifest->projects.end(),
1054-
[](const CScraperManifest::dentry& entry) {
1066+
[manifest](const CScraperManifest::dentry& entry) {
1067+
// Clang was giving a false positive on thread safety without this.
1068+
LOCK(manifest->cs_manifest);
10551069
return entry.project == "VerifiedBeacons";
10561070
});
10571071

1072+
LOCK2(CSplitBlob::cs_mapParts, manifest->cs_manifest);
1073+
10581074
if (verified_beacons_entry_iter == manifest->projects.end()) {
10591075
LogPrintf("ValidateSuperblock(): verified beacon project missing.");
10601076
return;
@@ -1229,6 +1245,8 @@ class SuperblockValidator
12291245

12301246
const CScraperManifest_shared_ptr manifest = iter->second;
12311247

1248+
LOCK(manifest->cs_manifest);
1249+
12321250
for (const auto& entry : manifest->projects) {
12331251
auto project_option = TallyProject(entry.project, scraper_id);
12341252

src/gridcoin/researcher.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
using namespace GRC;
3030

3131
extern CCriticalSection cs_main;
32+
extern CCriticalSection cs_ScraperGlobals;
3233
extern std::string msMiningErrors;
3334
extern unsigned int nActiveBeforeSB;
3435

@@ -1086,6 +1087,8 @@ void Researcher::RunRenewBeaconJob()
10861087
// window begins nActiveBeforeSB seconds before the next superblock.
10871088
// This is four hours by default unless overridden by protocol entry.
10881089
//
1090+
LOCK(cs_ScraperGlobals);
1091+
10891092
if (!Quorum::SuperblockNeeded(pindexBest->nTime + nActiveBeforeSB)) {
10901093
TRY_LOCK(pwalletMain->cs_wallet, locked_wallet);
10911094

src/gridcoin/scraper/fwd.h

Lines changed: 5 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,6 @@ enum class scraperSBvalidationtype
4646
ProjectLevelConvergence
4747
};
4848

49-
50-
/*********************
51-
* Global Vars *
52-
*********************/
53-
54-
5549
typedef std::string ScraperID;
5650
// The inner map is sorted in descending order of time. The pair is manifest hash, content hash.
5751
typedef std::multimap<int64_t, std::pair<uint256, uint256>, std::greater <int64_t>> mCSManifest;
@@ -74,66 +68,13 @@ typedef std::map<std::string, CSplitBlob::CPart*> mConvergedManifestPart_ptrs;
7468
struct ConvergedManifest
7569
{
7670
// Empty converged manifest constructor
77-
ConvergedManifest()
78-
{
79-
nContentHash = {};
80-
ConsensusBlock = {};
81-
timestamp = 0;
82-
bByParts = false;
83-
84-
CScraperConvergedManifest_ptr = nullptr;
85-
86-
ConvergedManifestPartPtrsMap = {};
87-
88-
mIncludedScraperManifests = {};
89-
90-
nUnderlyingManifestContentHash = {};
91-
92-
vIncludedScrapers = {};
93-
vExcludedScrapers = {};
94-
vScrapersNotPublishing = {};
95-
96-
mIncludedScrapersbyProject = {};
97-
mIncludedProjectsbyScraper = {};
98-
99-
mScraperConvergenceCountbyProject = {};
100-
101-
vExcludedProjects = {};
102-
}
71+
ConvergedManifest();
10372

10473
// For constructing a dummy converged manifest from a single manifest
105-
ConvergedManifest(CScraperManifest_shared_ptr& in)
106-
{
107-
ConsensusBlock = in->ConsensusBlock;
108-
timestamp = GetAdjustedTime();
109-
bByParts = false;
110-
111-
CScraperConvergedManifest_ptr = in;
112-
113-
PopulateConvergedManifestPartPtrsMap();
114-
115-
ComputeConvergedContentHash();
116-
117-
nUnderlyingManifestContentHash = in->nContentHash;
118-
}
74+
ConvergedManifest(CScraperManifest_shared_ptr& in);
11975

12076
// Call operator to update an already initialized ConvergedManifest with a passed in CScraperManifest
121-
bool operator()(const CScraperManifest_shared_ptr& in)
122-
{
123-
ConsensusBlock = in->ConsensusBlock;
124-
timestamp = GetAdjustedTime();
125-
bByParts = false;
126-
127-
CScraperConvergedManifest_ptr = in;
128-
129-
bool bConvergedContentHashMatches = PopulateConvergedManifestPartPtrsMap();
130-
131-
ComputeConvergedContentHash();
132-
133-
nUnderlyingManifestContentHash = in->nContentHash;
134-
135-
return bConvergedContentHashMatches;
136-
}
77+
bool operator()(const CScraperManifest_shared_ptr& in);
13778

13879
// IMPORTANT... nContentHash is NOT the hash of part hashes in the order of vParts unlike CScraper::manifest.
13980
// It is the hash of the data in the ConvergedManifestPartsMap in the order of the key. It represents
@@ -173,61 +114,9 @@ struct ConvergedManifest
173114
// --------- project
174115
std::vector<std::string> vExcludedProjects;
175116

176-
bool PopulateConvergedManifestPartPtrsMap()
177-
{
178-
if (CScraperConvergedManifest_ptr == nullptr) return false;
179-
180-
int iPartNum = 0;
181-
CDataStream ss(SER_NETWORK,1);
182-
WriteCompactSize(ss, CScraperConvergedManifest_ptr->vParts.size());
183-
uint256 nContentHashCheck;
184-
185-
for (const auto& iter : CScraperConvergedManifest_ptr->vParts)
186-
{
187-
std::string sProject;
188-
189-
if (iPartNum == 0)
190-
sProject = "BeaconList";
191-
else
192-
sProject = CScraperConvergedManifest_ptr->projects[iPartNum-1].project;
193-
194-
// Copy the pointer to the CPart into the map. This is ok, because the parts will be held
195-
// until the CScraperManifest in this object is destroyed and all of the manifest refs to the part
196-
// are gone.
197-
ConvergedManifestPartPtrsMap.insert(std::make_pair(sProject, iter));
117+
bool PopulateConvergedManifestPartPtrsMap();
198118

199-
// Serialize the hash to doublecheck the content hash.
200-
ss << iter->hash;
201-
202-
iPartNum++;
203-
}
204-
205-
ss << CScraperConvergedManifest_ptr->ConsensusBlock;
206-
207-
nContentHashCheck = Hash(ss.begin(), ss.end());
208-
209-
if (nContentHashCheck != CScraperConvergedManifest_ptr->nContentHash)
210-
{
211-
LogPrintf("ERROR: PopulateConvergedManifestPartPtrsMap(): Selected Manifest content hash check failed! "
212-
"nContentHashCheck = %s and nContentHash = %s.",
213-
nContentHashCheck.GetHex(), CScraperConvergedManifest_ptr->nContentHash.GetHex());
214-
return false;
215-
}
216-
217-
return true;
218-
}
219-
220-
void ComputeConvergedContentHash()
221-
{
222-
CDataStream ss(SER_NETWORK,1);
223-
224-
for (const auto& iter : ConvergedManifestPartPtrsMap)
225-
{
226-
ss << iter.second->data;
227-
}
228-
229-
nContentHash = Hash(ss.begin(), ss.end());
230-
}
119+
void ComputeConvergedContentHash();
231120
};
232121

233122

0 commit comments

Comments
 (0)