Skip to content

Commit 3873f1d

Browse files
committed
WIP 3 - Scraper thread safety
1 parent 93de2a9 commit 3873f1d

File tree

4 files changed

+148
-124
lines changed

4 files changed

+148
-124
lines changed

src/gridcoin/quorum.cpp

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,17 +1039,22 @@ class SuperblockValidator
10391039

10401040
const CScraperManifest_shared_ptr manifest = iter->second;
10411041

1042-
LOCK(manifest->cs_manifest);
1043-
10441042
// If the manifest for the beacon list is now empty, we cannot
10451043
// proceed, but ProjectResolver should always select manifests
10461044
// with a beacon list part:
1047-
if (manifest->vParts.empty()) {
1048-
LogPrintf("ValidateSuperblock(): beacon list part missing.");
1049-
return;
1050-
}
10511045

1052-
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+
}
10531058

10541059
// Find the offset of the verified beacons project part. Typically
10551060
// this exists at vParts offset 1 when a scraper verified at least
@@ -1058,10 +1063,14 @@ class SuperblockValidator
10581063
const auto verified_beacons_entry_iter = std::find_if(
10591064
manifest->projects.begin(),
10601065
manifest->projects.end(),
1061-
[](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);
10621069
return entry.project == "VerifiedBeacons";
10631070
});
10641071

1072+
LOCK2(CSplitBlob::cs_mapParts, manifest->cs_manifest);
1073+
10651074
if (verified_beacons_entry_iter == manifest->projects.end()) {
10661075
LogPrintf("ValidateSuperblock(): verified beacon project missing.");
10671076
return;
@@ -1074,8 +1083,6 @@ class SuperblockValidator
10741083
return;
10751084
}
10761085

1077-
LOCK(CSplitBlob::cs_mapParts);
1078-
10791086
convergence.AddPart("VerifiedBeacons", manifest->vParts[part_offset]);
10801087
}
10811088
}; // ProjectCombiner

src/gridcoin/scraper/scraper.cpp

Lines changed: 116 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -3819,6 +3819,9 @@ bool ScraperSaveCScraperManifestToFiles(uint256 nManifestHash)
38193819
// This is from the map find above.
38203820
const CScraperManifest_shared_ptr manifest = pair->second;
38213821

3822+
LOCK(manifest->cs_manifest);
3823+
_log(logattribute::INFO, "LOCK", "cs_manifest");
3824+
38223825
// Write out to files the parts. Note this assumes one-to-one part to file. Needs to
38233826
// be fixed for more than one part per file.
38243827
int iPartNum = 0;
@@ -3859,6 +3862,7 @@ bool ScraperSaveCScraperManifestToFiles(uint256 nManifestHash)
38593862
iPartNum++;
38603863
}
38613864

3865+
_log(logattribute::INFO, "ENDLOCK", "cs_manifest");
38623866
_log(logattribute::INFO, "ENDLOCK", "CScraperManifest::cs_mapManifest");
38633867

38643868
return true;
@@ -4178,6 +4182,9 @@ unsigned int ScraperDeleteUnauthorizedCScraperManifests()
41784182
{
41794183
CScraperManifest_shared_ptr manifest = iter->second;
41804184

4185+
LOCK(manifest->cs_manifest);
4186+
_log(logattribute::INFO, "LOCK", "cs_manifest");
4187+
41814188
// We are not going to do anything with the banscore here, but it is an out parameter of IsManifestAuthorized.
41824189
unsigned int banscore_out = 0;
41834190

@@ -4194,6 +4201,8 @@ unsigned int ScraperDeleteUnauthorizedCScraperManifests()
41944201
iter = CScraperManifest::DeleteManifest(iter, true);
41954202
nDeleted++;
41964203
}
4204+
4205+
_log(logattribute::INFO, "ENDLOCK", "cs_manifest");
41974206
}
41984207

41994208
// End LOCK(CScraperManifest::cs_mapManifest)
@@ -4211,25 +4220,30 @@ bool ScraperSendFileManifestContents(CBitcoinAddress& Address, CKey& Key) EXCLUS
42114220

42124221
auto manifest = std::shared_ptr<CScraperManifest>(new CScraperManifest());
42134222

4214-
// The manifest name is the authorized address of the scraper.
4215-
manifest->sCManifestName = Address.ToString();
4216-
4217-
// Also store local sCManifestName, because the manifest will be std::moved by addManifest.
4218-
std::string sCManifestName = Address.ToString();
4219-
4220-
manifest->nTime = StructScraperFileManifest.timestamp;
4221-
4222-
// Also store local nTime, because the manifest will be std::moved by addManifest.
4223-
int64_t nTime = StructScraperFileManifest.timestamp;
4224-
4225-
manifest->ConsensusBlock = StructScraperFileManifest.nConsensusBlockHash;
4223+
std::string sCManifestName;
4224+
int64_t nTime = 0;
42264225

42274226
// This will have to be changed to support files bigger than 32 MB, where more than one
42284227
// part per object will be required.
42294228
int iPartNum = 0;
42304229

4231-
// Inject the BeaconList part.
42324230
{
4231+
LOCK2(CSplitBlob::cs_mapParts, manifest->cs_manifest);
4232+
_log(logattribute::INFO, "LOCK2", "cs_mapParts, cs_manifest");
4233+
4234+
// The manifest name is the authorized address of the scraper.
4235+
manifest->sCManifestName = Address.ToString();
4236+
4237+
// Also store local sCManifestName, because the manifest will be std::moved by addManifest.
4238+
sCManifestName = Address.ToString();
4239+
4240+
manifest->nTime = StructScraperFileManifest.timestamp;
4241+
4242+
// Also store local nTime, because the manifest will be std::moved by addManifest.
4243+
nTime = StructScraperFileManifest.timestamp;
4244+
4245+
manifest->ConsensusBlock = StructScraperFileManifest.nConsensusBlockHash;
4246+
42334247
// Read in BeaconList
42344248
fs::path inputfile = "BeaconList.csv.gz";
42354249
fs::path inputfilewpath = pathScraper / inputfile;
@@ -4268,136 +4282,132 @@ bool ScraperSendFileManifestContents(CBitcoinAddress& Address, CKey& Key) EXCLUS
42684282

42694283
CDataStream part(std::move(vchData), SER_NETWORK, 1);
42704284

4271-
LOCK(CSplitBlob::cs_mapParts);
4272-
_log(logattribute::INFO, "LOCK", "cs_mapParts");
4273-
42744285
manifest->addPartData(std::move(part));
42754286

42764287
iPartNum++;
42774288

4278-
_log(logattribute::INFO, "ENDLOCK", "cs_mapParts");
4279-
}
4289+
// Inject the VerifiedBeaconList as a "project" called VerifiedBeacons. This is inelegant, but
4290+
// will maintain compatibility with older nodes. The older nodes will simply ignore this extra part
4291+
// because it will never match any whitelisted project. Only include it if it is not empty.
4292+
{
4293+
LOCK(cs_VerifiedBeacons);
4294+
_log(logattribute::INFO, "LOCK", "cs_VerifiedBeacons");
42804295

4281-
// Inject the VerifiedBeaconList as a "project" called VerifiedBeacons. This is inelegant, but
4282-
// will maintain compatibility with older nodes. The older nodes will simply ignore this extra part
4283-
// because it will never match any whitelisted project. Only include it if it is not empty.
4284-
{
4285-
LOCK(cs_VerifiedBeacons);
4286-
_log(logattribute::INFO, "LOCK", "cs_VerifiedBeacons");
4296+
ScraperVerifiedBeacons& ScraperVerifiedBeacons = GetVerifiedBeacons();
42874297

4288-
ScraperVerifiedBeacons& ScraperVerifiedBeacons = GetVerifiedBeacons();
4298+
if (!ScraperVerifiedBeacons.mVerifiedMap.empty())
4299+
{
4300+
CScraperManifest::dentry ProjectEntry;
42894301

4290-
if (!ScraperVerifiedBeacons.mVerifiedMap.empty())
4291-
{
4292-
CScraperManifest::dentry ProjectEntry;
4302+
ProjectEntry.project = "VerifiedBeacons";
4303+
ProjectEntry.LastModified = ScraperVerifiedBeacons.timestamp;
4304+
ProjectEntry.current = true;
42934305

4294-
ProjectEntry.project = "VerifiedBeacons";
4295-
ProjectEntry.LastModified = ScraperVerifiedBeacons.timestamp;
4296-
ProjectEntry.current = true;
4306+
// For now each object will only have one part.
4307+
ProjectEntry.part1 = iPartNum;
4308+
ProjectEntry.partc = 0;
4309+
ProjectEntry.GridcoinTeamID = -1; //Not used anymore
42974310

4298-
// For now each object will only have one part.
4299-
ProjectEntry.part1 = iPartNum;
4300-
ProjectEntry.partc = 0;
4301-
ProjectEntry.GridcoinTeamID = -1; //Not used anymore
4311+
ProjectEntry.last = 1;
43024312

4303-
ProjectEntry.last = 1;
4313+
manifest->projects.push_back(ProjectEntry);
43044314

4305-
manifest->projects.push_back(ProjectEntry);
4315+
CDataStream part(SER_NETWORK, 1);
43064316

4307-
CDataStream part(SER_NETWORK, 1);
4317+
part << ScraperVerifiedBeacons.mVerifiedMap;
43084318

4309-
part << ScraperVerifiedBeacons.mVerifiedMap;
4319+
LOCK(CSplitBlob::cs_mapParts);
4320+
_log(logattribute::INFO, "LOCK", "cs_mapParts");
43104321

4311-
LOCK(CSplitBlob::cs_mapParts);
4312-
_log(logattribute::INFO, "LOCK", "cs_mapParts");
4322+
manifest->addPartData(std::move(part));
43134323

4314-
manifest->addPartData(std::move(part));
4324+
iPartNum++;
43154325

4316-
iPartNum++;
4326+
_log(logattribute::INFO, "ENDLOCK", "cs_mapParts");
4327+
}
43174328

4318-
_log(logattribute::INFO, "ENDLOCK", "cs_mapParts");
4329+
_log(logattribute::INFO, "ENDLOCK", "cs_VerifiedBeacons");
43194330
}
43204331

4321-
_log(logattribute::INFO, "ENDLOCK", "cs_VerifiedBeacons");
4322-
}
4323-
4332+
for (auto const& entry : StructScraperFileManifest.mScraperFileManifest)
4333+
{
4334+
auto scraper_cmanifest_include_noncurrent_proj_files =
4335+
[]() { LOCK(cs_ScraperGlobals); return SCRAPER_CMANIFEST_INCLUDE_NONCURRENT_PROJ_FILES; };
43244336

4325-
for (auto const& entry : StructScraperFileManifest.mScraperFileManifest)
4326-
{
4327-
auto scraper_cmanifest_include_noncurrent_proj_files =
4328-
[]() { LOCK(cs_ScraperGlobals); return SCRAPER_CMANIFEST_INCLUDE_NONCURRENT_PROJ_FILES; };
4337+
// If SCRAPER_CMANIFEST_INCLUDE_NONCURRENT_PROJ_FILES is false, only include current files to send across the network.
4338+
// Also continue (exclude) if it is a non-publishable entry (excludefromcsmanifest is true).
4339+
if ((!scraper_cmanifest_include_noncurrent_proj_files() && !entry.second.current) || entry.second.excludefromcsmanifest)
4340+
continue;
43294341

4330-
// If SCRAPER_CMANIFEST_INCLUDE_NONCURRENT_PROJ_FILES is false, only include current files to send across the network.
4331-
// Also continue (exclude) if it is a non-publishable entry (excludefromcsmanifest is true).
4332-
if ((!scraper_cmanifest_include_noncurrent_proj_files() && !entry.second.current) || entry.second.excludefromcsmanifest)
4333-
continue;
4342+
fs::path inputfile = entry.first;
43344343

4335-
fs::path inputfile = entry.first;
4344+
//_log(logattribute::INFO, "ScraperSendFileManifestContents", "Input file for CScraperManifest is " + inputfile.string());
43364345

4337-
//_log(logattribute::INFO, "ScraperSendFileManifestContents", "Input file for CScraperManifest is " + inputfile.string());
4346+
fs::path inputfilewpath = pathScraper / inputfile;
43384347

4339-
fs::path inputfilewpath = pathScraper / inputfile;
4348+
// open input file, and associate with CAutoFile
4349+
FILE *file = fsbridge::fopen(inputfilewpath, "rb");
4350+
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
43404351

4341-
// open input file, and associate with CAutoFile
4342-
FILE *file = fsbridge::fopen(inputfilewpath, "rb");
4343-
CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
4352+
if (filein.IsNull())
4353+
{
4354+
_log(logattribute::ERR, "ScraperSendFileManifestContents", "Failed to open file (" + inputfile.string() + ")");
4355+
return false;
4356+
}
43444357

4345-
if (filein.IsNull())
4346-
{
4347-
_log(logattribute::ERR, "ScraperSendFileManifestContents", "Failed to open file (" + inputfile.string() + ")");
4348-
return false;
4349-
}
4358+
// use file size to size memory buffer
4359+
int dataSize = fs::file_size(inputfilewpath);
4360+
std::vector<unsigned char> vchData;
4361+
vchData.resize(dataSize);
43504362

4351-
// use file size to size memory buffer
4352-
int dataSize = fs::file_size(inputfilewpath);
4353-
std::vector<unsigned char> vchData;
4354-
vchData.resize(dataSize);
4363+
// read data from file
4364+
try
4365+
{
4366+
filein.read((char *)&vchData[0], dataSize);
4367+
}
4368+
catch (std::exception &e)
4369+
{
4370+
_log(logattribute::ERR, "ScraperSendFileManifestContents", "Failed to read file (" + inputfile.string() + ")");
4371+
return false;
4372+
}
43554373

4356-
// read data from file
4357-
try
4358-
{
4359-
filein.read((char *)&vchData[0], dataSize);
4360-
}
4361-
catch (std::exception &e)
4362-
{
4363-
_log(logattribute::ERR, "ScraperSendFileManifestContents", "Failed to read file (" + inputfile.string() + ")");
4364-
return false;
4365-
}
4374+
filein.fclose();
43664375

4367-
filein.fclose();
43684376

4377+
CScraperManifest::dentry ProjectEntry;
43694378

4370-
CScraperManifest::dentry ProjectEntry;
4379+
ProjectEntry.project = entry.second.project;
4380+
std::string sProject = entry.second.project + "-";
43714381

4372-
ProjectEntry.project = entry.second.project;
4373-
std::string sProject = entry.second.project + "-";
4382+
std::string sinputfile = inputfile.string();
4383+
std::string suffix = ".csv.gz";
43744384

4375-
std::string sinputfile = inputfile.string();
4376-
std::string suffix = ".csv.gz";
4385+
// Remove project-
4386+
sinputfile.erase(sinputfile.find(sProject), sProject.length());
4387+
// Remove suffix. What is left is the ETag.
4388+
ProjectEntry.ETag = sinputfile.erase(sinputfile.find(suffix), suffix.length());
43774389

4378-
// Remove project-
4379-
sinputfile.erase(sinputfile.find(sProject), sProject.length());
4380-
// Remove suffix. What is left is the ETag.
4381-
ProjectEntry.ETag = sinputfile.erase(sinputfile.find(suffix), suffix.length());
4390+
ProjectEntry.LastModified = entry.second.timestamp;
43824391

4383-
ProjectEntry.LastModified = entry.second.timestamp;
4392+
// For now each object will only have one part.
4393+
ProjectEntry.part1 = iPartNum;
4394+
ProjectEntry.partc = 0;
4395+
ProjectEntry.GridcoinTeamID = -1; //Not used anymore
43844396

4385-
// For now each object will only have one part.
4386-
ProjectEntry.part1 = iPartNum;
4387-
ProjectEntry.partc = 0;
4388-
ProjectEntry.GridcoinTeamID = -1; //Not used anymore
4397+
ProjectEntry.current = entry.second.current;
43894398

4390-
ProjectEntry.current = entry.second.current;
4399+
ProjectEntry.last = 1;
43914400

4392-
ProjectEntry.last = 1;
4401+
manifest->projects.push_back(ProjectEntry);
43934402

4394-
manifest->projects.push_back(ProjectEntry);
4403+
CDataStream part(vchData, SER_NETWORK, 1);
43954404

4396-
CDataStream part(vchData, SER_NETWORK, 1);
4405+
manifest->addPartData(std::move(part));
43974406

4398-
manifest->addPartData(std::move(part));
4407+
iPartNum++;
4408+
}
43994409

4400-
iPartNum++;
4410+
_log(logattribute::INFO, "ENDLOCK2", "cs_mapParts, cs_manifest");
44014411
}
44024412

44034413
// "Sign" and "send".
@@ -4447,6 +4457,9 @@ ConvergedManifest::ConvergedManifest()
44474457

44484458
ConvergedManifest::ConvergedManifest(CScraperManifest_shared_ptr& in)
44494459
{
4460+
// Make Clang happy.
4461+
LOCK(in->cs_manifest);
4462+
44504463
ConsensusBlock = in->ConsensusBlock;
44514464
timestamp = GetAdjustedTime();
44524465
bByParts = false;
@@ -4462,6 +4475,8 @@ ConvergedManifest::ConvergedManifest(CScraperManifest_shared_ptr& in)
44624475

44634476
bool ConvergedManifest::operator()(const CScraperManifest_shared_ptr& in)
44644477
{
4478+
LOCK(in->cs_manifest);
4479+
44654480
ConsensusBlock = in->ConsensusBlock;
44664481
timestamp = GetAdjustedTime();
44674482
bByParts = false;

0 commit comments

Comments
 (0)