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

llnl / dftracer-utils / 30058987329

24 Jul 2026 01:26AM UTC coverage: 52.76% (+0.1%) from 52.66%
30058987329

Pull #99

github

web-flow
Merge d78ce6d20 into 06bc84ec9
Pull Request #99: Support CM time_metric (NS/MS/SEC/US) across reader and viz

41934 of 102825 branches covered (40.78%)

Branch coverage included in aggregate %.

2278 of 2903 new or added lines in 61 files covered. (78.47%)

144 existing lines in 14 files now uncovered.

37072 of 46920 relevant lines covered (79.01%)

59158.32 hits per line

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

59.54
/src/dftracer/utils/core/runtime.cpp
1
#include <dftracer/utils/core/common/error.h>
2
#include <dftracer/utils/core/common/logging.h>
3
#include <dftracer/utils/core/common/platform_compat.h>
4
#include <dftracer/utils/core/env.h>
5
#include <dftracer/utils/core/runtime.h>
6

7
#include <algorithm>
8
#include <cstdlib>
9
#include <stdexcept>
10
#include <string>
11
#include <string_view>
12
#include <thread>
13

14
namespace dftracer::utils {
15

16
namespace {
17
// Resolve the worker-thread count. DFTRACER_UTILS_THREADS overrides everything
18
// (a debugging lever: force a fixed count, e.g. 1 for a single-threaded async
19
// loop). Otherwise 0 means hardware_concurrency.
20
std::size_t resolve_threads(std::size_t requested) {
910✔
21
    if (auto env = Env::get<std::string_view>("DFTRACER_UTILS_THREADS");
1,820!
22
        env.has_value()) {
910✔
23
        const long long n =
24
            std::strtoll(std::string(*env).c_str(), nullptr, 10);
×
25
        if (n > 0) return static_cast<std::size_t>(n);
×
26
    }
27
    return requested == 0 ? dftracer_utils_hardware_concurrency() : requested;
910✔
28
}
455✔
29

30
// Resolve the I/O-pool size. DFTRACER_UTILS_IO_THREADS overrides everything (a
31
// lever to bound the epoll/kqueue thread pool independently of the compute
32
// pool, e.g. so the coordinator does not open a full-machine-sized pool).
33
// Otherwise 0 means hardware_concurrency, matching the executor default.
34
std::size_t resolve_io_threads(std::size_t requested) {
910✔
35
    if (auto env = Env::get<std::string_view>("DFTRACER_UTILS_IO_THREADS");
1,820!
36
        env.has_value()) {
910✔
37
        const long long n =
NEW
38
            std::strtoll(std::string(*env).c_str(), nullptr, 10);
×
NEW
39
        if (n > 0) return static_cast<std::size_t>(n);
×
40
    }
41
    return requested == 0 ? dftracer_utils_hardware_concurrency() : requested;
910✔
42
}
455✔
43
}  // namespace
44

45
Runtime::Runtime(std::size_t threads) : threads_(resolve_threads(threads)) {
438!
46
    ExecutorConfig config;
292!
47
    config.num_threads = threads_;
292✔
48
    config.io_pool_size = resolve_io_threads(config.io_pool_size);
292!
49
    executor_ = std::make_unique<Executor>(config);
292!
50
    executor_->start();
292!
51

52
    watchdog_ = std::make_unique<Watchdog>();
292!
53
    watchdog_->set_executor(executor_.get());
292!
54
}
438✔
55

56
Runtime::Runtime(const ExecutorConfig& config, bool enable_watchdog)
234✔
57
    : threads_(resolve_threads(config.num_threads)) {
234!
58
    ExecutorConfig cfg = config;
156✔
59
    cfg.num_threads = threads_;
156✔
60
    cfg.io_pool_size = resolve_io_threads(config.io_pool_size);
156!
61
    executor_ = std::make_unique<Executor>(cfg);
156!
62
    executor_->start();
156!
63

64
    if (enable_watchdog) {
156!
65
        watchdog_ = std::make_unique<Watchdog>();
156!
66
        watchdog_->set_executor(executor_.get());
156!
67
    }
78✔
68
}
234✔
69

70
Runtime::Runtime(const ExecutorConfig& config,
693✔
71
                 std::unique_ptr<Watchdog> watchdog)
231✔
72
    : threads_(resolve_threads(config.num_threads)) {
693!
73
    ExecutorConfig cfg = config;
462✔
74
    cfg.num_threads = threads_;
462✔
75
    cfg.io_pool_size = resolve_io_threads(config.io_pool_size);
462!
76
    executor_ = std::make_unique<Executor>(cfg);
462!
77
    executor_->start();
462!
78

79
    watchdog_ = std::move(watchdog);
462✔
80
    if (watchdog_) {
462✔
81
        watchdog_->set_executor(executor_.get());
162!
82
    }
81✔
83
}
693✔
84

85
Runtime::~Runtime() { shutdown(); }
1,365!
86

87
TaskHandle Runtime::submit(coro::CoroTask<void> task, std::string name) {
1,050✔
88
    if (shutdown_called_.load(std::memory_order_acquire)) {
1,050✔
89
        throw DFTUtilsException(ErrorCode::PIPELINE, "Runtime is shut down");
4!
90
    }
91
    if (name.empty()) {
1,046✔
92
        name = "task-" + std::to_string(task_name_counter_++);
6!
93
    }
3✔
94

95
    auto promise = std::make_shared<std::promise<void>>();
1,046!
96
    auto future = promise->get_future().share();
1,046!
97
    auto tid = std::make_shared<std::atomic<TaskIndex>>(-1);
1,046!
98

99
    auto wrapper =
523✔
100
        [](coro::CoroTask<void> t, std::shared_ptr<std::promise<void>> p,
4,178!
101
           Executor* exec,
102
           std::shared_ptr<std::atomic<TaskIndex>> task_id) -> coro::Coro {
523!
103
        try {
104
            co_await std::move(t);
2,609!
105
            t = coro::CoroTask<void>{
1,040!
106
                std::coroutine_handle<coro::CoroTask<void>::promise_type>{}};
520✔
107
            exec->mark_coro_completed(task_id->load(std::memory_order_acquire));
520!
108
        } catch (...) {
523✔
109
            t = coro::CoroTask<void>{
6!
110
                std::coroutine_handle<coro::CoroTask<void>::promise_type>{}};
3✔
111
            exec->mark_coro_completed(task_id->load(std::memory_order_acquire));
3!
112
            p->set_exception(std::current_exception());
3!
113
            co_return;
3✔
114
        }
3!
115
        p->set_value();
520!
116
    };
2,089!
117

118
    // Set the executor on the task's promise so awaitables (e.g. channels)
119
    // that capture `get_root_promise()->get_executor()` can schedule
120
    // resumption. Without this, awaiters end up with executor=nullptr because
121
    // the wrapping `coro::Coro` doesn't extend PromiseBase and the
122
    // root-promise chain stops at the user's CoroTask.
123
    if (task.handle()) {
1,046✔
124
        task.handle().promise().set_executor(executor_.get());
1,046!
125
    }
523✔
126
    auto coro = wrapper(std::move(task), promise, executor_.get(), tid);
1,569!
127
    TaskIndex id = executor_->enqueue_tracked(std::move(coro), name, tid);
1,046!
128

129
    {
130
        std::lock_guard<std::mutex> lock(futures_mutex_);
1,046!
131
        cleanup_completed_futures();
1,046!
132
        outstanding_futures_.push_back(future);
1,046!
133
    }
1,046✔
134

135
    return TaskHandle{future, id, std::move(name)};
1,569!
136
}
1,048✔
137

138
void Runtime::wait_all() {
200✔
139
    std::vector<std::shared_future<void>> futures;
200✔
140
    {
141
        std::lock_guard<std::mutex> lock(futures_mutex_);
200!
142
        futures = std::move(outstanding_futures_);
200✔
143
        outstanding_futures_.clear();
200✔
144
    }
200✔
145
    for (auto& f : futures) {
278✔
146
        f.wait();
78!
147
    }
148
}
200✔
149

150
void Runtime::cleanup_completed_futures() {
1,218✔
151
    outstanding_futures_.erase(
2,436!
152
        std::remove_if(outstanding_futures_.begin(), outstanding_futures_.end(),
1,218!
153
                       [](const std::shared_future<void>& f) {
949✔
154
                           return f.wait_for(std::chrono::seconds(0)) ==
949!
155
                                  std::future_status::ready;
456✔
156
                       }),
157
        outstanding_futures_.end());
1,218✔
158
}
1,218✔
159

160
ExecutorProgress Runtime::get_progress() const {
38✔
161
    return executor_->get_progress();
38✔
162
}
163

164
bool Runtime::is_responsive() const { return executor_->is_responsive(); }
4✔
165

166
void Runtime::set_global_timeout(std::chrono::milliseconds timeout) {
×
167
    if (!watchdog_) {
×
168
        throw DFTUtilsException(
×
169
            ErrorCode::PIPELINE,
170
            "Cannot set timeout: Runtime created without watchdog");
×
171
    }
172
    watchdog_->set_global_timeout(timeout);
×
173
}
×
174

175
void Runtime::set_default_task_timeout(std::chrono::milliseconds timeout) {
×
176
    if (!watchdog_) {
×
177
        throw DFTUtilsException(
×
178
            ErrorCode::PIPELINE,
179
            "Cannot set timeout: Runtime created without watchdog");
×
180
    }
181
    watchdog_->set_default_task_timeout(timeout);
×
182
}
×
183

184
void Runtime::shutdown() {
1,724✔
185
    bool expected = false;
1,724✔
186
    if (!shutdown_called_.compare_exchange_strong(expected, true)) return;
1,724✔
187
    if (watchdog_) watchdog_->stop();
910!
188
    if (executor_) executor_->shutdown();
910!
189
}
862✔
190

191
std::size_t Runtime::threads() const { return threads_; }
334✔
192

193
std::size_t Runtime::io_threads() const {
×
194
    return executor_ ? executor_->get_io_pool_size() : 0;
×
195
}
196

197
}  // namespace dftracer::utils
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc