我对我在Linux C ++应用程序中使用boost :: mutex对象的方式有疑问。我有一组用于执行各种互斥操作的便捷宏,例如我有一些宏可以返回任何线程是否锁定互斥锁,无论是否已经被调用线程专门锁定,还有其他几个。需要知道当前锁定互斥锁的线程(如果有)的宏如下:
// Determine whether or not a boost mutex is already locked
#define IsBoostMutexLocked(m) ((m.native_handle()->__data.__owner) != 0)
// Determine whether or not a boost mutex is already locked by the calling thread
#define IsBoostMutexLockedByCallingThread(m) ((m.native_handle()->__data.__owner) == (syscall(SYS_gettid)))
但我开始怀疑直接读取__owner int字段是否安全。当另一个线程忙于锁定或解锁互斥锁时,一个线程是否可以不尝试读取__owner字段,从而写入__owner字段?
因此,我设计了一个测试,试图暴露任何数据竞争漏洞并在检测到这种情况时中止。到目前为止,我有100个线程同时锁定,解锁和读取一个全局互斥锁的__owner,每个线程进行数千万次循环迭代,而不是曾经读过一个无效的__owner值。下面我已经包含了整个测试代码。坦率地说,我很惊讶我从来没有读过一个糟糕的__owner值。
有人可以向我解释为什么即使其他线程试图锁定/解锁,直接读取互斥锁的__owner(显然)也是安全的吗?提前谢谢!
// The test mutex
boost::mutex g_TestMutex;
// The number of threads to launch for the test
#define NUM_THREADS_TO_LAUNCH 100
// The thread IDs of all test threads
long int g_AllSpecialThreadsTIDs[NUM_THREADS_TO_LAUNCH];
// Whether or not each test thread is ready to begin the test
std::atomic<bool> g_bEachTestThreadIsReadyToBegin[NUM_THREADS_TO_LAUNCH];
// Whether or not the test is ready to begin
std::atomic<bool> g_bTestReadyToBegin(false);
// A structure that encapsulates data to be passed to each test thread
typedef struct {
long *pStoreTIDLoc; // A pointer to the variable at which to store the thread ID
std::atomic<bool> *pTIDStoredLoc; // A pointer to the variable at which to store the status of whether or not the thread ID has been set
} TestThreadDataStructure;
// Ensure that a test thread ID is valid
void AssertIsValidTID(int iTID)
{
// Whether or not this thread ID is valid
bool bValid = false;
// If the thread ID indicates that no-one has locked the mutex
if (iTID == 0)
{
// A thread ID indicating that no-one has locked the mutex is always valid
bValid = true;
}
// Or, if this is a non-zero thread ID
else
{
// For each test thread
for (int i = 0; i < NUM_THREADS_TO_LAUNCH; i++)
{
// If this is a thread ID match
if (iTID == static_cast<int>(g_AllSpecialThreadsTIDs[i]))
{
// Set that the incoming thread ID is valid
bValid = true;
// Stop looking
break;
}
}
}
// If the incoming thread ID is invalid
if (!bValid)
{
// The test has failed
abort();
}
}
// Each test thread
void TestMutexTesterThread(void *pArg)
{
// Each mutex owner thread ID
int iOwner = 0;
// Unpack the incoming data structure
TestThreadDataStructure *pStruct = ((TestThreadDataStructure *)pArg);
long int *pStoreHere = pStruct->pStoreTIDLoc;
std::atomic<bool> *pTIDStoredLoc = pStruct->pTIDStoredLoc;
// Clean up
delete pStruct;
pStruct = NULL;
pArg = NULL;
// Get this thread ID
const long int lThisTID = syscall(SYS_gettid);
// Store this thread ID
(*pStoreHere) = lThisTID;
// Set that we have finished storing the thread ID
pTIDStoredLoc->store(true);
// While we are waiting for everything to be ready so that we can begin the test
while (true)
{
// If we are now ready to begin the test
if (g_bTestReadyToBegin.load())
{
// Stop waiting
break;
}
}
// The loop iteration count
uint64_t uCount = 0;
// For the life of the test, i.e. forever
while (true)
{
// Increment the count
uCount++;
// If we are about to go over the edge
if (uCount >= (UINT64_MAX - 1))
{
// Reset the count
uCount = 0;
}
// Every so often
if ((uCount % 500000) == 0)
{
// Print our progress
printf("Thread %05ld: uCount = %lu\n", lThisTID, uCount);
}
// Get the mutex owner's thread ID
iOwner = g_TestMutex.native_handle()->__data.__owner;
// Ensure that this is a valid thread ID
AssertIsValidTID(iOwner);
// Lock the mutex as part of the test
g_TestMutex.lock();
// Get the mutex owner's thread ID
iOwner = g_TestMutex.native_handle()->__data.__owner;
// Ensure that this is a valid thread ID
AssertIsValidTID(iOwner);
// Unlock the mutex as part of the test
g_TestMutex.unlock();
// Get the mutex owner's thread ID
iOwner = g_TestMutex.native_handle()->__data.__owner;
// Ensure that this is a valid thread ID
AssertIsValidTID(iOwner);
}
}
// Start the test
void StartTest()
{
// For each thread to launch
for (int i = 0; i < NUM_THREADS_TO_LAUNCH; i++)
{
// Initialize that we do not have a thread ID yet
g_AllSpecialThreadsTIDs[i] = 0;
g_bEachTestThreadIsReadyToBegin[i].store(false);
}
// For each thread to launch
for (int i = 0; i < NUM_THREADS_TO_LAUNCH; i++)
{
// Allocate a data structure with which to pass data to each thread
TestThreadDataStructure *pDataStruct = new TestThreadDataStructure;
// Store the location at which the thread should place its thread ID
pDataStruct->pStoreTIDLoc = ((long int *)((&(g_AllSpecialThreadsTIDs[i]))));
// Store the location of the atomic variable that each thread should set to true when it has finished storing its thread ID
pDataStruct->pTIDStoredLoc = ((std::atomic<bool> *)((&(g_bEachTestThreadIsReadyToBegin[i]))));
// The thread to return
boost::thread *pNewThread = NULL;
// Launch the new thread
try { pNewThread = new boost::thread(TestMutexTesterThread, pDataStruct); }
// Catch errors
catch (boost::thread_resource_error &ResourceError)
{
// Print this error
printf("boost::thread construction error: '%s'", ResourceError.what());
// This is a fatal error
abort();
}
// Clean up
delete pNewThread;
pNewThread = NULL;
}
// Whether or not all threads are ready to begin
bool bAllThreadsReadyToBegin = false;
// While we are waiting for all threads to be ready to begin
while (true)
{
// Reset to assuming all threads are ready to begin
bAllThreadsReadyToBegin = true;
// For each thread we launched
for (int i = 0; i < NUM_THREADS_TO_LAUNCH; i++)
{
// If this thread has not yet stored its thread ID
if (g_bEachTestThreadIsReadyToBegin[i].load() == false)
{
// We are not yet ready to begin
bAllThreadsReadyToBegin = false;
// Start over
break;
}
}
// If all threads are ready to begin
if (bAllThreadsReadyToBegin)
{
// We are done waiting
break;
}
}
// Atomically store that all threads are ready to begin and that the test should proceed
g_bTestReadyToBegin.store(true);
}
答案 0 :(得分:-1)
它是'安全的',因为读取和写入最多8个字节,与相应的字节数对齐,是原子的。假设x86_64
我不知道提升,但你的宏看起来很糟糕: