我有一个使用堆内存的函数,如果在同一个函数的另一个实例完成之前调用它,那么它将出现严重错误。 如何在编译时防止这种情况发生?
我有一个使用堆内存的函数,如果在同一个函数的另一个实例完成之前调用它,那么它将出现严重错误。 如何在编译时防止这种情况发生?
bool MyFnSimple()
{
static bool entered = false;
if( entered )
{
cout << "Re-entered function!" << endl;
return false;
}
entered = true;
// ...
entered = false;
return true;
}
bool MyFnCritSecBlocking()
{
static HANDLE cs = CreateMutex(0, 0, 0);
WaitForSingleObject(cs, INFINITE);
// ... do stuff
ReleaseMutex(cs);
return true;
}
bool MyFnCritSecNonBlocking()
{
static HANDLE cs = CreateMutex(0, 0, 0);
DWORD ret = WaitForSingleObject(cs, 0);
if( WAIT_TIMEOUT == ret )
return false; // someone's already in here
// ... do stuff
ReleaseMutex(cs);
return true;
}
除了使用静态布尔值和critsecs,可能有无限种方法来解决这个问题。其中一个方法是将本地值与Windows中的Interlocked函数之一进行测试的组合:
bool MyFnInterlocked()
{
static LONG volatile entered = 0;
LONG ret = InterlockedCompareExchange(&entered, 1, 0);
if( ret == 1 )
return false; // someone's already in here
// ... do stuff
InterlockedExchange(&entered, 0);
return false;
}
#include <windows.h>
#include <cstdlib>
#include <stdexcept>
#include <iostream>
class CritSecLock
{
public:
CritSecLock(HANDLE cs) : cs_(cs)
{
DWORD ret = WaitForSingleObject(cs_, INFINITE);
if( ret != WAIT_OBJECT_0 )
throw std::runtime_error("Unable To Acquire Mutex");
std::cout << "Locked" << std::endl;
}
~CritSecLock()
{
std::cout << "Unlocked" << std::endl;
ReleaseMutex(cs_);
}
private:
HANDLE cs_;
};
bool MyFnPrimitiveRAII()
{
static HANDLE cs = CreateMutex(0, 0, 0);
try
{
CritSecLock lock(cs);
// ... do stuff
throw std::runtime_error("kerflewy!");
return true;
}
catch(...)
{
// something went wrong
// either with the CritSecLock instantiation
// or with the 'do stuff' code
std::cout << "ErrorDetected" << std::endl;
return false;
}
}
int main()
{
MyFnPrimitiveRAII();
return 0;
}
如果没有进行静态分析,你无法在编译时完成这项操作。以下是一个安全的递归断言:
#include <cassert>
class simple_lock
{
public:
simple_lock(bool& pLock):
mLock(pLock)
{
assert(!mLock && "recursive call");
mLock = true;
}
~simple_lock(void)
{
mLock = false;
}
private:
simple_lock(const simple_lock&);
simple_lock& operator=(const simple_lock&);
bool& mLock;
};
#define ASSERT_RECURSION static bool _lockFlag = false; \
simple_lock _lock(_lockFlag)
void foo(void)
{
ASSERT_RECURSION;
foo();
}
int main(void)
{
foo();
//foo();
}
如果没有某种静态分析器,您无法在编译时完成此操作。但是,可以进行简单的运行时检查:
注意:为了防止多线程并发但非递归调用,您需要更加健壮的解决方案。
void myFunc() {
static int locked = 0;
if (locked++)
{
printf("recursion detected\n!");
}
....
locked--;
}
.c
或.cc
文件中,而不是头文件中。在任何图灵完备的语言中,这个问题是不可判定的。虽然我无法证明它,但我知道。
void f() {if (!recurses(f)) f();}
,仅当它不会递归时才递归。 - Mike Seymourc++-faq-lite在类似情况下提供了一些不错的建议:写下你预期在处理这样的事情时会出现问题的注释:
// We'll fire you if you try recursion here
我还没有查看建议是否也适用于递归
函数调用堆栈是在运行时创建的,编译时只能检查函数本身是否递归,即它是否调用自己?
这个可以编译
#include <stdio.h>
int testFunc() {
#define testFunc
printf("Ok\n");
}
#undef testFunc
int main() { testFunc(); }
这不是
#include <stdio.h>
int testFunc() {
#define testFunc
printf("Ok\n");
testFunc();
}
#undef testFunc
int main() { testFunc(); }
test.c:7: error: expected expression before ‘)’ token
它也适用于多功能递归:#include <stdio.h>
int testFunc1() {
#define testFunc1
printf("1\n");
testFunc2();
}
int testFunc2() {
#define testFunc2
printf("2\n");
//uncomment to cause error: `test.c:13: error: expected expression before ‘)’ token`
//testFunc1();
}
#undef testFunc1
#undef testFunc2
int main() { testFunc1(); }
你无法在编译时完成这个任务,你需要跟踪整个应用程序的完整控制流程才能实现它(如果函数调用另一个函数,该函数又调用另一个函数,再调用原始函数...)。
改为在思考时间执行。在函数文档中添加大量注释。此外,也可以使用其他响应中提供的运行时解决方案之一-用于调试版本。
再补充一个:使用静态互斥锁来保护函数体(boost的scoped_lock会大大简化它)。