• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

llnl / dftracer-utils / 28693295402

04 Jul 2026 03:17AM UTC coverage: 52.408% (+0.1%) from 52.278%
28693295402

push

github

hariharan-devarajan
feat: silence noisy warnings on aarch64

37318 of 92666 branches covered (40.27%)

Branch coverage included in aggregate %.

33462 of 42389 relevant lines covered (78.94%)

20557.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

49.8
/src/dftracer/utils/python/runtime.cpp
1
#define PY_SSIZE_T_CLEAN
2
#include <Python.h>
3
#include <dftracer/utils/python/py_dict_helpers.h>
4
#include <dftracer/utils/python/py_errors.h>
5
#include <dftracer/utils/python/py_runtime_mixin.h>
6
#include <dftracer/utils/python/py_type_helpers.h>
7
#include <dftracer/utils/python/runtime.h>
8

9
#include <chrono>
10
#include <memory>
11

12
static std::shared_ptr<dftracer::utils::Runtime> g_default_runtime;
13

14
dftracer::utils::Runtime *get_default_runtime() {
684✔
15
    if (!g_default_runtime) {
684✔
16
        g_default_runtime = std::make_shared<dftracer::utils::Runtime>(0);
2!
17
    }
1✔
18
    return g_default_runtime.get();
684✔
19
}
20

21
static void Runtime_dealloc(RuntimeObject *self) {
158✔
22
    self->runtime.reset();
158✔
23
    Py_TYPE(self)->tp_free((PyObject *)self);
158✔
24
}
158✔
25

26
static PyObject *Runtime_new(PyTypeObject *type, PyObject *args,
156✔
27
                             PyObject *kwds) {
28
    RuntimeObject *self = (RuntimeObject *)type->tp_alloc(type, 0);
156✔
29
    if (self) {
156✔
30
        // Placement-new the shared_ptr (tp_alloc gives raw memory)
31
        new (&self->runtime) std::shared_ptr<dftracer::utils::Runtime>(nullptr);
156✔
32
    }
78✔
33
    return (PyObject *)self;
156✔
34
}
35

36
static int Runtime_init(RuntimeObject *self, PyObject *args, PyObject *kwds) {
156✔
37
    static const char *kwlist[] = {"threads", "io_threads", NULL};
38
    Py_ssize_t threads = 0;
156✔
39
    Py_ssize_t io_threads = 0;
156✔
40

41
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|nn", (char **)kwlist,
156!
42
                                     &threads, &io_threads)) {
43
        return -1;
×
44
    }
45

46
    if (threads < 0) {
156✔
47
        PyErr_SetString(PyExc_ValueError, "threads must be >= 0");
×
48
        return -1;
×
49
    }
50
    if (io_threads < 0) {
156✔
51
        PyErr_SetString(PyExc_ValueError, "io_threads must be >= 0");
×
52
        return -1;
×
53
    }
54

55
    try {
56
        dftracer::utils::ExecutorConfig config;
156!
57
        config.num_threads = static_cast<std::size_t>(threads);
156✔
58
        config.io_pool_size = static_cast<std::size_t>(io_threads);
156✔
59
        self->runtime =
78✔
60
            std::make_shared<dftracer::utils::Runtime>(config, true);
156!
61
    } catch (const std::exception &e) {
78!
62
        set_typed_py_error(e);
×
63
        return -1;
×
64
    }
×
65

66
    return 0;
156✔
67
}
78✔
68

69
static PyObject *Runtime_shutdown(RuntimeObject *self,
156✔
70
                                  PyObject *Py_UNUSED(ignored)) {
71
    if (!self->runtime) {
156!
72
        PyErr_SetString(PyExc_RuntimeError, "Runtime not initialized");
×
73
        return NULL;
×
74
    }
75
    Py_BEGIN_ALLOW_THREADS self->runtime->shutdown();
156✔
76
    Py_END_ALLOW_THREADS Py_RETURN_NONE;
156✔
77
}
78✔
78

79
static bool set_size(PyObject *d, const char *key, std::size_t val) {
150✔
80
    return dict_set_size(d, key, val) == 0;
150✔
81
}
82

83
static bool set_double(PyObject *d, const char *key, double val) {
42✔
84
    return dict_set_f64(d, key, val) == 0;
42✔
85
}
86

87
static bool set_str(PyObject *d, const char *key, const std::string &val) {
58✔
88
    return dict_set_str(d, key, val.c_str()) == 0;
58✔
89
}
90

91
static bool set_bool(PyObject *d, const char *key, bool val) {
16✔
92
    return dict_set_bool(d, key, val) == 0;
16✔
93
}
94

95
static PyObject *build_task_progress(const dftracer::utils::TaskProgress &tp) {
14✔
96
    PyObject *td = PyDict_New();
14✔
97
    if (!td) return NULL;
14✔
98

99
    if (!set_str(td, "name", tp.name) || !set_str(td, "state", tp.state) ||
21!
100
        !set_double(td, "queued_duration_ms", tp.queued_duration_ms) ||
14!
101
        !set_double(td, "execution_duration_ms", tp.execution_duration_ms) ||
14!
102
        !set_size(td, "total_subtasks", tp.total_subtasks) ||
14!
103
        !set_size(td, "completed_subtasks", tp.completed_subtasks) ||
14!
104
        !set_double(td, "progress_pct", tp.progress_percentage) ||
28!
105
        !set_str(td, "location", tp.location)) {
14!
106
        Py_DECREF(td);
107
        return NULL;
×
108
    }
109

110
    PyObject *children =
7✔
111
        PyList_New(static_cast<Py_ssize_t>(tp.children.size()));
14✔
112
    if (!children) {
14✔
113
        Py_DECREF(td);
114
        return NULL;
×
115
    }
116
    for (std::size_t i = 0; i < tp.children.size(); ++i) {
14!
117
        PyObject *child = build_task_progress(tp.children[i]);
×
118
        if (!child) {
×
119
            Py_DECREF(children);
120
            Py_DECREF(td);
121
            return NULL;
×
122
        }
123
        PyList_SET_ITEM(children, static_cast<Py_ssize_t>(i), child);
×
124
    }
125
    if (PyDict_SetItemString(td, "children", children) < 0) {
14✔
126
        Py_DECREF(children);
127
        Py_DECREF(td);
128
        return NULL;
×
129
    }
130
    Py_DECREF(children);
7✔
131
    return td;
14✔
132
}
7✔
133

134
static PyObject *Runtime_get_progress(RuntimeObject *self,
18✔
135
                                      PyObject *Py_UNUSED(ignored)) {
136
    if (!self->runtime) {
18!
137
        PyErr_SetString(PyExc_RuntimeError, "Runtime not initialized");
×
138
        return NULL;
×
139
    }
140

141
    dftracer::utils::ExecutorProgress prog;
18✔
142
    Py_BEGIN_ALLOW_THREADS prog = self->runtime->get_progress();
18!
143
    Py_END_ALLOW_THREADS
18!
144

145
        PyObject *d = PyDict_New();
18!
146
    if (!d) return NULL;
18✔
147

148
    if (!set_size(d, "total", prog.total_tasks_submitted) ||
18!
149
        !set_size(d, "completed", prog.tasks_completed) ||
18!
150
        !set_size(d, "running", prog.tasks_running) ||
18!
151
        !set_size(d, "queued", prog.tasks_queued) ||
45!
152
        !set_size(d, "failed", prog.tasks_failed)) {
18!
153
        Py_DECREF(d);
×
154
        return NULL;
×
155
    }
156

157
    // Workers
158
    PyObject *workers =
9✔
159
        PyList_New(static_cast<Py_ssize_t>(prog.workers.size()));
18!
160
    if (!workers) {
18!
161
        Py_DECREF(d);
×
162
        return NULL;
×
163
    }
164
    for (std::size_t i = 0; i < prog.workers.size(); ++i) {
34✔
165
        const auto &w = prog.workers[i];
16✔
166
        PyObject *wd = PyDict_New();
16!
167
        if (!wd || !set_size(wd, "id", w.worker_id) ||
16!
168
            !set_bool(wd, "idle", w.is_idle) ||
16!
169
            !set_str(wd, "task", w.current_task_name) ||
40!
170
            !set_size(wd, "queue_depth", w.local_queue_depth)) {
16!
171
            Py_XDECREF(wd);
×
172
            Py_DECREF(workers);
×
173
            Py_DECREF(d);
×
174
            return NULL;
×
175
        }
176
        PyList_SET_ITEM(workers, static_cast<Py_ssize_t>(i), wd);
16!
177
    }
8✔
178
    if (PyDict_SetItemString(d, "workers", workers) < 0) {
18!
179
        Py_DECREF(workers);
×
180
        Py_DECREF(d);
×
181
        return NULL;
×
182
    }
183
    Py_DECREF(workers);
9!
184

185
    // Tasks
186
    PyObject *tasks =
9✔
187
        PyList_New(static_cast<Py_ssize_t>(prog.root_tasks.size()));
18!
188
    if (!tasks) {
18!
189
        Py_DECREF(d);
×
190
        return NULL;
×
191
    }
192
    for (std::size_t i = 0; i < prog.root_tasks.size(); ++i) {
32✔
193
        PyObject *tp = build_task_progress(prog.root_tasks[i]);
14!
194
        if (!tp) {
14!
195
            Py_DECREF(tasks);
×
196
            Py_DECREF(d);
×
197
            return NULL;
×
198
        }
199
        PyList_SET_ITEM(tasks, static_cast<Py_ssize_t>(i), tp);
14!
200
    }
7✔
201
    if (PyDict_SetItemString(d, "tasks", tasks) < 0) {
18!
202
        Py_DECREF(tasks);
×
203
        Py_DECREF(d);
×
204
        return NULL;
×
205
    }
206
    Py_DECREF(tasks);
9!
207

208
    // Errors
209
    PyObject *errors =
9✔
210
        PyList_New(static_cast<Py_ssize_t>(prog.recent_errors.size()));
18!
211
    if (!errors) {
18!
212
        Py_DECREF(d);
×
213
        return NULL;
×
214
    }
215
    for (std::size_t i = 0; i < prog.recent_errors.size(); ++i) {
18✔
216
        const auto &[tid, msg] = prog.recent_errors[i];
×
217
        PyObject *ed = PyDict_New();
×
218
        if (!ed || !set_size(ed, "task_id", static_cast<std::size_t>(tid)) ||
×
219
            !set_str(ed, "message", msg)) {
×
220
            Py_XDECREF(ed);
×
221
            Py_DECREF(errors);
×
222
            Py_DECREF(d);
×
223
            return NULL;
×
224
        }
225
        PyList_SET_ITEM(errors, static_cast<Py_ssize_t>(i), ed);
×
226
    }
227
    if (PyDict_SetItemString(d, "errors", errors) < 0) {
18!
228
        Py_DECREF(errors);
×
229
        Py_DECREF(d);
×
230
        return NULL;
×
231
    }
232
    Py_DECREF(errors);
9!
233

234
    return d;
18✔
235
}
18✔
236

237
static PyObject *Runtime_is_responsive(RuntimeObject *self,
2✔
238
                                       PyObject *Py_UNUSED(ignored)) {
239
    if (!self->runtime) {
2!
240
        PyErr_SetString(PyExc_RuntimeError, "Runtime not initialized");
×
241
        return NULL;
×
242
    }
243
    bool resp;
244
    Py_BEGIN_ALLOW_THREADS resp = self->runtime->is_responsive();
2✔
245
    Py_END_ALLOW_THREADS return PyBool_FromLong(resp ? 1 : 0);
2!
246
}
1✔
247

248
static PyObject *Runtime_set_timeout(RuntimeObject *self, PyObject *args,
×
249
                                     PyObject *kwds) {
250
    static const char *kwlist[] = {"global_ms", NULL};
251
    Py_ssize_t ms = 0;
×
252

253
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|n", (char **)kwlist, &ms)) {
×
254
        return NULL;
×
255
    }
256

257
    if (!self->runtime) {
×
258
        PyErr_SetString(PyExc_RuntimeError, "Runtime not initialized");
×
259
        return NULL;
×
260
    }
261

262
    Py_BEGIN_ALLOW_THREADS self->runtime->set_global_timeout(
×
263
        std::chrono::milliseconds(ms));
264
    Py_END_ALLOW_THREADS Py_RETURN_NONE;
×
265
}
266

267
static PyObject *Runtime_set_default_task_timeout(RuntimeObject *self,
×
268
                                                  PyObject *args,
269
                                                  PyObject *kwds) {
270
    static const char *kwlist[] = {"ms", NULL};
271
    Py_ssize_t ms = 0;
×
272

273
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|n", (char **)kwlist, &ms)) {
×
274
        return NULL;
×
275
    }
276

277
    if (!self->runtime) {
×
278
        PyErr_SetString(PyExc_RuntimeError, "Runtime not initialized");
×
279
        return NULL;
×
280
    }
281

282
    Py_BEGIN_ALLOW_THREADS self->runtime->set_default_task_timeout(
×
283
        std::chrono::milliseconds(ms));
284
    Py_END_ALLOW_THREADS Py_RETURN_NONE;
×
285
}
286

287
static PyObject *Runtime_wait_all(RuntimeObject *self,
188✔
288
                                  PyObject *Py_UNUSED(ignored)) {
289
    if (!self->runtime) {
188!
290
        PyErr_SetString(PyExc_RuntimeError, "Runtime not initialized");
×
291
        return NULL;
×
292
    }
293
    if (!run_blocking([&] { self->runtime->wait_all(); })) return NULL;
376!
294
    Py_RETURN_NONE;
188✔
295
}
94✔
296

297
static PyObject *Runtime_enter(RuntimeObject *self,
×
298
                               PyObject *Py_UNUSED(ignored)) {
299
    Py_INCREF(self);
300
    return (PyObject *)self;
×
301
}
302

303
static PyObject *Runtime_exit(RuntimeObject *self, PyObject *args) {
×
304
    if (self->runtime) {
×
305
        Py_BEGIN_ALLOW_THREADS self->runtime->shutdown();
×
306
        Py_END_ALLOW_THREADS
×
307
    }
308
    Py_RETURN_NONE;
×
309
}
310

311
static PyObject *Runtime_get_threads(RuntimeObject *self, void *closure) {
14✔
312
    if (!self->runtime) {
14!
313
        PyErr_SetString(PyExc_RuntimeError, "Runtime not initialized");
×
314
        return NULL;
×
315
    }
316
    return PyLong_FromSize_t(self->runtime->threads());
14✔
317
}
7✔
318

319
static PyObject *get_default_runtime_py(PyObject *Py_UNUSED(module),
2✔
320
                                        PyObject *Py_UNUSED(ignored)) {
321
    dftracer::utils::Runtime *rt = get_default_runtime();
2✔
322
    if (!rt) {
2✔
323
        PyErr_SetString(PyExc_RuntimeError, "Failed to create default runtime");
×
324
        return NULL;
×
325
    }
326

327
    RuntimeObject *obj = (RuntimeObject *)RuntimeType.tp_alloc(&RuntimeType, 0);
2✔
328
    if (!obj) return NULL;
2✔
329

330
    new (&obj->runtime)
2✔
331
        std::shared_ptr<dftracer::utils::Runtime>(g_default_runtime);
2✔
332
    return (PyObject *)obj;
2✔
333
}
1✔
334

335
static PyObject *set_default_runtime_py(PyObject *Py_UNUSED(module),
4✔
336
                                        PyObject *args) {
337
    PyObject *arg;
338
    if (!PyArg_ParseTuple(args, "O", &arg)) return NULL;
4!
339

340
    if (arg == Py_None) {
4✔
341
        g_default_runtime.reset();
×
342
        Py_RETURN_NONE;
×
343
    }
344

345
    if (!PyObject_TypeCheck(arg, &RuntimeType)) {
4!
346
        PyErr_SetString(PyExc_TypeError, "Expected Runtime or None");
×
347
        return NULL;
×
348
    }
349

350
    g_default_runtime = ((RuntimeObject *)arg)->runtime;
4✔
351
    Py_RETURN_NONE;
4✔
352
}
2✔
353

354
static PyMethodDef Runtime_methods[] = {
355
    {"shutdown", (PyCFunction)Runtime_shutdown, METH_NOARGS,
356
     "shutdown()\n"
357
     "--\n"
358
     "\n"
359
     "Shut down the runtime.\n"},
360
    {"get_progress", (PyCFunction)Runtime_get_progress, METH_NOARGS,
361
     "Return progress dict with keys: total, completed, running,\n"
362
     "queued, failed."},
363
    {"is_responsive", (PyCFunction)Runtime_is_responsive, METH_NOARGS,
364
     "Return True if the runtime is making progress."},
365
    {"set_timeout", (PyCFunction)Runtime_set_timeout,
366
     METH_VARARGS | METH_KEYWORDS,
367
     "Set global timeout in milliseconds.\n"
368
     "\n"
369
     "Args:\n"
370
     "    global_ms (int): Timeout in milliseconds (0 = no timeout).\n"},
371
    {"set_default_task_timeout", (PyCFunction)Runtime_set_default_task_timeout,
372
     METH_VARARGS | METH_KEYWORDS,
373
     "Set default per-task timeout in milliseconds.\n"
374
     "\n"
375
     "Args:\n"
376
     "    ms (int): Timeout in milliseconds (0 = no timeout).\n"},
377
    {"wait_all", (PyCFunction)Runtime_wait_all, METH_NOARGS,
378
     "Wait for all outstanding submitted tasks to complete."},
379
    {"__enter__", (PyCFunction)Runtime_enter, METH_NOARGS,
380
     "Enter context manager."},
381
    {"__exit__", (PyCFunction)Runtime_exit, METH_VARARGS,
382
     "Exit context manager (calls shutdown)."},
383
    {NULL}};
384

385
static PyObject *Runtime_get_io_threads(RuntimeObject *self, void *closure) {
×
386
    if (!self->runtime) {
×
387
        PyErr_SetString(PyExc_RuntimeError, "Runtime not initialized");
×
388
        return NULL;
×
389
    }
390
    return PyLong_FromSize_t(self->runtime->io_threads());
×
391
}
392

393
static PyGetSetDef Runtime_getsetters[] = {
394
    {"threads", (getter)Runtime_get_threads, NULL, "Number of worker threads",
395
     NULL},
396
    {"io_threads", (getter)Runtime_get_io_threads, NULL,
397
     "Number of I/O threads", NULL},
398
    {NULL}};
399

400
PyTypeObject RuntimeType = {
401
    PyVarObject_HEAD_INIT(NULL, 0) "dftracer_utils_ext.Runtime",
402
    sizeof(RuntimeObject),                    /* tp_basicsize */
403
    0,                                        /* tp_itemsize */
404
    (destructor)Runtime_dealloc,              /* tp_dealloc */
405
    0,                                        /* tp_vectorcall_offset */
406
    0,                                        /* tp_getattr */
407
    0,                                        /* tp_setattr */
408
    0,                                        /* tp_as_async */
409
    0,                                        /* tp_repr */
410
    0,                                        /* tp_as_number */
411
    0,                                        /* tp_as_sequence */
412
    0,                                        /* tp_as_mapping */
413
    0,                                        /* tp_hash */
414
    0,                                        /* tp_call */
415
    0,                                        /* tp_str */
416
    0,                                        /* tp_getattro */
417
    0,                                        /* tp_setattro */
418
    0,                                        /* tp_as_buffer */
419
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
420
    "Runtime(threads: int = 0, io_threads: int = 0)\n"
421
    "--\n"
422
    "\n"
423
    "Coroutine runtime backed by a thread pool.\n"
424
    "\n"
425
    "Args:\n"
426
    "    threads (int): Number of worker threads. 0 (default) uses\n"
427
    "        the hardware concurrency.\n"
428
    "    io_threads (int): Number of I/O threads. 0 (default) uses\n"
429
    "        the hardware concurrency.\n", /* tp_doc */
430
    0,                                     /* tp_traverse */
431
    0,                                     /* tp_clear */
432
    0,                                     /* tp_richcompare */
433
    0,                                     /* tp_weaklistoffset */
434
    0,                                     /* tp_iter */
435
    0,                                     /* tp_iternext */
436
    Runtime_methods,                       /* tp_methods */
437
    0,                                     /* tp_members */
438
    Runtime_getsetters,                    /* tp_getset */
439
    0,                                     /* tp_base */
440
    0,                                     /* tp_dict */
441
    0,                                     /* tp_descr_get */
442
    0,                                     /* tp_descr_set */
443
    0,                                     /* tp_dictoffset */
444
    (initproc)Runtime_init,                /* tp_init */
445
    0,                                     /* tp_alloc */
446
    Runtime_new,                           /* tp_new */
447
};
448

449
// Module-level function table (registered via PyModule_AddFunctions or
450
// appended to the module's method table in init_runtime).
451
static PyMethodDef runtime_module_methods[] = {
452
    {"get_default_runtime", get_default_runtime_py, METH_NOARGS,
453
     "Return the module-level default Runtime (lazy-created)."},
454
    {"set_default_runtime", set_default_runtime_py, METH_VARARGS,
455
     "Replace the module-level default Runtime (pass None to clear).\n"
456
     "\n"
457
     "Args:\n"
458
     "    runtime (Runtime or None): New default runtime.\n"},
459
    {NULL}};
460

461
int init_runtime(PyObject *m) {
2✔
462
    if (register_type(m, &RuntimeType, "Runtime") < 0) return -1;
2✔
463

464
    for (PyMethodDef *def = runtime_module_methods; def->ml_name; ++def) {
6✔
465
        PyObject *fn = PyCFunction_New(def, NULL);
4✔
466
        if (!fn) return -1;
4!
467
        if (PyModule_AddObject(m, def->ml_name, fn) < 0) {
4!
468
            Py_DECREF(fn);
469
            return -1;
×
470
        }
471
    }
2✔
472

473
    return 0;
2✔
474
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc