ATLAS Offline Software
Loading...
Searching...
No Matches
SharedWriterTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include "SharedWriterTool.h"
7
11#include "CxxUtils/xmalloc.h"
12#include "GaudiKernel/IEvtSelector.h"
13#include "GaudiKernel/IConversionSvc.h"
14#include "GaudiKernel/IIoComponentMgr.h"
15
17#include <filesystem>
18
20 , const std::string& name
21 , const IInterface* parent)
22 : AthenaMPToolBase(type,name,parent)
23 , m_rankId(0)
24 , m_sharedRankQueue(nullptr)
25{
26 m_subprocDirPrefix = "shared_writer";
27}
28
30
32{
33 ATH_MSG_DEBUG("In initialize");
34
36 m_cnvSvc = serviceLocator()->service("AthenaPoolSharedIOCnvSvc");
37 ATH_CHECK(m_cnvSvc.isValid());
38
39 return StatusCode::SUCCESS;
40}
41
43{
44 ATH_MSG_DEBUG("In finalize");
45
46 return StatusCode::SUCCESS;
47}
48
49int SharedWriterTool::makePool(int /*maxevt*/, int nprocs, const std::string& topdir)
50{
51 ATH_MSG_DEBUG("In makePool " << getpid());
52
53 if(topdir.empty()) {
54 ATH_MSG_ERROR("Empty name for the top directory!");
55 return -1;
56 }
57
58 m_nprocs = (nprocs == -1 ? sysconf(_SC_NPROCESSORS_ONLN) : nprocs) + 1;
59 m_subprocTopDir = topdir;
60
61 // Create rank queue and fill it
62 m_sharedRankQueue = std::make_unique<AthenaInterprocess::SharedQueue>(
63 "SharedWriterTool_RankQueue_" + m_randStr, 1, sizeof(int));
64 if(!m_sharedRankQueue->send_basic<int>(0)) {
65 ATH_MSG_ERROR("Unable to send int to the ranks queue!");
66 return -1;
67 }
68
69 // Create the process group and map_async bootstrap
70 m_processGroup = new AthenaInterprocess::ProcessGroup(1);
71 ATH_MSG_INFO("Created shared writer process");
72 if(mapAsyncFlag(AthenaMPToolBase::FUNC_BOOTSTRAP))
73 return -1;
74 ATH_MSG_INFO("Shared writer process bootstrapped");
75 return 1;
76}
77
78StatusCode SharedWriterTool::exec()
79{
80 ATH_MSG_DEBUG("In exec " << getpid());
81
82 if(mapAsyncFlag(AthenaMPToolBase::FUNC_EXEC))
83 return StatusCode::FAILURE;
84 ATH_MSG_INFO("Shared writer started write events");
85
86 // Set exit flag on writer
87 if(m_processGroup->map_async(0,0)){
88 ATH_MSG_ERROR("Unable to set exit to the writer");
89 return StatusCode::FAILURE;
90 }
91 return StatusCode::SUCCESS;
92}
93
94void SharedWriterTool::subProcessLogs(std::vector<std::string>& filenames)
95{
96 filenames.clear();
97 std::filesystem::path writer_rundir(m_subprocTopDir);
98 writer_rundir /= m_subprocDirPrefix;
99 filenames.push_back((writer_rundir / "AthenaMP.log").string());
100}
101
107
108std::unique_ptr<AthenaInterprocess::ScheduledWork> SharedWriterTool::bootstrap_func()
109{
110 // It's possible to debug SharedWriter just like any other AthenaMP worker.
111 // The following procedure provides a minimal explanation on how this can be achieved:
112 //
113 // Terminal #1:
114 // * Run athena w/ debugging enabled, e.g. athena.py --debugWorker --stdcmalloc --nprocs=8 [...]
115 // * In this mode, workers will be stopped after fork(), waiting for SIGUSR1 to be resumed
116 // * Find the PID of the worker to be debugged (printed by the job in stdout)
117 //
118 // Terminal #2:
119 // * Attach gdb to the relevant worker, i.e. gdb python PID
120 // * Once the symbols are loaded, one can perform any gdb action such as setting breakpoints etc.
121 // * Once ready, send SIGUSR1 to the worker to resume work, i.e. signal SIGUSR1 (in gdb)
122 //
123 // Terminal #3:
124 // * Send SIGUSR1 to the remaining workers (easiest to use htop)
125 //
126 // However, note that sometimes Shared I/O infrastructure struggles with timing problems,
127 // such as server/client(s) starting/stopping too early/later. Debugging can change this
128 // behavior so please keep this in mind.
130
131 auto outwork = std::make_unique<AthenaInterprocess::ScheduledWork>();
132 outwork->data = CxxUtils::xmalloc(sizeof(int));
133 *static_cast<int*>(outwork->data) = 1; // Error code: for now use 0 success, 1 failure
134 outwork->size = sizeof(int);
135
136 // ...
137 // (possible) TODO: extend outwork with some error message, which will be eventually
138 // reported in the master proces
139 // ...
140
141 // ________________________ Get RankID ________________________
142 //
143 if(!m_sharedRankQueue->receive_basic<int>(m_rankId)) {
144 ATH_MSG_ERROR("Unable to get rank ID!");
145 return outwork;
146 }
147 // Writer dir: mkdir
148 std::filesystem::path writer_rundir(m_subprocTopDir);
149 writer_rundir /= std::filesystem::path(m_subprocDirPrefix);
150
151 if(mkdir(writer_rundir.string().c_str(),S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)==-1) {
152 ATH_MSG_ERROR("Unable to make writer run directory: " << writer_rundir.string() << ". " << fmterror(errno));
153 return outwork;
154 }
155
156 // __________ Redirect logs unless we want to attach debugger ____________
157 if(!m_debug) {
158 if(redirectLog(writer_rundir.string()))
159 return outwork;
160
161 ATH_MSG_INFO("Logs redirected in the AthenaMP Shared Writer PID=" << getpid());
162 }
163
164 // Update Io Registry
165 if(updateIoReg(writer_rundir.string()))
166 return outwork;
167
168 ATH_MSG_INFO("Io registry updated in the AthenaMP Shared Writer PID=" << getpid());
169
170 // _______________________ Handle saved PFC (if any) ______________________
171 std::filesystem::path abs_writer_rundir = std::filesystem::absolute(writer_rundir);
172 if(handleSavedPfc(abs_writer_rundir))
173 return outwork;
174
175 // Reopen file descriptors
176 if(reopenFds())
177 return outwork;
178
179 ATH_MSG_INFO("File descriptors re-opened in the AthenaMP Shared Writer PID=" << getpid());
180
181 // Try to initialize AthenaRootSharedWriterSvc early on
182 SmartIF<IAthenaSharedWriterSvc> sharedWriterSvc(serviceLocator()->service("AthenaRootSharedWriterSvc"));
183 if(!sharedWriterSvc) {
184 ATH_MSG_WARNING("Error retrieving AthenaRootSharedWriterSvc from SharedWriterTool::bootstrap_func()");
185 }
186
187 // Use IDataShare to make ConversionSvc a Share Server
188 SmartIF<IDataShare> cnvSvc(m_cnvSvc);
189 if (!cnvSvc || !cnvSvc->makeServer(-m_nprocs - 1 - 1024 * m_rankId).isSuccess()) {
190 ATH_MSG_ERROR("Failed to make the conversion service a share server");
191 return outwork;
192 }
193 else {
194 ATH_MSG_DEBUG("Successfully made the conversion service a share server");
195 }
196
197 // ________________________ I/O reinit ________________________
198 if(!m_ioMgr->io_reinitialize().isSuccess()) {
199 ATH_MSG_ERROR("Failed to reinitialize I/O");
200 return outwork;
201 } else {
202 ATH_MSG_DEBUG("Successfully reinitialized I/O");
203 }
204
205 // Writer dir: chdir
206 if(chdir(writer_rundir.string().c_str())==-1) {
207 ATH_MSG_ERROR("Failed to chdir to " << writer_rundir.string());
208 return outwork;
209 }
210
211 // Declare success and return
212 *static_cast<int*>(outwork->data) = 0;
213 return outwork;
214}
215
216std::unique_ptr<AthenaInterprocess::ScheduledWork> SharedWriterTool::exec_func()
217{
218 ATH_MSG_INFO("Exec function in the AthenaMP Shared Writer PID=" << getpid());
219 bool all_ok = true;
220
221 SmartIF<IAthenaSharedWriterSvc> sharedWriterSvc(serviceLocator()->service("AthenaRootSharedWriterSvc"));
222 if(!sharedWriterSvc) {
223 ATH_MSG_ERROR("Error retrieving AthenaRootSharedWriterSvc");
224 all_ok=false;
225 }
226 else if(!sharedWriterSvc->share(m_nprocs, m_nMotherProcess.value()).isSuccess()) {
227 ATH_MSG_ERROR("Exec function could not share data");
228 all_ok=false;
229 }
230 AthCnvSvc* cnvSvc = dynamic_cast<AthCnvSvc*>(m_cnvSvc.get());
231 if (cnvSvc == nullptr || !cnvSvc->disconnectOutput("").isSuccess()) {
232 ATH_MSG_ERROR("Exec function could not disconnectOutput");
233 all_ok=false;
234 }
235
236 if(m_appMgr->stop().isFailure()) {
237 ATH_MSG_ERROR("Unable to stop AppMgr");
238 all_ok=false;
239 }
240 else {
241 if(m_appMgr->finalize().isFailure()) {
242 std::cerr << "Unable to finalize AppMgr" << std::endl;
243 all_ok=false;
244 }
245 }
246
247 auto outwork = std::make_unique<AthenaInterprocess::ScheduledWork>();
248 outwork->data = CxxUtils::xmalloc(sizeof(int));
249 *static_cast<int*>(outwork->data) = (all_ok ? 0 : 1); // Error code: for now use 0 success, 1 failure
250 outwork->size = sizeof(int);
251
252 // ...
253 // (possible) TODO: extend outwork with some error message, which will be eventually
254 // reported in the master proces
255 // ...
256 return outwork;
257}
258
259std::unique_ptr<AthenaInterprocess::ScheduledWork> SharedWriterTool::fin_func()
260{
261 // Dummy
262 auto outwork = std::make_unique<AthenaInterprocess::ScheduledWork>();
263 outwork->data = CxxUtils::xmalloc(sizeof(int));
264 *static_cast<int*>(outwork->data) = 0; // Error code: for now use 0 success, 1 failure
265 outwork->size = sizeof(int);
266 return outwork;
267}
#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_INFO(x,...)
Base class for all conversion services.
Definition AthCnvSvc.h:66
virtual StatusCode disconnectOutput(const std::string &output)
Disconnect output files from the service.
std::string m_subprocTopDir
Top run directory for subprocesses.
int handleSavedPfc(const std::filesystem::path &dest_path)
int updateIoReg(const std::string &rundir)
AthenaMPToolBase(const std::string &type, const std::string &name, const IInterface *parent)
virtual StatusCode initialize() override
int redirectLog(const std::string &rundir, bool addTimeStamp=true)
ServiceHandle< IAppMgrUI > m_appMgr
int m_nprocs
Number of workers spawned by the master process.
ServiceHandle< IIoComponentMgr > m_ioMgr
AthenaInterprocess::ProcessGroup * m_processGroup
std::string m_subprocDirPrefix
For ex. "worker__".
std::string fmterror(int errnum)
Gaudi::Property< bool > m_nMotherProcess
virtual std::unique_ptr< AthenaInterprocess::ScheduledWork > exec_func() override
SmartIF< IConversionSvc > m_cnvSvc
Gaudi::Property< bool > m_debug
virtual std::unique_ptr< AthenaInterprocess::ScheduledWork > fin_func() override
virtual std::unique_ptr< AthenaInterprocess::ScheduledWork > bootstrap_func() override
virtual AthenaMP::AllWorkerOutputs_ptr generateOutputReport() override
std::unique_ptr< AthenaInterprocess::SharedQueue > m_sharedRankQueue
virtual void subProcessLogs(std::vector< std::string > &) override
virtual ~SharedWriterTool() override
virtual StatusCode finalize() override
virtual StatusCode initialize() override
std::map< std::string, SingleWorkerOutputs > AllWorkerOutputs
std::unique_ptr< AllWorkerOutputs > AllWorkerOutputs_ptr
void * xmalloc(size_t size)
Trapping version of malloc.
Definition xmalloc.cxx:31
::StatusCode StatusCode
StatusCode definition for legacy code.
Trapping version of malloc.