我有一种方法可以修补不是线程安全的复杂结构。通常,这不是问题,因为对该方法的基本上所有调用都应在初始化阶段进行,该阶段应该在单个线程中运行(与我们从main()
开始的线程相同)。 / p>
但是,似乎我的代码库中的某些恶意组件确实在从非主线程调用此方法。我显然可以在此方法中添加互斥锁/锁定防护,以防止潜在的调用,但是,从我的角度来看,这是一种解决方法,而不是我想要的解决方案。
当然,我还可以添加一个运行时断言,例如:
static std::thread::id s_main_thread_id
int main(...) {
s_main_thread_id = std::this_thread::get_id();
// ...
}
void vulnerable_function() {
ASSERT(std::this_thread::get_id() == s_main_thread_id);
}
...但这不是保证。
问题:是否有可能在编译时断言某个方法仅应从某个线程(即主线程)运行?我想在我的代码库中找到流氓调用,并防止这种情况再次发生。
我已经在C ++ 20中浏览了assert-like contracts,但是如果我走在正确的道路上和/或可以应用它。
答案 0 :(得分:2)
您可以做的是构建一些断言宏。使用您的线程库获取线程标识符。在您的代码首次构建该数据结构时,请保存线程ID。然后,在每次输入这些功能时断言它与当前线程ID匹配。
CEF(铬嵌入式框架)库使用此库。
通过像断言一样包装这些宏,预处理器将在设置-DNDEBUG的发行版本中删除它们。
如果您需要更高级的知识,CEF还具有发布和处理消息的功能,以便其他线程可以通过向其发布消息来在UI线程上完成工作。
答案 1 :(得分:1)
...对此方法的所有调用都应在初始化阶段进行...
忘记线程。如果仅在“初始化”期间调用该函数,请确保只能在初始化期间调用该函数:
boolean initialization_is_complete = false;
problematic_function(...) {
ASSERT(! initialization_is_complete);
...
}
initialize_the_system(...) {
...
call_things_that_call_problematic_function(...);
...
initialization_is_complete = true;
}
ASSERT()会在运行时发生,对吧?...我希望某些静态分析工具能够在编译时[防止]发生这种情况。
我不是C ++专家。我知道防止“未经授权”使用任何东西的唯一编译时方法是在编译单元中对类或private
进行声明static
。*如果我可以进行{ {1}}在编译单元中problematic_function()
,其公共符号仅在“初始化”时才有用,这就是我要做的。
另一方面,如果我的开发人员有充分的理由为什么他们想在其他时间从其他地方调用static
,那么我将认真考虑重新设计它,这样就不会有问题了。
*如果您提出一些我没想到的problematic_function()
技巧或某些constexpr
技巧(我不是不是 C ++专家),那么我敢打赌仍然取决于 something 的声明,该声明在初始代码的范围之内,而在所有其他代码的范围之内。
回到您的原始想法,这是为了防止任何“错误的”线程调用该函数:实际上,C ++语言中没有诸如线程之类的东西。 template
数据类型由库提供,并且编译器本身无法得知将执行其转换后的代码的线程的身份。
也就是说,std::thread
不是std::this_thread::get_id()
,并且没有 compile-time 方式可以将其与任何constexpr
的身份进行比较该程序。
也许有一天他们会定义std::thread
...