ATLAS Offline Software
Loading...
Searching...
No Matches
AthenaPoolSharedIOCnvSvc.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
9
11
12#include "GaudiKernel/ClassID.h"
13#include "GaudiKernel/FileIncident.h"
14#include "GaudiKernel/GenericAddress.h"
15#include "GaudiKernel/IIncidentSvc.h"
16#include "GaudiKernel/IOpaqueAddress.h"
17
25
26#include "StorageSvc/DbReflex.h"
27#include "StorageSvc/DbType.h"
29
31
32#include <algorithm>
33#include <iomanip>
34#include <sstream>
35#include <format>
36
37//______________________________________________________________________________
38// Initialize the service.
40 if (!m_inputStreamingTool.empty() || !m_outputStreamingTool.empty()) {
41 // Retrieve AthenaSerializeSvc
42 ATH_CHECK(m_serializeSvc.retrieve());
43 m_auxDynTool = Gaudi::PluginService::Factory< RootAuxDynIO::IFactoryTool*() >::create("RootAuxDynIO::FactoryTool");
44 if (!m_auxDynTool) {
45 return StatusCode::FAILURE;
46 }
47 }
48 // Retrieve InputStreamingTool (if configured)
49 if (!m_inputStreamingTool.empty()) {
51 m_auxInput = m_auxDynTool->getAuxDynShare(m_serializeSvc.get(), m_inputStreamingTool.get());
52 }
53 // Retrieve OutputStreamingTool (if configured)
54 if (!m_outputStreamingTool.empty()) {
56 m_auxOutput = m_auxDynTool->getAuxDynShare(m_serializeSvc.get(), m_outputStreamingTool.get());
57 if (m_makeStreamingToolClient.value() == -1) {
58 // Initialize AthenaRootSharedWriter
59 ServiceHandle<IService> arswsvc("AthenaRootSharedWriterSvc", this->name());
60 ATH_CHECK(arswsvc.retrieve());
61 }
62 // Put PoolSvc into share mode to avoid duplicating catalog.
63 getPoolSvc()->setShareMode(true);
64 }
65 ServiceHandle<IIncidentSvc> incSvc("IncidentSvc", name());
66 long int pri = 1000;
67 if (!m_outputStreamingTool.empty()) {
68 incSvc->addListener(this, "StoreCleared", pri);
69 ATH_MSG_DEBUG("Subscribed to StoreCleared");
70 }
71 return this->AthenaPoolCnvSvc::initialize();
72}
73//______________________________________________________________________________
75 // Release AthenaSerializeSvc
76 if (!m_serializeSvc.empty()) {
77 if (!m_serializeSvc.release().isSuccess()) {
78 ATH_MSG_WARNING("Cannot release AthenaSerializeSvc.");
79 }
80 }
81 // Release OutputStreamingTool (if configured)
82 if (!m_outputStreamingTool.empty()) {
83 if (!m_outputStreamingTool.release().isSuccess()) {
84 ATH_MSG_WARNING("Cannot release Output AthenaIPCTool.");
85 }
86 }
87 // Release InputStreamingTool (if configured)
88 if (!m_inputStreamingTool.empty()) {
89 if (!m_inputStreamingTool.release().isSuccess()) {
90 ATH_MSG_WARNING("Cannot release Input AthenaIPCTool.");
91 }
92 }
93 return this->AthenaPoolCnvSvc::finalize();
94}
95//______________________________________________________________________________
96StatusCode AthenaPoolSharedIOCnvSvc::connectOutput(const std::string& outputConnectionSpec,
97 const std::string& openMode) {
98 return AthenaPoolCnvSvc::connectOutput(outputConnectionSpec, openMode);
99}
100//______________________________________________________________________________
101StatusCode AthenaPoolSharedIOCnvSvc::connectOutput(const std::string& outputConnectionSpec) {
102// This is called before DataObjects are being converted.
103 std::string outputConnection = outputConnectionSpec.substr(0, outputConnectionSpec.find('['));
104 if (m_makeStreamingToolClient.value() > 0 && !m_outputStreamingTool.empty() && !m_outputStreamingTool->isServer() && !m_outputStreamingTool->isClient()) {
105 if (!makeClient(m_makeStreamingToolClient.value()).isSuccess()) {
106 ATH_MSG_ERROR("Could not make AthenaPoolSharedIOCnvSvc a Share Client");
107 return(StatusCode::FAILURE);
108 }
109 }
110 if (!m_outputStreamingTool.empty() && m_outputStreamingTool->isClient()
111 && (!m_parallelCompression || outputConnectionSpec.find("[PoolContainerPrefix=" + m_metadataContainerProp.value() + "]") != std::string::npos)) {
112 return(StatusCode::SUCCESS);
113 }
114 if (!m_outputStreamingTool.empty() && !m_outputStreamingTool->isClient()) {
115 if (m_parallelCompression && outputConnectionSpec.find("[PoolContainerPrefix=" + m_metadataContainerProp.value() + "]") == std::string::npos) {
116 ATH_MSG_DEBUG(std::format("connectOutput SKIPPED for metadata-only server: {}", outputConnectionSpec));
117 return(StatusCode::SUCCESS);
118 }
120 ATH_MSG_DEBUG("connectOutput SKIPPED for expired server.");
121 return(StatusCode::SUCCESS);
122 }
123 }
125 outputConnection += m_streamPortString.value();
126 }
127 std::size_t apend = outputConnectionSpec.find('[');
128 if (apend != std::string::npos) {
129 outputConnection += outputConnectionSpec.substr(apend);
130 }
131 if (outputConnectionSpec.find("[PoolContainerPrefix=" + m_metadataContainerProp.value() + "]") != std::string::npos) {
132 return AthenaPoolCnvSvc::connectOutput(outputConnection, "APPEND");
133 }
134 return AthenaPoolCnvSvc::connectOutput(outputConnection);
135}
136
137//______________________________________________________________________________
138StatusCode AthenaPoolSharedIOCnvSvc::commitOutput(const std::string& outputConnectionSpec, bool doCommit) {
139 // This is called after all DataObjects are converted.
140 std::string outputConnection = outputConnectionSpec.substr(0, outputConnectionSpec.find('['));
141 if (!m_outputStreamingTool.empty() && m_outputStreamingTool->isClient()
142 && (!m_parallelCompression || outputConnectionSpec.find("[PoolContainerPrefix=" + m_metadataContainerProp.value() + "]") != std::string::npos)) {
143 m_outputStreamingTool->lockObject("wait").ignore();
144 if (!this->cleanUp(outputConnection).isSuccess()) {
145 ATH_MSG_ERROR("commitOutput FAILED to cleanup converters.");
146 return(StatusCode::FAILURE);
147 }
148 return(StatusCode::SUCCESS);
149 }
150 if (!m_outputStreamingTool.empty() && !m_outputStreamingTool->isClient() && !m_outputStreamingTool->isServer()) {
151 ATH_MSG_DEBUG("commitOutput SKIPPED for uninitialized server.");
152 return(StatusCode::SUCCESS);
153 }
154 std::map<void*, RootType> commitCache;
155 std::string fileName;
156 if (!m_outputStreamingTool.empty() && !m_outputStreamingTool->isClient() && m_outputStreamingTool->isServer() && m_streamServerActive) {
157 // Clear object to get Placements for all objects in a Stream
158 const char* placementStr = nullptr;
159 int num = -1;
160 StatusCode sc = m_outputStreamingTool->clearObject(&placementStr, num);
161 if (sc.isSuccess() && placementStr != nullptr && strlen(placementStr) > 6 && num > 0) {
162 const char * matchedChars = strstr(placementStr, "[FILE=");
163 if (!matchedChars){
164 ATH_MSG_ERROR(std::format("No matching filename in {}", placementStr));
165 return abortSharedWrClients(num);
166 }
167 fileName = matchedChars;
168 fileName = fileName.substr(6, fileName.find(']') - 6);
169 if (!this->connectOutput(fileName).isSuccess()) {
170 ATH_MSG_ERROR(std::format("Failed to connectOutput for {}", fileName));
171 return abortSharedWrClients(num);
172 }
173 IConverter* DHcnv = converter(ClassID_traits<DataHeader>::ID());
174 bool dataHeaderSeen = false;
175 std::string dataHeaderID;
176 while (num > 0) {
177 std::string objName = "ALL";
178 if (useDetailChronoStat()) {
179 objName = placementStr; //FIXME, better descriptor
180 }
181 // StopWatch listens from here until the end of this current scope
182 {
183 PMonUtils::BasicStopWatch stopWatch("cRep_" + objName, this->m_chronoMap);
184 std::string_view pStr = placementStr;
185 std::string::size_type cpos = pStr.find ("[CONT=");
186 if (cpos == std::string::npos) {
187 ATH_MSG_ERROR(std::format("No CONT field in placement string: {}", pStr));
188 return StatusCode::FAILURE;
189 }
190 std::string tokenStr (pStr.substr(0, cpos));
191 std::string contName (pStr.substr(cpos, std::string::npos));
192 std::string::size_type cl1 = contName.find(']');
193 if (cl1 == std::string::npos) {
194 ATH_MSG_ERROR(std::format("Missing close bracket after CONT field in placement string: {}", pStr));
195 return StatusCode::FAILURE;
196 }
197 tokenStr.append(contName, cl1 + 1);
198 contName = contName.substr(6, cl1 - 6);
199
200 std::string::size_type ppos = pStr.find ("[PNAME=");
201 if (ppos == std::string::npos) {
202 ATH_MSG_ERROR(std::format("No PNAME field in placement string: {}", pStr));
203 return StatusCode::FAILURE;
204 }
205 std::string className (pStr.substr(ppos, std::string::npos));
206 std::string::size_type cl2 = className.find(']');
207 if (cl2 == std::string::npos) {
208 ATH_MSG_ERROR(std::format("Missing close bracket after PNAME field in placement string: {}", pStr));
209 return StatusCode::FAILURE;
210 }
211 className = className.substr(7, cl2 - 7);
212 RootType classDesc = RootType::ByNameNoQuiet(className);
213 void* obj = nullptr;
214 const std::string numStr = std::to_string(num);
215 std::string::size_type len = m_metadataContainerProp.value().size();
216 bool foundContainer = false;
217 std::size_t opPos = contName.find('(');
218 if (contName.compare(0, opPos, m_metadataContainerProp.value()) == 0) {
219 foundContainer = true;
220 } else {
221 for (const auto& item: m_metadataContainersAug.value()) {
222 if (contName.compare(0, opPos, item) == 0){
223 foundContainer = true;
224 len = item.size();
225 break;
226 }
227 }
228 }
229 if (len > 0 && foundContainer && contName[len] == '(' ) {
230 ServiceHandle<IIncidentSvc> incSvc("IncidentSvc", name());
231 // For Metadata, before moving to next client, fire file incidents
232 if (m_metadataClient != num) {
233 if (m_metadataClient != 0) {
234 std::string memName = std::format("SHM[NUM={}]", m_metadataClient);
235 auto guard = InputFileIncidentGuard::begin(*incSvc, name(),
236 memName, {}, /*endFileName=*/memName,
237 "BeginInputMemFile", "EndInputMemFile");
238 }
239 m_metadataClient = num;
240 }
241 // Retrieve MetaDataSvc
242 ServiceHandle<IAthMetaDataSvc> metadataSvc("MetaDataSvc", name());
243 ATH_CHECK(metadataSvc.retrieve());
244 sc = metadataSvc->shmProxy(std::format("{}[NUM={}]", pStr, numStr));
245 if (sc.isRecoverable()) {
246 ATH_MSG_WARNING("MetaDataSvc::shmProxy() no proxy added.");
247 } else if (sc.isFailure()) {
248 ATH_MSG_FATAL("MetaDataSvc::shmProxy() failed!");
249 return abortSharedWrClients(num);
250 }
251 } else {
252 Token readToken;
253 readToken.setOid(Token::OID_t(num, 0));
254 readToken.setAuxString("[PNAME=" + className + "]");
255 this->setObjPtr(obj, &readToken); // Pull/read Object out of shared memory
256 if (len == 0 || contName.compare(0, len, m_metadataContainerProp.value()) != 0) {
257 // Write object
258 if( m_oneDataHeaderForm.value() ) {
259 auto placementWithSwn = [&] { return std::format("{}[SWN={}]", placementStr, num); };
260 if( className == "DataHeaderForm_p6" ) {
261 // Pass DHForms to the converter for later writing in the correct order - do not write it now
263 "", placementWithSwn());
264 DHcnv->updateRepRefs(&address, static_cast<DataObject*>(obj)).ignore();
265 tokenStr = "";
266 } else {
267 Placement placement;
268 placement.fromString(placementStr);
269 std::unique_ptr<Token> token(registerForWrite(&placement, obj, classDesc));
270 if (token == nullptr) {
271 ATH_MSG_ERROR("Failed to write Data for: " << className);
272 return abortSharedWrClients(num);
273 }
274 tokenStr = token->toString();
275 }
276 if( className == "DataHeader_p6" ) {
277 // Found DataHeader - call the converter to update DHForm Ref
279 tokenStr, placementWithSwn());
280 if (!DHcnv->updateRep(&address, static_cast<DataObject*>(obj)).isSuccess()) {
281 ATH_MSG_ERROR("Failed updateRep for obj = " << tokenStr);
282 return abortSharedWrClients(num);
283 }
284 } else
285 if (className != "Token" && className != "DataHeaderForm_p6" && !classDesc.IsFundamental()) {
286 commitCache.insert(std::pair<void*, RootType>(obj, classDesc));
287 }
288 placementStr = nullptr;
289 } else {
290 // Multiple shared DataHeaderForms
291 Placement placement;
292 placement.fromString(placementStr); placementStr = nullptr;
293 std::unique_ptr<Token> token(registerForWrite(&placement, obj, classDesc));
294 if (token == nullptr) {
295 ATH_MSG_ERROR("Failed to write Data for: " << className);
296 return abortSharedWrClients(num);
297 }
298 tokenStr = token->toString();
299 if (className == "DataHeader_p6") {
300 // Found DataHeader
302 tokenStr, placement.auxString());
303 // call DH converter to add the ref to DHForm (stored earlier) and to itself
304 if (!DHcnv->updateRep(&address, static_cast<DataObject*>(obj)).isSuccess()) {
305 ATH_MSG_ERROR("Failed updateRep for obj = " << tokenStr);
306 return abortSharedWrClients(num);
307 }
308 dataHeaderSeen = true;
309 // This dataHeaderID is used in DataHeaderCnv to index the DataHeaderForm cache.
310 // It must be unique per worker per stream so that we have a correct DataHeader(Form) association.
311 // This is achieved by building it as "CONTID/WORKERID/DBID".
312 // CONTID, e.g., POOLContainer(DataHeader), allows us to distinguish data and metadata headers,
313 // WORKERID allows us to distinguish AthenaMP workers,
314 // and DBID allows us to distinguish streams.
315 dataHeaderID = std::format("{}/{}/{}", token->contID(), numStr, token->dbID().toString());
316 } else if (dataHeaderSeen) {
317 dataHeaderSeen = false;
318 // next object after DataHeader - may be a DataHeaderForm
319 // in any case we need to call the DH converter to update the DHForm Ref
320 if (className == "DataHeaderForm_p6") {
321 // Tell DataHeaderCnv that it should use a new DHForm
323 tokenStr, dataHeaderID);
324 if (!DHcnv->updateRepRefs(&address, static_cast<DataObject*>(obj)).isSuccess()) {
325 ATH_MSG_ERROR("Failed updateRepRefs for obj = " << tokenStr);
326 return abortSharedWrClients(num);
327 }
328 } else {
329 // Tell DataHeaderCnv that it should use the old DHForm
330 GenericAddress address(0, 0, "", dataHeaderID);
331 if (!DHcnv->updateRepRefs(&address, nullptr).isSuccess()) {
332 ATH_MSG_ERROR("Failed updateRepRefs for DataHeader");
333 return abortSharedWrClients(num);
334 }
335 }
336 }
337 if (className != "Token" && className != "DataHeaderForm_p6" && !classDesc.IsFundamental()) {
338 commitCache.insert(std::pair<void*, RootType>(obj, classDesc));
339 }
340 }
341 }
342 }
343 // Send Token back to Client
344 sc = m_outputStreamingTool->lockObject(tokenStr.c_str(), num);
345 while (sc.isRecoverable()) {
346 sc = m_outputStreamingTool->lockObject(tokenStr.c_str(), num);
347 }
348 if (!sc.isSuccess()) {
349 ATH_MSG_ERROR("Failed to lock Data for " << tokenStr);
350 return abortSharedWrClients(-1);
351 }
352 }
353 sc = m_outputStreamingTool->clearObject(&placementStr, num);
354 while (sc.isRecoverable()) {
355 sc = m_outputStreamingTool->clearObject(&placementStr, num);
356 }
357 if (sc.isFailure()) {
358 // no more clients, break the loop and exit
359 num = -1;
360 }
361 }
362 if (dataHeaderSeen) {
363 // DataHeader was the last object, need to tell the converter there is no DHForm coming
364 GenericAddress address(0, 0, "", std::move(dataHeaderID));
365 if (!DHcnv->updateRepRefs(&address, nullptr).isSuccess()) {
366 ATH_MSG_ERROR("Failed updateRepRefs for DataHeader");
367 return abortSharedWrClients(-1);
368 }
369 }
370 placementStr = nullptr;
371 } else if (sc.isSuccess() && placementStr != nullptr && strncmp(placementStr, "stop", 4) == 0) {
372 return(StatusCode::RECOVERABLE);
373 } else if (sc.isRecoverable() || num == -1) {
374 return(StatusCode::RECOVERABLE);
375 }
376 if (sc.isFailure() || fileName.empty()) {
377 ServiceHandle<IIncidentSvc> incSvc("IncidentSvc", name());
378 std::string memName = std::format("SHM[NUM={}]", m_metadataClient);
379 {
380 auto guard = InputFileIncidentGuard::begin(*incSvc, name(),
381 memName, {}, /*endFileName=*/memName,
382 "BeginInputMemFile", "EndInputMemFile");
383 }
384 if (sc.isFailure()) {
385 ATH_MSG_INFO("All SharedWriter clients stopped - exiting");
386 } else {
387 ATH_MSG_INFO("Failed to get Data for client: " << num);
388 }
389 return(StatusCode::FAILURE);
390 }
391 }
392 if (m_parallelCompression && !fileName.empty()) {
393 ATH_MSG_DEBUG(std::format("commitOutput SKIPPED for metadata-only server: {}", outputConnectionSpec));
394 return(StatusCode::SUCCESS);
395 }
396 if (outputConnection.empty()) {
397 outputConnection = std::move(fileName);
398 } else {
399 outputConnection = outputConnectionSpec;
401 outputConnection += m_streamPortString.value();
402 }
403 }
404 std::size_t merge = outputConnection.find("?pmerge="); // Used to remove trailing TMemFile
405 const std::string baseOutputConnection = outputConnection.substr(0, merge);
406 m_fileCommitCounter[baseOutputConnection]++;
408 m_fileFlushSetting.value().contains(baseOutputConnection) &&
409 m_fileFlushSetting[baseOutputConnection] > 0 &&
410 m_fileCommitCounter[baseOutputConnection] % m_fileFlushSetting[baseOutputConnection] == 0) {
411 doCommit = true;
412 ATH_MSG_DEBUG("commitOutput sending data.");
413 }
414 StatusCode status = AthenaPoolCnvSvc::commitOutput(outputConnection, doCommit);
415 for (auto& [ptr, rootType] : commitCache) {
416 rootType.Destruct(ptr);
417 }
418 return(status);
419}
420
421//______________________________________________________________________________
422StatusCode AthenaPoolSharedIOCnvSvc::disconnectOutput(const std::string& outputConnectionSpec) {
423 std::string outputConnection = outputConnectionSpec.substr(0, outputConnectionSpec.find('['));
424 if (!m_outputStreamingTool.empty() && m_outputStreamingTool->isClient()
425 && (!m_parallelCompression || outputConnectionSpec.find("[PoolContainerPrefix=" + m_metadataContainerProp.value() + "]") != std::string::npos)) {
426 return(StatusCode::SUCCESS);
427 }
428 if (!m_outputStreamingTool.empty() && !m_outputStreamingTool->isClient()) {
430 m_streamServerActive = false;
431 ATH_MSG_DEBUG("disconnectOutput SKIPPED to expire server.");
432 return(StatusCode::SUCCESS);
433 } else {
434 m_streamServerActive = false;
435 }
436 ATH_MSG_DEBUG("disconnectOutput not SKIPPED for server.");
437 }
439 outputConnection += m_streamPortString.value();
440 }
441 return AthenaPoolCnvSvc::disconnectOutput(outputConnectionSpec + m_streamPortString.value());
442}
443
444//______________________________________________________________________________
445Token* AthenaPoolSharedIOCnvSvc::registerForWrite(Placement* placement, const void* obj, const RootType& classDesc) {
446 if (m_makeStreamingToolClient.value() > 0 && !m_outputStreamingTool.empty() && !m_outputStreamingTool->isServer() && !m_outputStreamingTool->isClient()) {
447 if (!makeClient(m_makeStreamingToolClient.value()).isSuccess()) {
448 ATH_MSG_ERROR("Could not make AthenaPoolSharedIOCnvSvc a Share Client");
449 return(nullptr);
450 }
451 }
452 Token* token = nullptr;
453 const Guid guid = pool::DbReflex::guid(classDesc);
454 if (!m_outputStreamingTool.empty() && m_outputStreamingTool->isClient()
455 && (!m_parallelCompression || placement->containerName().compare(0, m_metadataContainerProp.value().size(), m_metadataContainerProp.value()) == 0)) {
456 // Lock object
457 std::string placementStr = placement->toString();
458 placementStr += "[PNAME=";
459 placementStr += classDesc.Name();
460 placementStr += ']';
461 ATH_MSG_VERBOSE("Requesting write object for: " << placementStr);
462 StatusCode sc = m_outputStreamingTool->lockObject(placementStr.c_str());
463 while (sc.isRecoverable()) {
464 //usleep(100);
465 sc = m_outputStreamingTool->lockObject(placementStr.c_str());
466 }
467 if (!sc.isSuccess()) {
468 ATH_MSG_ERROR("Failed to lock Data for " << placementStr);
469 return(nullptr);
470 }
471 // Serialize object via ROOT
472 const void* buffer = nullptr;
473 std::size_t nbytes = 0;
474 bool own = true;
475 if (classDesc.Name() == "Token") {
476 nbytes = strlen(static_cast<const char*>(obj)) + 1;
477 buffer = obj;
478 own = false;
479 } else if (classDesc.IsFundamental()) {
480 nbytes = classDesc.SizeOf();
481 buffer = obj;
482 own = false;
483 } else {
484 buffer = m_serializeSvc->serialize(obj, classDesc, nbytes);
485 }
486 // Share object
487 sc = m_outputStreamingTool->putObject(buffer, nbytes);
488 while (sc.isRecoverable()) {
489 //usleep(100);
490 sc = m_outputStreamingTool->putObject(buffer, nbytes);
491 }
492 if (own) { delete [] static_cast<const char*>(buffer); }
493 buffer = nullptr;
494 if (!sc.isSuccess()) {
495 ATH_MSG_ERROR("Could not share object for: " << placementStr);
496 m_outputStreamingTool->putObject(nullptr, 0).ignore();
497 return(nullptr);
498 }
499 const pool::DbTypeInfo* info = pool::DbTypeInfo::create(guid);
500 if (info != nullptr) {
501 if (m_auxDynTool->hasAuxStore(placement->containerName(), info->clazz().Class() ) && !m_auxOutput->sendStore(info->clazz().Class(), obj, guid.toString(), placement->containerName()).isSuccess()) {
502 ATH_MSG_ERROR("Could not share dynamic aux store for: " << placementStr);
503 m_outputStreamingTool->putObject(nullptr, 0).ignore();
504 return(nullptr);
505 }
506 }
507 if (!m_outputStreamingTool->putObject(nullptr, 0).isSuccess()) {
508 ATH_MSG_ERROR("Failed to put Data for " << placementStr);
509 return(nullptr);
510 }
511 // Get Token back from Server
512 const char* tokenStr = nullptr;
513 int num = -1;
514 sc = m_outputStreamingTool->clearObject(&tokenStr, num);
515 while (sc.isRecoverable()) {
516 //usleep(100);
517 sc = m_outputStreamingTool->clearObject(&tokenStr, num);
518 }
519 if (!sc.isSuccess()) {
520 ATH_MSG_ERROR("Failed to get Token");
521 return(nullptr);
522 }
523 if (!strcmp(tokenStr, "ABORT")) {
524 ATH_MSG_ERROR("Writer requested ABORT");
525 // tell the server we are leaving
526 m_outputStreamingTool->stop().ignore();
527 return nullptr;
528 }
529 Token* tempToken = new Token();
530 tempToken->fromString(tokenStr); tokenStr = nullptr;
531 tempToken->setClassID(guid);
532 token = tempToken; tempToken = nullptr;
533// Client Write Request
534 } else {
535 if (!m_outputStreamingTool.empty() && !m_outputStreamingTool->isClient() && !m_outputStreamingTool->isServer()) {
536 ATH_MSG_DEBUG("registerForWrite SKIPPED for uninitialized server, Placement = " << placement->toString());
537 Token* tempToken = new Token();
538 tempToken->setClassID(guid);
539 token = tempToken; tempToken = nullptr;
540 } else if (!m_outputStreamingTool.empty() && !m_outputStreamingTool->isClient() && !m_streamServerActive) {
541 if(placement->technology() == 0) { // No technology specified, use the default
542 placement->setTechnology(pool::DbType::getType(m_defaultContainerType).type());
543 }
544 ATH_MSG_DEBUG("Requested write object for: " << placement->toString());
545 token = getPoolSvc()->registerForWrite(placement, obj, classDesc);
546 } else {
548 placement->setFileName(placement->fileName() + m_streamPortString.value());
549 }
550 token = AthenaPoolCnvSvc::registerForWrite(placement, obj, classDesc);
551 }
552 }
553 return(token);
554}
555//______________________________________________________________________________
556void AthenaPoolSharedIOCnvSvc::setObjPtr(void*& obj, const Token* token) {
557 if (m_makeStreamingToolClient.value() > 0 && !m_inputStreamingTool.empty() && !m_inputStreamingTool->isServer() && !m_inputStreamingTool->isClient()) {
558 if (!makeClient(-m_makeStreamingToolClient.value()).isSuccess()) {
559 ATH_MSG_ERROR("Could not make AthenaPoolSharedIOCnvSvc a Share Client");
560 }
561 }
562 if (!m_outputStreamingTool.empty() && m_outputStreamingTool->isServer()) {
563 if (token->dbID() == Guid::null()) {
564 int num = token->oid().first;
565 // Get object from SHM
566 void* buffer = nullptr;
567 std::size_t nbytes = 0;
568 StatusCode sc = m_outputStreamingTool->getObject(&buffer, nbytes, num);
569 while (sc.isRecoverable()) {
570 sc = m_outputStreamingTool->getObject(&buffer, nbytes, num);
571 }
572 if (!sc.isSuccess()) {
573 ATH_MSG_ERROR("Failed to get Data for " << token->toString());
574 obj = nullptr;
575 } else {
576 ATH_MSG_DEBUG("Server deserializing " << token->toString());
577 if (token->classID() != Guid::null()) {
578 // Deserialize object
579 RootType cltype(pool::DbReflex::forGuid(token->classID()));
580 obj = m_serializeSvc->deserialize(buffer, nbytes, cltype); buffer = nullptr;
581 } else {
582 // Deserialize object
583 std::string className = token->auxString();
584 className = className.substr(className.find("[PNAME="));
585 className = className.substr(7, className.find(']') - 7);
586 RootType cltype(RootType::ByNameNoQuiet(className));
587 obj = m_serializeSvc->deserialize(buffer, nbytes, cltype); buffer = nullptr;
588 }
589 buffer = nullptr;
590 nbytes = 0;
591 sc = m_outputStreamingTool->getObject(&buffer, nbytes, num);
592 while (sc.isRecoverable() && nbytes > 0) {
593 sc = m_outputStreamingTool->getObject(&buffer, nbytes, num);
594 }
595 if (sc.isSuccess() && nbytes > 0) { // Found dynamic attributes
596 Guid classId;
597 classId.fromString(static_cast<const char*>(buffer));
598 if (m_outputStreamingTool->getObject(&buffer, nbytes, num).isSuccess() && nbytes > 0) {
599 const std::string contName = std::string(static_cast<const char*>(buffer));
600 if (classId != Guid::null()) {
601 const pool::DbTypeInfo* info = pool::DbTypeInfo::create(classId);
602 if (info != nullptr) {
603 if (m_auxDynTool->hasAuxStore(contName, info->clazz().Class() ) && !m_auxOutput->receiveStore(info->clazz().Class(), obj, num).isSuccess()) {
604 ATH_MSG_ERROR("Failed to get Dynamic Aux Store for " << token->toString());
605 obj = nullptr;
606 }
607 }
608 }
609 }
610 }
611 }
612 }
613 }
614 if (!m_inputStreamingTool.empty() && m_inputStreamingTool->isClient() && (m_streamingTechnology.value() < 0 || token->technology() == m_streamingTechnology.value())) {
615 ATH_MSG_VERBOSE("Requesting remote object for: " << token->toString());
616 if (!m_inputStreamingTool->lockObject(token->toString().c_str()).isSuccess()) {
617 ATH_MSG_ERROR("Failed to lock Data for " << token->toString());
618 obj = nullptr;
619 } else {
620 void* buffer = nullptr;
621 std::size_t nbytes = 0;
622 StatusCode sc = StatusCode::FAILURE;
623 // StopWatch listens from here until the end of this current scope
624 {
625 PMonUtils::BasicStopWatch stopWatch("gObj_ALL", this->m_chronoMap);
626 sc = m_inputStreamingTool->getObject(&buffer, nbytes);
627 while (sc.isRecoverable()) {
628 // sleep
629 sc = m_inputStreamingTool->getObject(&buffer, nbytes);
630 }
631 }
632 if (!sc.isSuccess()) {
633 ATH_MSG_ERROR("Failed to get Data for " << token->toString());
634 obj = nullptr;
635 } else {
636 RootType cltype(pool::DbReflex::forGuid(token->classID()));
637 obj = m_serializeSvc->deserialize(buffer, nbytes, cltype); buffer = nullptr;
638 buffer = nullptr;
639 nbytes = 0;
640 sc = m_inputStreamingTool->getObject(&buffer, nbytes);
641 while (sc.isRecoverable() && nbytes > 0) {
642 sc = m_inputStreamingTool->getObject(&buffer, nbytes);
643 }
644 if (sc.isSuccess() && nbytes > 0) { // Found dynamic attributes
645 Guid classId;
646 classId.fromString(static_cast<const char*>(buffer));
647 if (m_inputStreamingTool->getObject(&buffer, nbytes).isSuccess() && nbytes > 0) {
648 const std::string contName = std::string(static_cast<const char*>(buffer));
649 if (classId != Guid::null()) {
650 const pool::DbTypeInfo* info = pool::DbTypeInfo::create(classId);
651 if (info != nullptr) {
652 if (!m_auxInput->receiveStore(info->clazz().Class(), obj).isSuccess()) {
653 ATH_MSG_ERROR("Failed to get Dynamic Aux Store for " << token->toString());
654 obj = nullptr;
655 }
656 }
657 }
658 }
659 }
660 }
661 }
662 } else if (token->dbID() != Guid::null()) {
663 AthenaPoolCnvSvc::setObjPtr(obj, token);
664 }
665}
666//______________________________________________________________________________
668 const CLID& clid,
669 const std::string* par,
670 const unsigned long* ip,
671 IOpaqueAddress*& refpAddress) {
672 if (m_makeStreamingToolClient.value() > 0 && !m_inputStreamingTool.empty() && !m_inputStreamingTool->isServer() && !m_inputStreamingTool->isClient()) {
674 }
675 if (!m_inputStreamingTool.empty() && m_inputStreamingTool->isClient()) {
676 Token addressToken;
677 addressToken.setDb(par[0].substr(4));
678 addressToken.setCont(par[1]);
679 addressToken.setOid(Token::OID_t(ip[0], ip[1]));
680 ATH_CHECK(m_inputStreamingTool->lockObject(addressToken.toString().c_str()));
681 void* buffer = nullptr;
682 std::size_t nbytes = 0;
683 StatusCode sc = m_inputStreamingTool->getObject(&buffer, nbytes);
684 while (sc.isRecoverable()) {
685 // sleep
686 sc = m_inputStreamingTool->getObject(&buffer, nbytes);
687 }
688 if (!sc.isSuccess()) {
689 ATH_MSG_WARNING("Failed to get Address Token: " << addressToken.toString());
690 return(StatusCode::FAILURE);
691 }
692 auto token = std::make_unique<Token>();
693 token->fromString(static_cast<const char*>(buffer)); buffer = nullptr;
694 if (token->classID() == Guid::null()) {
695 token.reset();
696 }
697 m_inputStreamingTool->getObject(&buffer, nbytes).ignore();
698 if (token) {
699 refpAddress = new TokenAddress(pool::POOL_StorageType.type(), clid, "", par[1], IPoolSvc::kInputStream, std::move(token));
700 return(StatusCode::SUCCESS);
701 }
702 else {
703 return(StatusCode::RECOVERABLE);
704 }
705 } else {
706 if (par[0].compare(0, 3, "SHM") == 0) {
707 std::unique_ptr<Token> token;
708 token = std::make_unique<Token>();
709 token->setOid(Token::OID_t(ip[0], ip[1]));
710 token->setAuxString("[PNAME=" + par[2] + "]");
711 RootType classDesc = RootType::ByNameNoQuiet(par[2]);
712 token->setClassID(pool::DbReflex::guid(classDesc));
713 refpAddress = new TokenAddress(repSvcType(), clid, "", par[1], IPoolSvc::kInputStream, std::move(token));
714 return(StatusCode::SUCCESS);
715 } else {
716 return AthenaPoolCnvSvc::createAddress(svcType, clid, par, ip, refpAddress);
717 }
718 }
719}
720//______________________________________________________________________________
722 const CLID& clid,
723 const std::string& refAddress,
724 IOpaqueAddress*& refpAddress) {
725 return AthenaPoolCnvSvc::createAddress(svcType, clid, refAddress, refpAddress);
726}
727//______________________________________________________________________________
728StatusCode AthenaPoolSharedIOCnvSvc::cleanUp(const std::string& connection) {
729 auto pos = connection.find("?pmerge=");
730 std::string conn = (pos == std::string::npos) ? connection : connection.substr(0, pos);
731 return AthenaPoolCnvSvc::cleanUp(conn);
732}
733//______________________________________________________________________________
735 if (num < 0) {
736 num = -num;
738 num = num % 1024;
739 if (!m_outputStreamingTool.empty() && !m_outputStreamingTool->isServer()) {
740 ATH_MSG_DEBUG(std::format("makeServer: {} = {}", m_outputStreamingTool.name(), num));
741 ATH_MSG_DEBUG(std::format("makeServer: Calling shared memory tool with port suffix {}", m_streamPortString.value()));
742 const std::string streamPortSuffix = m_streamPortString.value();
743 if (m_outputStreamingTool->makeServer(num, streamPortSuffix).isFailure()) {
744 ATH_MSG_ERROR("makeServer: " << m_outputStreamingTool << " failed");
745 return(StatusCode::FAILURE);
746 }
747 // Disable PersistencySvc per output file mode, for SharedWriter Server
748 m_persSvcPerOutput.setValue(false);
749 return(StatusCode::SUCCESS);
750 }
751 return(StatusCode::RECOVERABLE);
752 }
753 if (m_inputStreamingTool.empty()) {
754 return(StatusCode::RECOVERABLE);
755 }
756 ATH_MSG_DEBUG("makeServer: " << m_inputStreamingTool << " = " << num);
757 return(m_inputStreamingTool->makeServer(num, ""));
758}
759//________________________________________________________________________________
761 if (!m_outputStreamingTool.empty()) {
762 ATH_MSG_DEBUG("makeClient: " << m_outputStreamingTool << " = " << num);
763 std::string streamPortSuffix;
764 if (m_outputStreamingTool->makeClient(num, streamPortSuffix).isFailure()) {
765 ATH_MSG_ERROR("makeClient: " << m_outputStreamingTool << " failed");
766 return(StatusCode::FAILURE);
767 } else if (m_streamPortString.value().find("localhost:0") != std::string::npos) {
768 // We don't seem to use a dedicated port per stream so doing this for the first client is probably OK
769 ATH_MSG_DEBUG("makeClient: Setting conversion service port suffix to " << streamPortSuffix);
770 m_streamPortString.setValue(streamPortSuffix);
771 }
772 }
773 if (m_inputStreamingTool.empty()) {
774 return(StatusCode::SUCCESS);
775 }
776 ATH_MSG_DEBUG("makeClient: " << m_inputStreamingTool << " = " << num);
777 std::string dummyStr;
778 return(m_inputStreamingTool->makeClient(num, dummyStr));
779}
780//________________________________________________________________________________
782 if (m_inputStreamingTool.empty()) {
783 return(StatusCode::FAILURE);
784 }
785 const char* tokenStr = nullptr;
786 int num = -1;
787 StatusCode sc = m_inputStreamingTool->clearObject(&tokenStr, num);
788 if (sc.isSuccess() && tokenStr != nullptr && strlen(tokenStr) > 0 && num > 0) {
789 ATH_MSG_DEBUG("readData: " << tokenStr << ", for client: " << num);
790 } else {
791 return(sc);
792 }
793 // Read object instance via POOL/ROOT
794 void* instance = nullptr;
795 Token token;
796 token.fromString(tokenStr); tokenStr = nullptr;
797 if (token.classID() != Guid::null()) {
798 std::string objName = "ALL";
799 if (useDetailChronoStat()) {
800 objName = token.classID().toString();
801 }
802 // StopWatch listens from here until the end of this current scope
803 PMonUtils::BasicStopWatch stopWatch("cObj_" + objName, this->m_chronoMap);
804 this->setObjPtr(instance, &token);
805 // Serialize object via ROOT
807 void* buffer = nullptr;
808 std::size_t nbytes = 0;
809 buffer = m_serializeSvc->serialize(instance, cltype, nbytes);
810 sc = m_inputStreamingTool->putObject(buffer, nbytes, num);
811 while (sc.isRecoverable()) {
812 sc = m_inputStreamingTool->putObject(buffer, nbytes, num);
813 }
814 delete [] static_cast<char*>(buffer); buffer = nullptr;
815 if (!sc.isSuccess()) {
816 ATH_MSG_ERROR("Could not share object for: " << token.toString());
817 return(StatusCode::FAILURE);
818 }
820 if (info != nullptr) {
821 if (m_auxDynTool->hasAuxStore(token.contID(), info->clazz().Class() ) && !m_auxInput->sendStore(info->clazz().Class(), instance, token.classID().toString(), token.contID(), num).isSuccess()) {
822 ATH_MSG_ERROR("Could not share dynamic aux store for: " << token.toString());
823 return(StatusCode::FAILURE);
824 }
825 }
826 cltype.Destruct(instance); instance = nullptr;
827 if (!m_inputStreamingTool->putObject(nullptr, 0, num).isSuccess()) {
828 ATH_MSG_ERROR("Could not share object for: " << token.toString());
829 return(StatusCode::FAILURE);
830 }
831 } else if (token.dbID() != Guid::null()) {
832 std::string returnToken;
833 Token* metadataToken = getPoolSvc()->getToken("FID:" + token.dbID().toString(), token.contID(), token.oid().first);
834 if( metadataToken ) {
835 returnToken = metadataToken->toString();
836 metadataToken->release(); metadataToken = nullptr;
837 } else {
838 returnToken = token.toString();
839 }
840 // Share token
841 sc = m_inputStreamingTool->putObject(returnToken.c_str(), returnToken.size() + 1, num);
842 if (!sc.isSuccess() || !m_inputStreamingTool->putObject(nullptr, 0, num).isSuccess()) {
843 ATH_MSG_ERROR("Could not share token for: " << token.toString());
844 return(StatusCode::FAILURE);
845 }
846 } else {
847 return(StatusCode::RECOVERABLE);
848 }
849 return(StatusCode::SUCCESS);
850}
851
852//________________________________________________________________________________
854 getPoolSvc()->commitCatalog();
855 getPoolSvc()->startCatalog();
856 return(StatusCode::SUCCESS);
857}
858
859//______________________________________________________________________________
861{
862 ATH_MSG_ERROR("Sending ABORT to clients");
863 // the master process will kill this process once workers abort
864 // but it could be a time-limited loop
865 StatusCode sc = StatusCode::SUCCESS;
866 while (sc.isSuccess()) {
867 if (client_n >= 0) {
868 sc = m_outputStreamingTool->lockObject("ABORT", client_n);
869 }
870 const char* dummy;
871 sc = m_outputStreamingTool->clearObject(&dummy, client_n);
872 while (sc.isRecoverable()) {
873 sc = m_outputStreamingTool->clearObject(&dummy, client_n);
874 }
875 }
876 return StatusCode::FAILURE;
877}
878
879//______________________________________________________________________________
880void AthenaPoolSharedIOCnvSvc::handle(const Incident& incident) {
881 if (incident.type() == "StoreCleared" && m_outputStreamingTool->isClient() && !m_parallelCompression) {
882 m_outputStreamingTool->lockObject("release").ignore();
883 }
884}
885//______________________________________________________________________________
886AthenaPoolSharedIOCnvSvc::AthenaPoolSharedIOCnvSvc(const std::string& name, ISvcLocator* pSvcLocator) :
887 base_class(name, pSvcLocator) {
888}
889//______________________________________________________________________________
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
This file contains the class definition for the AthenaPoolSharedIOCnvSvc class.
This file contains the class definition for the DataHeader and DataHeaderElement classes.
uint32_t CLID
The Class ID type.
This file contains the class definition for the IAthMetaDataSvc class.
Interface to an output stream tool.
RAII guard that guarantees a matching end-incident for every begin-incident.
static Double_t sc
This file contains the class definition for the Placement class (migrated from POOL).
TTypeAdapter RootType
Definition RootType.h:211
std::map< std::string, double > instance
This file contains the class definition for the TokenAddress class.
This file contains the class definition for the Token class (migrated from POOL).
std::map< std::string, int > m_fileCommitCounter
Force SharedWriter to flush data to output file at given intervals, needed by parallel compression.
virtual StatusCode commitOutput(const std::string &outputConnectionSpec, bool doCommit) override
Implementation of IConversionSvc: Commit pending output.
Gaudi::Property< std::string > m_streamPortString
Extension to use ROOT TMemFile for event data, "?pmerge=<host>:<port>".
virtual StatusCode finalize() override
Required of all Gaudi Services.
AthenaPoolSharedIOCnvSvc(const std::string &name, ISvcLocator *pSvcLocator)
Standard Service Constructor.
virtual StatusCode cleanUp(const std::string &connection) override
Implement cleanUp to call all registered IAthenaPoolCleanUp cleanUp() function.
virtual void setObjPtr(void *&obj, const Token *token) override
StatusCode createAddress(long svcType, const CLID &clid, const std::string *par, const unsigned long *ip, IOpaqueAddress *&refpAddress) override
Create a Generic address using explicit arguments to identify a single object.
std::unique_ptr< RootAuxDynIO::IAuxDynShare > m_auxOutput
StatusCode abortSharedWrClients(int client_n)
Send abort to SharedWriter clients if the server quits on error.
virtual StatusCode makeServer(int num) override
Make this a server.
virtual void handle(const Incident &incident) override
Implementation of IIncidentListener: Handle for EndEvent incidence.
Gaudi::Property< std::string > m_metadataContainerProp
For SharedWriter: To use MetadataSvc to merge data placed in a certain container.
virtual StatusCode disconnectOutput(const std::string &outputConnectionSpec) override
Disconnect to the output connection.
virtual StatusCode readData() override
Read the next data object.
virtual StatusCode commitCatalog() override
Commit Catalog.
virtual StatusCode connectOutput(const std::string &outputConnectionSpec, const std::string &openMode) override
Implementation of IConversionSvc: Connect to the output connection specification with open mode.
std::unique_ptr< RootAuxDynIO::IAuxDynShare > m_auxInput
virtual ~AthenaPoolSharedIOCnvSvc()
Destructor.
Gaudi::Property< std::vector< std::string > > m_metadataContainersAug
Gaudi::Property< bool > m_parallelCompression
Use Athena Object sharing for metadata only, event data is collected and send via ROOT TMemFile.
ToolHandle< IAthenaIPCTool > m_outputStreamingTool
Gaudi::Property< std::map< std::string, int > > m_fileFlushSetting
Gaudi::Property< int > m_streamingTechnology
Use Streaming for selected technologies only.
virtual StatusCode initialize() override
Required of all Gaudi Services.
ToolHandle< IAthenaIPCTool > m_inputStreamingTool
ServiceHandle< IAthenaSerializeSvc > m_serializeSvc
virtual Token * registerForWrite(Placement *placement, const void *obj, const RootType &classDesc) override
Gaudi::Property< int > m_makeStreamingToolClient
Make this instance a Streaming Client during first connect/write automatically.
virtual StatusCode makeClient(int num) override
Make this a client.
std::unique_ptr< RootAuxDynIO::IFactoryTool > m_auxDynTool
This class provides a encapsulation of a GUID/UUID/CLSID/IID data structure (128 bit number).
Definition Guid.h:25
constexpr void fromString(std::string_view s)
Automatic conversion from string representation.
Definition Guid.h:143
static const Guid & null() noexcept
NULL-Guid: static class method.
Definition Guid.cxx:14
constexpr void toString(std::span< char, StrLen > buf, bool uppercase=true) const noexcept
Automatic conversion to string representation.
@ kInputStream
Definition IPoolSvc.h:39
static InputFileIncidentGuard begin(IIncidentSvc &incSvc, std::string_view source, std::string_view beginFileName, std::string_view guid, std::string_view endFileName={}, std::string_view beginType=IncidentType::BeginInputFile, std::string_view endType=IncidentType::EndInputFile)
Factory: fire the begin incident and return a guard whose destructor fires the matching end incident.
This class holds all the necessary information to guide the writing of an object in a physical place.
Definition Placement.h:20
const std::string & auxString() const
Access auxiliary string.
Definition Placement.h:41
const std::string & containerName() const
Access container name.
Definition Placement.h:33
Placement & setFileName(const std::string &fileName)
Set file name.
Definition Placement.h:31
const std::string toString() const
Retrieve the string representation of the placement.
Definition Placement.cxx:15
Placement & setTechnology(int technology)
Set technology type.
Definition Placement.h:39
const std::string & fileName() const
Access file name.
Definition Placement.h:29
Placement & fromString(const std::string &from)
Build from the string representation of a placement.
Definition Placement.cxx:28
int technology() const
Access technology type.
Definition Placement.h:37
static TScopeAdapter ByNameNoQuiet(const std::string &name, Bool_t load=kTRUE)
Definition RootType.cxx:586
Bool_t IsFundamental() const
Definition RootType.cxx:731
std::string Name(unsigned int mod=Reflex::SCOPED) const
Definition RootType.cxx:612
void Destruct(void *place) const
Definition RootType.cxx:677
size_t SizeOf() const
Definition RootType.cxx:765
This class provides a Generic Transient Address for POOL tokens.
This class provides a token that identifies in a unique way objects on the persistent storage.
Definition Token.h:22
const std::string & auxString() const
Access auxiliary string.
Definition Token.h:92
Token & setCont(const std::string &cnt)
Set container name.
Definition Token.h:72
Token & setDb(const Guid &db)
Set database name.
Definition Token.h:67
const std::string & contID() const
Access container identifier.
Definition Token.h:70
const Guid & classID() const
Access database identifier.
Definition Token.h:74
Token & setClassID(const Guid &cl_id)
Access database identifier.
Definition Token.h:76
const std::string toString() const
Retrieve the string representation of the token.
Definition Token.cxx:135
int technology() const
Access technology type.
Definition Token.h:78
int release()
Release token: Decrease reference count and eventually delete.
Definition Token.cxx:81
Token & setOid(const OID_t &oid)
Set object identifier.
Definition Token.h:86
const OID_t & oid() const
Access object identifier.
Definition Token.h:82
Token & fromString(const std::string_view from)
Build from the string representation of a token.
Definition Token.cxx:169
const Guid & dbID() const
Access database identifier.
Definition Token.h:65
Token & setAuxString(std::string &&auxString)
Set auxiliary string.
Definition Token.h:94
static const TypeH forGuid(const Guid &info)
Access classes by Guid.
static Guid guid(const TypeH &id)
Determine Guid (normalized string form) from reflection type.
Definition of class DbTypeInfo.
Definition DbTypeInfo.h:49
static const DbTypeInfo * create(const std::string &cl_name)
Create type information using name.
static DbType getType(const std::string &name)
Access known storage type object by name.
Definition merge.py:1
static const DbType POOL_StorageType
Definition DbType.h:84