Skip to content

Commit 7f690ec

Browse files
committed
Protect CcdbApi ROOT thread-unsafe methods by mutex
ROOT I/O as well as TClass::GetClass(std::type_info) methods are not thread safe, need to be guarded if CcdbApi is used in multithread code
1 parent 0707c26 commit 7f690ec

File tree

2 files changed

+43
-23
lines changed

2 files changed

+43
-23
lines changed

CCDB/include/CCDB/CcdbApi.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -376,33 +376,36 @@ class CcdbApi //: public DatabaseInterface
376376
* @param cl The TClass object describing the serialized type
377377
* @return raw pointer to created object
378378
*/
379-
void* extractFromLocalFile(std::string const& filename, TClass const* cl) const;
379+
void* extractFromLocalFile(std::string const& filename, std::type_info const& tinfo) const;
380380

381381
/**
382382
* Helper function to download binary content from alien:// storage
383383
* @param fullUrl The alien URL
384384
* @param tcl The TClass object describing the serialized type
385385
* @return raw pointer to created object
386386
*/
387-
void* downloadAlienContent(std::string const& fullUrl, TClass* tcl) const;
387+
void* downloadAlienContent(std::string const& fullUrl, std::type_info const& tinfo) const;
388388

389389
// initialize the TGrid (Alien connection)
390390
bool initTGrid() const;
391391
// checks if an alien token is available, required to make a TGrid connection
392392
bool checkAlienToken() const;
393393

394394
/// Queries the CCDB server and navigates through possible redirects until binary content is found; Retrieves content as instance
395-
/// given by TClass if that is possible. Returns nullptr if something fails...
396-
void* navigateURLsAndRetrieveContent(CURL*, std::string const& url, TClass* cl, std::map<std::string, std::string>* headers) const;
395+
/// given by tinfo if that is possible. Returns nullptr if something fails...
396+
void* navigateURLsAndRetrieveContent(CURL*, std::string const& url, std::type_info const& tinfo, std::map<std::string, std::string>* headers) const;
397397

398398
// helper that interprets a content chunk as TMemFile and extracts the object therefrom
399-
void* interpretAsTMemFileAndExtract(char* contentptr, size_t contentsize, TClass* cl) const;
399+
void* interpretAsTMemFileAndExtract(char* contentptr, size_t contentsize, std::type_info const& tinfo) const;
400400

401401
/**
402402
* Initialization of CURL
403403
*/
404404
void curlInit();
405405

406+
// convert type_info to TClass, throw on failure
407+
static TClass* tinfo2TClass(std::type_info const& tinfo);
408+
406409
/// Base URL of the CCDB (with port)
407410
std::string mUrl{};
408411
std::string mSnapshotTopPath{};

CCDB/src/CcdbApi.cxx

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <boost/filesystem.hpp>
3939
#include <boost/algorithm/string.hpp>
4040
#include <iostream>
41+
#include <mutex>
4142

4243
namespace o2
4344
{
@@ -46,6 +47,8 @@ namespace ccdb
4647

4748
using namespace std;
4849

50+
std::mutex gIOMutex; // to protect TMemFile IO operations
51+
4952
CcdbApi::~CcdbApi()
5053
{
5154
curl_global_cleanup();
@@ -97,6 +100,7 @@ std::unique_ptr<std::vector<char>> CcdbApi::createObjectImage(const void* obj, s
97100
{
98101
// Create a binary image of the object, if CcdbObjectInfo pointer is provided, register there
99102
// the assigned object class name and the filename
103+
std::lock_guard<std::mutex> guard(gIOMutex);
100104
std::string className = o2::utils::MemFileHelper::getClassName(tinfo);
101105
std::string tmpFileName = generateFileName(className);
102106
if (info) {
@@ -116,6 +120,7 @@ std::unique_ptr<std::vector<char>> CcdbApi::createObjectImage(const TObject* roo
116120
info->setFileName(tmpFileName);
117121
info->setObjectType("TObject"); // why TObject and not the actual name?
118122
}
123+
std::lock_guard<std::mutex> guard(gIOMutex);
119124
return o2::utils::MemFileHelper::createFileImage(*rootObject, tmpFileName, CCDBOBJECT_ENTRY);
120125
}
121126

@@ -358,6 +363,7 @@ TObject* CcdbApi::retrieve(std::string const& path, std::map<std::string, std::s
358363
long response_code;
359364
res = curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response_code);
360365
if ((res == CURLE_OK) && (response_code != 404)) {
366+
std::lock_guard<std::mutex> guard(gIOMutex);
361367
TMessage mess(kMESS_OBJECT);
362368
mess.SetBuffer(chunk.memory, chunk.size, kFALSE);
363369
mess.SetReadMode();
@@ -484,6 +490,7 @@ TObject* CcdbApi::retrieveFromTFile(std::string const& path, std::map<std::strin
484490
if ((res == CURLE_OK) && (response_code != 404)) {
485491
Int_t previousErrorLevel = gErrorIgnoreLevel;
486492
gErrorIgnoreLevel = kFatal;
493+
std::lock_guard<std::mutex> guard(gIOMutex);
487494
TMemFile memFile("name", chunk.memory, chunk.size, "READ");
488495
gErrorIgnoreLevel = previousErrorLevel;
489496
if (!memFile.IsZombie()) {
@@ -584,14 +591,13 @@ void CcdbApi::retrieveBlob(std::string const& path, std::string const& targetdir
584591
if (success) {
585592
// trying to append metadata to the file so that it can be inspected WHERE/HOW/WHAT IT corresponds to
586593
// Just a demonstrator for the moment
587-
TFile snapshotfile(targetpath.c_str(), "UPDATE");
588594
CCDBQuery querysummary(path, metadata, timestamp);
589-
snapshotfile.WriteObjectAny(&querysummary, TClass::GetClass(typeid(querysummary)), CCDBQUERY_ENTRY);
590-
591595
// retrieveHeaders
592596
auto headers = retrieveHeaders(path, metadata, timestamp);
597+
std::lock_guard<std::mutex> guard(gIOMutex);
598+
TFile snapshotfile(targetpath.c_str(), "UPDATE");
599+
snapshotfile.WriteObjectAny(&querysummary, TClass::GetClass(typeid(querysummary)), CCDBQUERY_ENTRY);
593600
snapshotfile.WriteObjectAny(&headers, TClass::GetClass(typeid(metadata)), CCDBMETA_ENTRY);
594-
595601
snapshotfile.Close();
596602
}
597603
}
@@ -641,12 +647,14 @@ void* CcdbApi::extractFromTFile(TFile& file, TClass const* cl)
641647
return result;
642648
}
643649

644-
void* CcdbApi::extractFromLocalFile(std::string const& filename, TClass const* tcl) const
650+
void* CcdbApi::extractFromLocalFile(std::string const& filename, std::type_info const& tinfo) const
645651
{
646652
if (!boost::filesystem::exists(filename)) {
647653
LOG(INFO) << "Local snapshot " << filename << " not found \n";
648654
return nullptr;
649655
}
656+
std::lock_guard<std::mutex> guard(gIOMutex);
657+
auto tcl = tinfo2TClass(tinfo);
650658
TFile f(filename.c_str(), "READ");
651659
return extractFromTFile(f, tcl);
652660
}
@@ -673,28 +681,32 @@ bool CcdbApi::initTGrid() const
673681
return mAlienInstance != nullptr;
674682
}
675683

676-
void* CcdbApi::downloadAlienContent(std::string const& url, TClass* cl) const
684+
void* CcdbApi::downloadAlienContent(std::string const& url, std::type_info const& tinfo) const
677685
{
678686
if (!initTGrid()) {
679687
return nullptr;
680688
}
689+
std::lock_guard<std::mutex> guard(gIOMutex);
681690
auto memfile = TMemFile::Open(url.c_str(), "OPEN");
682691
if (memfile) {
692+
auto cl = tinfo2TClass(tinfo);
683693
auto content = extractFromTFile(*memfile, cl);
684694
delete memfile;
685695
return content;
686696
}
687697
return nullptr;
688698
}
689699

690-
void* CcdbApi::interpretAsTMemFileAndExtract(char* contentptr, size_t contentsize, TClass* tcl) const
700+
void* CcdbApi::interpretAsTMemFileAndExtract(char* contentptr, size_t contentsize, std::type_info const& tinfo) const
691701
{
692702
void* result = nullptr;
693703
Int_t previousErrorLevel = gErrorIgnoreLevel;
694704
gErrorIgnoreLevel = kFatal;
705+
std::lock_guard<std::mutex> guard(gIOMutex);
695706
TMemFile memFile("name", contentptr, contentsize, "READ");
696707
gErrorIgnoreLevel = previousErrorLevel;
697708
if (!memFile.IsZombie()) {
709+
auto tcl = tinfo2TClass(tinfo);
698710
result = extractFromTFile(memFile, tcl);
699711
if (!result) {
700712
LOG(ERROR) << o2::utils::concat_string("Couldn't retrieve object corresponding to ", tcl->GetName(), " from TFile");
@@ -705,7 +717,7 @@ void* CcdbApi::interpretAsTMemFileAndExtract(char* contentptr, size_t contentsiz
705717
}
706718

707719
// navigate sequence of URLs until TFile content is found; object is extracted and returned
708-
void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string const& url, TClass* cl, std::map<string, string>* headers) const
720+
void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string const& url, std::type_info const& tinfo, std::map<string, string>* headers) const
709721
{
710722
// a global internal data structure that can be filled with HTTP header information
711723
// static --> to avoid frequent alloc/dealloc as optimization
@@ -714,7 +726,7 @@ void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string con
714726

715727
// let's see first of all if the url is something specific that curl cannot handle
716728
if (url.find("alien:/", 0) != std::string::npos) {
717-
return downloadAlienContent(url, cl);
729+
return downloadAlienContent(url, tinfo);
718730
}
719731
// add other final cases here
720732
// example root://
@@ -750,7 +762,7 @@ void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string con
750762
}
751763
if (200 <= response_code && response_code < 300) {
752764
// good response and the content is directly provided and should have been dumped into "chunk"
753-
content = interpretAsTMemFileAndExtract(chunk.memory, chunk.size, cl);
765+
content = interpretAsTMemFileAndExtract(chunk.memory, chunk.size, tinfo);
754766
} else if (response_code == 304) {
755767
// this means the object exist but I am not serving
756768
// it since it's already in your possession
@@ -791,7 +803,7 @@ void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string con
791803
for (auto& l : locs) {
792804
if (l.size() > 0) {
793805
LOG(DEBUG) << "Trying content location " << l;
794-
content = navigateURLsAndRetrieveContent(curl_handle, l, cl, nullptr);
806+
content = navigateURLsAndRetrieveContent(curl_handle, l, tinfo, nullptr);
795807
if (content /* or other success marker in future */) {
796808
break;
797809
}
@@ -824,17 +836,12 @@ void* CcdbApi::retrieveFromTFile(std::type_info const& tinfo, std::string const&
824836
const std::string& createdNotAfter, const std::string& createdNotBefore) const
825837
{
826838
// We need the TClass for this type; will verify if dictionary exists
827-
auto tcl = TClass::GetClass(tinfo);
828-
if (!tcl) {
829-
std::cerr << "Could not retrieve ROOT dictionary for type " << tinfo.name() << " aborting to read from CCDB\n";
830-
return nullptr;
831-
}
832839

833840
CURL* curl_handle = curl_easy_init();
834841
string fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp);
835842
// if we are in snapshot mode we can simply open the file; extract the object and return
836843
if (mInSnapshotMode) {
837-
return extractFromLocalFile(fullUrl, tcl);
844+
return extractFromLocalFile(fullUrl, tinfo);
838845
}
839846

840847
// add some global options to the curl query
@@ -853,7 +860,7 @@ void* CcdbApi::retrieveFromTFile(std::type_info const& tinfo, std::string const&
853860
}
854861
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list);
855862

856-
auto content = navigateURLsAndRetrieveContent(curl_handle, fullUrl, tcl, headers);
863+
auto content = navigateURLsAndRetrieveContent(curl_handle, fullUrl, tinfo, headers);
857864
curl_easy_cleanup(curl_handle);
858865
return content;
859866
}
@@ -1142,5 +1149,15 @@ std::vector<std::string> CcdbApi::getAllFolders(std::string const& top) const
11421149
return folders;
11431150
}
11441151

1152+
TClass* CcdbApi::tinfo2TClass(std::type_info const& tinfo)
1153+
{
1154+
TClass* cl = TClass::GetClass(tinfo);
1155+
if (!cl) {
1156+
throw std::runtime_error(fmt::format("Could not retrieve ROOT dictionary for type {}, aborting", tinfo.name()));
1157+
return nullptr;
1158+
}
1159+
return cl;
1160+
}
1161+
11451162
} // namespace ccdb
11461163
} // namespace o2

0 commit comments

Comments
 (0)