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