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

celerity / celerity-runtime / 10304230986

08 Aug 2024 02:40PM UTC coverage: 95.07% (+0.1%) from 94.946%
10304230986

push

github

fknorr
Fix executor-scheduler race between instruction retirement and pruning

live_executor previously dereferenced instruction pointers on retirement,
which raced with their deletion on IDAG pruning when the executing
instruction was an horizon or epoch. To avoid future similar issues,
we now strictly regard the pointer to any instruction that has been issued
as dangling and work with instruction ids instead in those cases.

out_of_order_engine also had potentially hazardous uses of instruction
pointers that might - under the right circumstances - incorrectly regard
two instruction as identical when their pointers happen to alias as the
old one being freed before the new one was allocated in the same memory
location.

2979 of 3378 branches covered (88.19%)

Branch coverage included in aggregate %.

22 of 23 new or added lines in 2 files covered. (95.65%)

14 existing lines in 5 files now uncovered.

6547 of 6642 relevant lines covered (98.57%)

1554450.58 hits per line

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

78.86
/src/utils.cc
1
#include "utils.h"
2
#include "log.h"
3

4
#include <atomic>
5
#include <regex>
6
#include <stdexcept>
7

8
#if !defined(_MSC_VER)
9
// Required for kernel name demangling in Clang
10
#include <cxxabi.h>
11
#endif
12

13

14
namespace celerity::detail::utils {
15

16
std::string get_simplified_type_name_from_pointer(const std::type_info& pointer_type_info) {
1,986✔
17
#if !defined(_MSC_VER)
18
        const std::unique_ptr<char, void (*)(void*)> demangle_buffer(abi::__cxa_demangle(pointer_type_info.name(), nullptr, nullptr, nullptr), std::free);
1,986✔
19
        std::string demangled_type_name = demangle_buffer.get();
5,958✔
20
#else
21
        std::string demangled_type_name = pointer_type_info.name();
22
#endif
23

24
        // get rid of the pointer "*"
25
        if(!demangled_type_name.empty() && demangled_type_name.back() == '*') { demangled_type_name.pop_back(); }
1,986!
26

27
        if(demangled_type_name.length() < 2) return demangled_type_name;
1,986!
28
        bool templated = false;
1,986✔
29
        // there are two options:
30
        // 1. the type is templated; in this case, the last character is ">" and we go back to the matching "<"
31
        std::string::size_type last_idx = demangled_type_name.length() - 1;
1,986✔
32
        if(demangled_type_name[last_idx] == '>') {
1,986✔
33
                templated = true;
46✔
34
                int open = 0;
46✔
35
                while(last_idx > 1) {
1,100!
36
                        last_idx--;
1,100✔
37
                        if(demangled_type_name[last_idx] == '>') { open++; }
1,100✔
38
                        if(demangled_type_name[last_idx] == '<') {
1,100✔
39
                                if(open > 0) {
54✔
40
                                        open--;
8✔
41
                                } else {
42
                                        last_idx--;
46✔
43
                                        break;
46✔
44
                                }
45
                        }
46
                }
47
        }
48
        // 2. the type isn't templated (or we just removed the template); in this case, we are interested in the part from the end to the last ":" (or the start)
49
        std::string::size_type start_idx = last_idx - 1;
1,986✔
50
        while(start_idx > 0 && demangled_type_name[start_idx - 1] != ':') {
35,094✔
51
                start_idx--;
33,108✔
52
        }
53
        // if the type was templated, we add a "<...>" to indicate that
54
        return demangled_type_name.substr(start_idx, last_idx - start_idx + 1) + (templated ? "<...>" : "");
3,972✔
55
}
1,986✔
56

57
std::string escape_for_dot_label(std::string str) {
427✔
58
        str = std::regex_replace(str, std::regex("&"), "&amp;");
427✔
59
        str = std::regex_replace(str, std::regex("<"), "&lt;");
427✔
60
        str = std::regex_replace(str, std::regex(">"), "&gt;");
427✔
61
        return str;
427✔
62
}
63

64
// LCOV_EXCL_START
65
[[noreturn]] void unreachable() {
66
        assert(!"executed unreachable code");
67
        abort();
68
}
69
// LCOV_EXCL_STOP
70

71
// The panic solution defaults to `log_and_abort`, but is set to `throw_logic_error` in test binaries. Since panics are triggered from celerity library code, we
72
// manage it in a global and decide which path to take at runtime. We have also considered deciding this at link time by defining a weak symbol (GCC
73
// __attribute__((weak))) in the library which is overwritten by a strong symbol in the test library, but decided against this because there is no equivalent in
74
// MSVC and we would have to resort to even dirtier linker hacks for that target.
75
std::atomic<panic_solution> g_panic_solution = panic_solution::log_and_abort; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
76

77
void set_panic_solution(panic_solution solution) { g_panic_solution.store(solution, std::memory_order_relaxed); }
13✔
78

79
[[noreturn]] void panic(const std::string& msg) {
27✔
80
        const auto solution = g_panic_solution.load(std::memory_order_relaxed);
27✔
81
        if(solution == panic_solution::throw_logic_error) {
27!
82
                throw std::logic_error(msg);
27✔
83
        } else /* panic_solution::log_and_abort */ {
84
                // LCOV_EXCL_START
85
                // Print directly instead of logging: The abort message must not be hidden by log level setting, and in tests would be captured without the logging
86
                // infrastructure having a chance of dumping the logs due to the abort.
87
                fmt::print(stderr, "celerity-runtime panic: {}\n", msg);
88
                std::abort();
89
                // LCOV_EXCL_STOP
90
        }
91
}
92

93
void report_error(const error_policy policy, const std::string& msg) {
32✔
94
        switch(policy) {
32!
UNCOV
95
        case error_policy::ignore: break;
×
96
        case error_policy::log_warning: CELERITY_WARN("{}", msg); break;
2!
97
        case error_policy::log_error: CELERITY_ERROR("{}", msg); break;
13!
98
        case error_policy::panic: panic(msg); break;
17✔
99
        }
100
}
15✔
101

102
std::string make_buffer_debug_label(const buffer_id bid, const std::string& name) {
369✔
103
        // if there is no name defined, the name will be the buffer id.
104
        // if there is a name we want "id name"
105
        return !name.empty() ? fmt::format("B{} \"{}\"", bid, name) : fmt::format("B{}", bid);
738✔
106
}
107

108
std::string make_task_debug_label(const task_type tt, const task_id tid, const std::string& debug_name, bool title_case) {
44✔
109
        const auto type_string = [tt] {
44✔
110
                switch(tt) {
44!
UNCOV
111
                case task_type::epoch: return "epoch";
×
112
                case task_type::host_compute: return "host-compute task";
8✔
113
                case task_type::device_compute: return "device kernel";
27✔
114
                case task_type::collective: return "collective host task";
7✔
115
                case task_type::master_node: return "master-node host task";
2✔
116
                case task_type::horizon: return "horizon";
×
117
                case task_type::fence: return "fence";
×
UNCOV
118
                default: return "unknown task";
×
119
                }
120
        }();
44✔
121

122
        auto label = fmt::format("{} T{}", type_string, tid);
44✔
123
        if(title_case) { label[0] = static_cast<char>(std::toupper(label[0])); }
44✔
124
        if(!debug_name.empty()) { fmt::format_to(std::back_inserter(label), " \"{}\"", debug_name); }
44✔
125
        return label;
88✔
UNCOV
126
}
×
127

128
} // namespace celerity::detail::utils
129

130

131
// implemented here because types.h must not depend on utils.h
132
std::size_t std::hash<celerity::detail::transfer_id>::operator()(const celerity::detail::transfer_id& t) const noexcept {
7,894✔
133
        auto hash = std::hash<celerity::detail::task_id>{}(t.consumer_tid);
7,894✔
134
        celerity::detail::utils::hash_combine(hash, std::hash<celerity::detail::buffer_id>{}(t.bid));
7,894✔
135
        celerity::detail::utils::hash_combine(hash, std::hash<celerity::detail::reduction_id>{}(t.rid));
7,894✔
136
        return hash;
7,894✔
137
}
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