Skip to content

Commit 574bf3c

Browse files
committed
feat(errors): add concurrency runtime error rules
2 parents 6055b42 + d1d46d0 commit 574bf3c

9 files changed

Lines changed: 631 additions & 0 deletions

include/vix/cli/errors/runtime/IRuntimeErrorRule.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ namespace vix::cli::errors::runtime
3939
std::unique_ptr<IRuntimeErrorRule> makeThreadJoinableRule();
4040
std::unique_ptr<IRuntimeErrorRule> makeSegfaultRule();
4141
std::unique_ptr<IRuntimeErrorRule> makeAbortRule();
42+
std::unique_ptr<IRuntimeErrorRule> makeDataRaceRule();
43+
std::unique_ptr<IRuntimeErrorRule> makeDeadlockRule();
44+
std::unique_ptr<IRuntimeErrorRule> makeMutexMisuseRule();
45+
std::unique_ptr<IRuntimeErrorRule> makeConditionVariableMisuseRule();
46+
std::unique_ptr<IRuntimeErrorRule> makeFuturePromiseRule();
47+
std::unique_ptr<IRuntimeErrorRule> makeThreadCreationFailureRule();
48+
std::unique_ptr<IRuntimeErrorRule> makeDetachedThreadLifetimeRule();
4249
} // namespace vix::cli::errors::runtime
4350

4451
#endif

src/errors/RawLogDetectors.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,11 +1169,25 @@ namespace vix::cli::errors
11691169
{
11701170
using vix::cli::errors::runtime::IRuntimeErrorRule;
11711171
using vix::cli::errors::runtime::makeAbortRule;
1172+
using vix::cli::errors::runtime::makeConditionVariableMisuseRule;
1173+
using vix::cli::errors::runtime::makeDataRaceRule;
1174+
using vix::cli::errors::runtime::makeDeadlockRule;
1175+
using vix::cli::errors::runtime::makeDetachedThreadLifetimeRule;
1176+
using vix::cli::errors::runtime::makeFuturePromiseRule;
1177+
using vix::cli::errors::runtime::makeMutexMisuseRule;
11721178
using vix::cli::errors::runtime::makeSegfaultRule;
1179+
using vix::cli::errors::runtime::makeThreadCreationFailureRule;
11731180
using vix::cli::errors::runtime::makeThreadJoinableRule;
11741181

11751182
std::vector<std::unique_ptr<IRuntimeErrorRule>> rules;
11761183
rules.push_back(makeThreadJoinableRule());
1184+
rules.push_back(makeDataRaceRule());
1185+
rules.push_back(makeDeadlockRule());
1186+
rules.push_back(makeMutexMisuseRule());
1187+
rules.push_back(makeConditionVariableMisuseRule());
1188+
rules.push_back(makeFuturePromiseRule());
1189+
rules.push_back(makeThreadCreationFailureRule());
1190+
rules.push_back(makeDetachedThreadLifetimeRule());
11771191
rules.push_back(makeSegfaultRule());
11781192
rules.push_back(makeAbortRule());
11791193
return rules;
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/**
2+
*
3+
* @file ConditionVariableMisuseRule.cpp
4+
* @author Gaspard Kirira
5+
*
6+
* Copyright 2025, Gaspard Kirira. All rights reserved.
7+
* https://github.com/vixcpp/vix
8+
* Use of this source code is governed by a MIT license
9+
* that can be found in the License file.
10+
*
11+
* Vix.cpp
12+
*
13+
*/
14+
#include <vix/cli/errors/runtime/IRuntimeErrorRule.hpp>
15+
#include <vix/cli/errors/runtime/RuntimeRuleUtils.hpp>
16+
17+
#include <filesystem>
18+
#include <iostream>
19+
#include <memory>
20+
#include <string>
21+
#include <vector>
22+
23+
#include <vix/cli/Style.hpp>
24+
25+
using namespace vix::cli::style;
26+
27+
namespace vix::cli::errors::runtime
28+
{
29+
class ConditionVariableMisuseRule final : public IRuntimeErrorRule
30+
{
31+
public:
32+
bool match(
33+
const std::string &log,
34+
const std::filesystem::path &sourceFile) const override
35+
{
36+
(void)sourceFile;
37+
38+
return icontains(log, "condition variable") ||
39+
icontains(log, "condition_variable") ||
40+
icontains(log, "pthread_cond") ||
41+
icontains(log, "wait on uninitialized condition variable") ||
42+
icontains(log, "invalid condition variable") ||
43+
icontains(log, "wait without lock");
44+
}
45+
46+
bool handle(
47+
const std::string &log,
48+
const std::filesystem::path &sourceFile) const override
49+
{
50+
std::string title = "runtime error: condition variable misuse";
51+
std::vector<std::string> hints = {
52+
"a condition variable was likely used incorrectly",
53+
"check waits without a locked std::unique_lock, invalid lifetime, and missing predicate-based waits",
54+
};
55+
56+
if (icontains(log, "wait without lock"))
57+
{
58+
title = "runtime error: condition variable wait without lock";
59+
hints = {
60+
"wait() must be called with a locked std::unique_lock<std::mutex>",
61+
"lock the mutex before waiting and use wait(lock, predicate) when possible",
62+
};
63+
}
64+
else if (icontains(log, "uninitialized condition variable") ||
65+
icontains(log, "invalid condition variable") ||
66+
icontains(log, "pthread_cond"))
67+
{
68+
title = "runtime error: invalid condition variable state";
69+
hints = {
70+
"the condition variable may be uninitialized, destroyed, or otherwise invalid",
71+
"check object lifetime and avoid waiting or notifying after destruction",
72+
};
73+
}
74+
75+
std::cerr << RED
76+
<< title
77+
<< RESET << "\n";
78+
79+
print_runtime_hints_and_at(
80+
hints,
81+
!sourceFile.empty() ? ("source: " + sourceFile.filename().string()) : "");
82+
83+
return true;
84+
}
85+
};
86+
87+
std::unique_ptr<IRuntimeErrorRule> makeConditionVariableMisuseRule()
88+
{
89+
return std::make_unique<ConditionVariableMisuseRule>();
90+
}
91+
} // namespace vix::cli::errors::runtime
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
*
3+
* @file DataRaceRule.cpp
4+
* @author Gaspard Kirira
5+
*
6+
* Copyright 2025, Gaspard Kirira. All rights reserved.
7+
* https://github.com/vixcpp/vix
8+
* Use of this source code is governed by a MIT license
9+
* that can be found in the License file.
10+
*
11+
* Vix.cpp
12+
*
13+
*/
14+
#include <vix/cli/errors/runtime/IRuntimeErrorRule.hpp>
15+
#include <vix/cli/errors/runtime/RuntimeRuleUtils.hpp>
16+
17+
#include <filesystem>
18+
#include <iostream>
19+
#include <memory>
20+
#include <string>
21+
22+
#include <vix/cli/Style.hpp>
23+
24+
using namespace vix::cli::style;
25+
26+
namespace vix::cli::errors::runtime
27+
{
28+
class DataRaceRule final : public IRuntimeErrorRule
29+
{
30+
public:
31+
bool match(
32+
const std::string &log,
33+
const std::filesystem::path &sourceFile) const override
34+
{
35+
(void)sourceFile;
36+
37+
return icontains(log, "ThreadSanitizer") ||
38+
icontains(log, "data race");
39+
}
40+
41+
bool handle(
42+
const std::string &log,
43+
const std::filesystem::path &sourceFile) const override
44+
{
45+
(void)log;
46+
47+
std::cerr << RED
48+
<< "runtime error: data race"
49+
<< RESET << "\n";
50+
51+
print_runtime_hints_and_at(
52+
{
53+
"two or more threads accessed the same memory without proper synchronization",
54+
"protect shared state with std::mutex, std::scoped_lock, or std::atomic",
55+
},
56+
!sourceFile.empty() ? ("source: " + sourceFile.filename().string()) : "");
57+
58+
return true;
59+
}
60+
};
61+
62+
std::unique_ptr<IRuntimeErrorRule> makeDataRaceRule()
63+
{
64+
return std::make_unique<DataRaceRule>();
65+
}
66+
} // namespace vix::cli::errors::runtime
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
*
3+
* @file DeadlockRule.cpp
4+
* @author Gaspard Kirira
5+
*
6+
* Copyright 2025, Gaspard Kirira. All rights reserved.
7+
* https://github.com/vixcpp/vix
8+
* Use of this source code is governed by a MIT license
9+
* that can be found in the License file.
10+
*
11+
* Vix.cpp
12+
*
13+
*/
14+
#include <vix/cli/errors/runtime/IRuntimeErrorRule.hpp>
15+
#include <vix/cli/errors/runtime/RuntimeRuleUtils.hpp>
16+
17+
#include <filesystem>
18+
#include <iostream>
19+
#include <memory>
20+
#include <string>
21+
22+
#include <vix/cli/Style.hpp>
23+
24+
using namespace vix::cli::style;
25+
26+
namespace vix::cli::errors::runtime
27+
{
28+
class DeadlockRule final : public IRuntimeErrorRule
29+
{
30+
public:
31+
bool match(
32+
const std::string &log,
33+
const std::filesystem::path &sourceFile) const override
34+
{
35+
(void)sourceFile;
36+
37+
return icontains(log, "deadlock") ||
38+
icontains(log, "resource deadlock avoided") ||
39+
icontains(log, "e deadlk") ||
40+
icontains(log, "std::system_error: resource deadlock avoided");
41+
}
42+
43+
bool handle(
44+
const std::string &log,
45+
const std::filesystem::path &sourceFile) const override
46+
{
47+
(void)log;
48+
49+
std::cerr << RED
50+
<< "runtime error: deadlock"
51+
<< RESET << "\n";
52+
53+
print_runtime_hints_and_at(
54+
{
55+
"two or more execution paths are waiting on each other and cannot make progress",
56+
"lock mutexes in a consistent order and prefer std::scoped_lock for multiple mutexes",
57+
},
58+
!sourceFile.empty() ? ("source: " + sourceFile.filename().string()) : "");
59+
60+
return true;
61+
}
62+
};
63+
64+
std::unique_ptr<IRuntimeErrorRule> makeDeadlockRule()
65+
{
66+
return std::make_unique<DeadlockRule>();
67+
}
68+
} // namespace vix::cli::errors::runtime
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
*
3+
* @file DetachedThreadLifetimeRule.cpp
4+
* @author Gaspard Kirira
5+
*
6+
* Copyright 2025, Gaspard Kirira. All rights reserved.
7+
* https://github.com/vixcpp/vix
8+
* Use of this source code is governed by a MIT license
9+
* that can be found in the License file.
10+
*
11+
* Vix.cpp
12+
*
13+
*/
14+
#include <vix/cli/errors/runtime/IRuntimeErrorRule.hpp>
15+
#include <vix/cli/errors/runtime/RuntimeRuleUtils.hpp>
16+
17+
#include <filesystem>
18+
#include <iostream>
19+
#include <memory>
20+
#include <string>
21+
#include <vector>
22+
23+
#include <vix/cli/Style.hpp>
24+
25+
using namespace vix::cli::style;
26+
27+
namespace vix::cli::errors::runtime
28+
{
29+
class DetachedThreadLifetimeRule final : public IRuntimeErrorRule
30+
{
31+
public:
32+
bool match(
33+
const std::string &log,
34+
const std::filesystem::path &sourceFile) const override
35+
{
36+
(void)sourceFile;
37+
38+
return (icontains(log, "detach") &&
39+
(icontains(log, "use-after-free") ||
40+
icontains(log, "stack-use-after-scope") ||
41+
icontains(log, "use-after-return"))) ||
42+
(icontains(log, "detached thread") &&
43+
(icontains(log, "dangling") ||
44+
icontains(log, "invalid memory") ||
45+
icontains(log, "lifetime"))) ||
46+
(icontains(log, "lambda") &&
47+
icontains(log, "reference") &&
48+
icontains(log, "detach"));
49+
}
50+
51+
bool handle(
52+
const std::string &log,
53+
const std::filesystem::path &sourceFile) const override
54+
{
55+
std::string title = "runtime error: detached thread lifetime bug";
56+
std::vector<std::string> hints = {
57+
"a detached thread likely outlived data it was still using",
58+
"avoid capturing local variables by reference in detached threads and ensure shared objects outlive the thread",
59+
};
60+
61+
if (icontains(log, "stack-use-after-scope") ||
62+
icontains(log, "use-after-return"))
63+
{
64+
title = "runtime error: detached thread captured expired stack data";
65+
hints = {
66+
"a detached thread likely kept a reference or pointer to a local variable after the scope ended",
67+
"capture by value, use shared ownership when needed, or avoid detach() for work tied to local lifetime",
68+
};
69+
}
70+
else if (icontains(log, "use-after-free"))
71+
{
72+
title = "runtime error: detached thread used freed memory";
73+
hints = {
74+
"a detached thread likely accessed memory after it had already been released",
75+
"ensure detached work owns its data safely, or join the thread before destroying shared state",
76+
};
77+
}
78+
79+
std::cerr << RED
80+
<< title
81+
<< RESET << "\n";
82+
83+
print_runtime_hints_and_at(
84+
hints,
85+
!sourceFile.empty() ? ("source: " + sourceFile.filename().string()) : "");
86+
87+
return true;
88+
}
89+
};
90+
91+
std::unique_ptr<IRuntimeErrorRule> makeDetachedThreadLifetimeRule()
92+
{
93+
return std::make_unique<DetachedThreadLifetimeRule>();
94+
}
95+
} // namespace vix::cli::errors::runtime

0 commit comments

Comments
 (0)