ATLAS Offline Software
Loading...
Searching...
No Matches
TracccTritonInitializer.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
11
12#include "Python.h"
13
15
17
18#include <dlfcn.h>
19
20#include <atomic>
21#include <cstddef>
22#include <mutex>
23#include <stdexcept>
24#include <string>
25
26// Embedded Athena application.
27#include "GaudiKernel/Bootstrap.h"
28#include "GaudiKernel/IAppMgrUI.h"
29#include "GaudiKernel/IAlgManager.h"
30#include "GaudiKernel/IAlgorithm.h"
31#include "GaudiKernel/ISvcLocator.h"
32#include "GaudiKernel/SmartIF.h"
33#include "GaudiKernel/StateMachine.h"
34
35
36namespace triton { namespace backend { namespace traccc {
37
38// TracccTritonBootstrap.bootstrap() brings up the embedded Athena application
39// with the device chain configured by TracccTritonDeviceRecoCfg, scheduled
40// inside an AthSequencer
41namespace {
42 void promoteSelfToGlobal() {
43 Dl_info info{};
44 if (!dladdr(reinterpret_cast<void*>(&promoteSelfToGlobal), &info) ||
45 !info.dli_fname) {
46 throw std::runtime_error(
47 "TracccTritonInitializer: dladdr() could not determine this "
48 "library's own path");
49 }
50 if (!dlopen(info.dli_fname, RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD)) {
51 throw std::runtime_error(
52 std::string("TracccTritonInitializer: failed to promote '") +
53 info.dli_fname + "' to RTLD_GLOBAL: " + dlerror());
54 }
55 }
56} // namespace
57
60 SmartIF<IAppMgrUI> app;
61 SmartIF<ISvcLocator> svcLocator;
62 bool ready = false;
64 std::atomic<std::size_t> nextSlot{0};
65 PyThreadState* pyMainThreadState = nullptr;
66
68 SmartIF<IAlgorithm> algorithm(const std::string& name) const {
69 IAlgManager* algMgr = svcLocator.as<IAlgManager>();
70 if (!algMgr) {
71 throw std::runtime_error(
72 "TracccTritonInitializer: no IAlgManager available");
73 }
74 SmartIF<IAlgorithm> alg = algMgr->algorithm(name, /*createIf*/ false);
75 if (!alg) {
76 throw std::runtime_error(
77 "TracccTritonInitializer: could not retrieve algorithm '" +
78 name + "' from the embedded Athena application");
79 }
80 return alg;
81 }
82
85 static std::string keyFromDict(PyObject* dict, const char* name) {
86 PyObject* item = PyDict_GetItemString(dict, name); // borrowed
87 if (!item || !PyUnicode_Check(item)) {
88 throw std::runtime_error(
89 std::string("TracccTritonInitializer: TracccTritonBootstrap."
90 "bootstrap() returned no string entry '") + name +
91 "' in its StoreGate key dictionary");
92 }
93 const char* value = PyUnicode_AsUTF8(item);
94 if (!value || !*value) {
95 throw std::runtime_error(
96 std::string("TracccTritonInitializer: empty StoreGate key '") +
97 name + "' returned by TracccTritonBootstrap.bootstrap()");
98 }
99 return value;
100 }
101
105 const std::size_t nSlots = config.nSlots;
106 const std::string& sequenceName = config.sequenceName;
107 if (!Py_IsInitialized()) {
108 if (!dlopen(TRACCC_TRITON_PYTHON_LIBRARY, RTLD_NOW | RTLD_GLOBAL)) {
109 throw std::runtime_error(
110 std::string(
111 "TracccTritonInitializer: failed to re-load '"
112 TRACCC_TRITON_PYTHON_LIBRARY "' with RTLD_GLOBAL: ") +
113 dlerror());
114 }
115
116 Py_Initialize();
117 if (!Py_IsInitialized()) {
118 throw std::runtime_error(
119 "TracccTritonInitializer: Py_Initialize() failed");
120 }
121 }
122
123 PyObject* module =
124 PyImport_ImportModule("TracccTritonBackend.TracccTritonBootstrap");
125 if (!module) {
126 PyErr_Print();
127 throw std::runtime_error(
128 "TracccTritonInitializer: failed to import "
129 "TracccTritonBackend.TracccTritonBootstrap -- check "
130 "PYTHONPATH");
131 }
132
133 PyObject* func = PyObject_GetAttrString(module, "bootstrap");
134 Py_DECREF(module);
135 if (!func || !PyCallable_Check(func)) {
136 Py_XDECREF(func);
137 PyErr_Print();
138 throw std::runtime_error(
139 "TracccTritonInitializer: TracccTritonBootstrap.bootstrap "
140 "not found or not callable");
141 }
142
143 PyObject* result =
144 PyObject_CallFunction(func, "ns", static_cast<Py_ssize_t>(nSlots),
145 sequenceName.c_str());
146 Py_DECREF(func);
147 if (!result) {
148 PyErr_Print();
149 throw std::runtime_error(
150 "TracccTritonInitializer: TracccTritonBootstrap.bootstrap() "
151 "raised a Python exception (see stderr above)");
152 }
153 if (!PyDict_Check(result)) {
154 Py_DECREF(result);
155 throw std::runtime_error(
156 "TracccTritonInitializer: TracccTritonBootstrap.bootstrap() "
157 "did not return a dictionary of StoreGate keys");
158 }
159 try {
160 config.cellsKey = keyFromDict(result, "cells");
161 config.measurementsKey = keyFromDict(result, "measurements");
162 config.clustersKey = keyFromDict(result, "clusters");
163 config.tracksKey = keyFromDict(result, "tracks");
164 } catch (...) {
165 Py_DECREF(result);
166 throw;
167 }
168 Py_DECREF(result);
169
170 pyMainThreadState = PyEval_SaveThread();
171 }
172};
173
176
178 try {
179 finalize();
180 } catch (...) {
181 }
182}
183
185 // The singleton is mutable, but every method that touches its state
186 // serialises on its own mutex, so sharing it between Triton's worker
187 // threads is safe.
189 return theInstance;
190}
191
193 static std::mutex initMutex;
194 std::lock_guard<std::mutex> lock(initMutex);
195
196 if (m_impl->ready) {
197 // Gaudi is a process singleton; a second initialize() with a
198 // different device id cannot be honored.
199 if (config.deviceId != m_impl->config.deviceId) {
200 throw std::runtime_error(
201 "TracccTritonInitializer: already initialized on device " +
202 std::to_string(m_impl->config.deviceId) +
203 ", cannot re-initialize on device " +
204 std::to_string(config.deviceId) +
205 ". Pin the model to a single GPU in instance_group.");
206 }
207 if (config.nSlots != m_impl->config.nSlots) {
208 throw std::runtime_error(
209 "TracccTritonInitializer: already initialized with " +
210 std::to_string(m_impl->config.nSlots) +
211 " event slots, cannot re-initialize with " +
212 std::to_string(config.nSlots));
213 }
214 return;
215 }
216
217 m_impl->config = config;
218
219 promoteSelfToGlobal();
220
221 // Fills in the StoreGate keys on m_impl->config from the python configuration
222 m_impl->bootstrapPython();
223
224 // Fetch the ApplicationMgr
225 m_impl->app = Gaudi::createApplicationMgr();
226 if (!m_impl->app) {
227 throw std::runtime_error(
228 "TracccTritonInitializer: Gaudi::createApplicationMgr() "
229 "returned null after the Python bootstrap");
230 }
231 // bootstrap() takes the application all the way to RUNNING
232 if (m_impl->app->FSMState() != Gaudi::StateMachine::RUNNING) {
233 throw std::runtime_error(
234 "TracccTritonInitializer: application manager is not "
235 "RUNNING after the Python bootstrap (FSM state " +
236 std::to_string(static_cast<int>(m_impl->app->FSMState())) + ")");
237 }
238
239 m_impl->svcLocator = SmartIF<ISvcLocator>(m_impl->app);
240 if (!m_impl->svcLocator) {
241 throw std::runtime_error(
242 "TracccTritonInitializer: could not obtain ISvcLocator");
243 }
244
245 // Sanity-check that the device chain the Runner will execute exists.
246 m_impl->algorithm(m_impl->config.sequenceName);
247
248 m_impl->ready = true;
249}
250
252 if (!m_impl->ready) return;
253
254 if (m_impl->app) {
255 // finalize() expects the INITIALIZED state, so the RUNNING state the
256 // bootstrap left the application in has to be unwound first.
257 m_impl->app->stop().ignore();
258 m_impl->app->finalize().ignore();
259 m_impl->app->terminate().ignore();
260 }
261
262 m_impl->svcLocator = nullptr;
263 m_impl->app = nullptr;
264 m_impl->ready = false;
265}
266
268 return m_impl->ready;
269}
270
273 return m_impl->config;
274}
275
276std::size_t
278 if (!m_impl->ready) {
279 throw std::runtime_error(
280 "TracccTritonInitializer::acquireSlot: not initialized");
281 }
282 const std::size_t slot = m_impl->nextSlot.fetch_add(1);
283 if (slot >= m_impl->config.nSlots) {
284 throw std::runtime_error(
285 "TracccTritonInitializer: Triton created more model instances "
286 "than the " + std::to_string(m_impl->config.nSlots) +
287 " event slot(s) the application was configured with");
288 }
289 return slot;
290}
291
293 return m_impl->config.deviceId;
294}
295
297 if (!m_impl->svcLocator) {
298 throw std::runtime_error(
299 "TracccTritonInitializer::serviceLocator: not initialized");
300 }
301 return *m_impl->svcLocator;
302}
303
304}}} // namespace triton::backend::traccc
virtual void lock()=0
Interface to allow an object to lock itself when made const in SG.
_object PyObject
Initialization of GPU tracking algs. for use in Triton as-a-Service implementation.
Define macros for attributes used to control the static checker.
#define ATLAS_THREAD_SAFE
static TracccTritonInitializer & instance()
Get the process-wide singleton.
std::size_t acquireSlot()
Claim the next free event-store slot for a Triton model instance.
bool isReady() const
Whether initialize has completed successfully.
ISvcLocator & serviceLocator() const
The service locator of the embedded Athena application (never null once ready).
TracccTritonInitializer(const TracccTritonInitializer &)=delete
void initialize(const Config &config)
Boot the embedded Athena application and resolve all cached handles.
const Config & config() const
The construction parameters the application was configured with.
void finalize()
Tear the application down. Safe to call when not initialized.
int deviceId() const
The CUDA device id this instance is pinned to.
STL namespace.
Construction parameters, normally read from the model config, plus the StoreGate keys initialize() fi...
static std::string keyFromDict(PyObject *dict, const char *name)
Read one string entry out of the dictionary bootstrap() returned.
SmartIF< IAlgorithm > algorithm(const std::string &name) const
Look up an algorithm (or sequence) by the name it was created under.
void bootstrapPython()
Embed a Python interpreter and call TracccTritonBackend.TracccTritonBootstrap.bootstrap().
std::atomic< std::size_t > nextSlot
Next event-store slot to hand out; see acquireSlot().