You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

822 lines
24 KiB

/*
* Copyright 2002-2019 Intel Corporation.
*
* This software is provided to you as Sample Source Code as defined in the accompanying
* End User License Agreement for the Intel(R) Software Development Products ("Agreement")
* section 1.L.
*
* This software and the related documents are provided as is, with no express or implied
* warranties, other than those that are expressly stated in the License.
*/
/*
* This is a test framework for testing the locking primitives. Use the
* "-test" knob to select a specific test. This tool is meant to be
* run with "mt-worker-posix.cpp" or "mt-worker-windows.cpp".
*/
#include <iostream>
#include "pin.H"
#include "atomic.hpp"
#include <set>
using std::set;
// The test continues until all threads have run at least this many iterations
// trying to acquire / release the lock.
//
#if defined(TARGET_WINDOWS)
static const unsigned long ITERATION_COUNT = 1000;
#else
static const unsigned long ITERATION_COUNT = 100000;
#endif
// Some of the tests hold a lock over a short delay loop. This is the number
// of iterations in that delay loop.
//
static const unsigned DELAY_COUNT = 16;
KNOB<std::string> KnobTest(KNOB_MODE_WRITEONCE, "pintool",
"test", "", "Name of the test to run [lock-integrity | lock-stress | "
"mutex-integrity | mutex-stress mutex-trystress | "
"writer-integrity | writer-stress | writer-trystress | "
"reader-stress | reader-trystress | "
"rw-integrity | rw-stress | rw-trystress | "
"semaphore | trylocks].");
enum TEST
{
TEST_NONE,
TEST_INVALID,
TEST_LOCK_INTEGRITY,
TEST_LOCK_STRESS,
TEST_MUTEX_INTEGRITY,
TEST_MUTEX_STRESS,
TEST_MUTEX_TRYSTRESS,
TEST_WRITER_INTEGRITY,
TEST_WRITER_STRESS,
TEST_WRITER_TRYSTRESS,
TEST_READER_STRESS,
TEST_READER_TRYSTRESS,
TEST_RW_INTEGRITY,
TEST_RW_STRESS,
TEST_RW_TRYSTRESS,
TEST_SEMAPHORE,
TEST_TRYLOCKS
};
TEST TestType;
// The lock variables that we test.
//
PIN_LOCK Lock;
PIN_MUTEX Mutex;
PIN_RWMUTEX RWMutex;
PIN_SEMAPHORE Sem1;
PIN_SEMAPHORE Sem2;
PIN_LOCK pinLock;
THREADID HasLock = INVALID_THREADID;
REG RegThreadInfo;
BOOL FoundTestFunc = FALSE;
BOOL FoundThreadCountFunc = FALSE;
volatile int RunningWorkers = 0;
volatile BOOL AllExit = FALSE;
volatile int ActiveReaders = 0;
volatile BOOL IsActiveWriter = FALSE;
struct THREAD_INFO
{
THREAD_INFO(unsigned id) : _workerId(id), _count(0) {}
unsigned _workerId;
UINT64 _count;
};
static TEST GetTestType(const std::string &);
static VOID OnThreadFini(THREADID, const CONTEXT *, INT32, VOID *);
static void InstrumentRtn(RTN ins, VOID *);
static void OnExit(INT32, VOID *);
static void GetThreadCount(ADDRINT);
static void DoTestLockIntegrity(THREADID, THREAD_INFO *, UINT32 *);
static void DoTestLockStress(THREADID, THREAD_INFO *, UINT32 *);
static void DoTestMutexIntegrity(THREADID, THREAD_INFO *, UINT32 *);
static void DoTestMutexStress(THREAD_INFO *, UINT32 *);
static void DoTestMutexTryStress(THREAD_INFO *, UINT32 *);
static void DoTestWriterIntegrity(THREADID, THREAD_INFO *, UINT32 *);
static void DoTestWriterStress(THREAD_INFO *, UINT32 *);
static void DoTestWriterTryStress(THREAD_INFO *, UINT32 *);
static void DoTestReaderStress(THREAD_INFO *, UINT32 *);
static void DoTestReaderTryStress(THREAD_INFO *, UINT32 *);
static void DoTestReaderWriterIntegrity(THREAD_INFO *, UINT32 *);
static void DoTestReaderWriterStress(THREAD_INFO *, UINT32 *);
static void DoTestReaderWriterTryStress(THREAD_INFO *, UINT32 *);
static void DoTestSemaphore(THREAD_INFO *, UINT32 *);
static void DoTestTryLocks(UINT32 *);
static bool CheckIfDone(THREAD_INFO *, UINT32 *);
int main(int argc, char * argv[])
{
PIN_Init(argc, argv);
PIN_InitSymbols();
PIN_InitLock(&pinLock);
RegThreadInfo = PIN_ClaimToolRegister();
if (RegThreadInfo == REG_INVALID())
{
std::cout << "Out of tool registers" << std::endl;
PIN_ExitProcess(1);
}
// Get the test type and initialize the corresponding lock variable.
//
TestType = GetTestType(KnobTest.Value());
switch (TestType)
{
case TEST_NONE:
std::cout << "Must specify a test to run with the '-test' knob" << std::endl;
PIN_ExitProcess(1);
break;
case TEST_INVALID:
std::cout << "Invalid test name: " << KnobTest.Value() << std::endl;
PIN_ExitProcess(1);
break;
case TEST_LOCK_INTEGRITY:
case TEST_LOCK_STRESS:
PIN_InitLock(&Lock);
break;
case TEST_MUTEX_INTEGRITY:
case TEST_MUTEX_STRESS:
case TEST_MUTEX_TRYSTRESS:
PIN_MutexInit(&Mutex);
break;
case TEST_WRITER_INTEGRITY:
case TEST_WRITER_STRESS:
case TEST_WRITER_TRYSTRESS:
case TEST_READER_STRESS:
case TEST_READER_TRYSTRESS:
case TEST_RW_INTEGRITY:
case TEST_RW_STRESS:
case TEST_RW_TRYSTRESS:
PIN_RWMutexInit(&RWMutex);
break;
case TEST_SEMAPHORE:
PIN_SemaphoreInit(&Sem1);
PIN_SemaphoreInit(&Sem2);
PIN_SemaphoreSet(&Sem1);
PIN_MutexInit(&Mutex);
break;
case TEST_TRYLOCKS:
PIN_MutexInit(&Mutex);
PIN_RWMutexInit(&RWMutex);
PIN_SemaphoreInit(&Sem1);
break;
default:
ASSERTX(0);
}
PIN_AddThreadFiniFunction(OnThreadFini, NULL);
RTN_AddInstrumentFunction(InstrumentRtn, NULL);
PIN_AddFiniFunction(OnExit, NULL);
PIN_StartProgram();
return 1;
}
static TEST GetTestType(const std::string &name)
{
if (name == "")
return TEST_NONE;
if (name == "lock-integrity")
return TEST_LOCK_INTEGRITY;
if (name == "lock-stress")
return TEST_LOCK_STRESS;
if (name == "mutex-integrity")
return TEST_MUTEX_INTEGRITY;
if (name == "mutex-stress")
return TEST_MUTEX_STRESS;
if (name == "mutex-trystress")
return TEST_MUTEX_TRYSTRESS;
if (name == "writer-integrity")
return TEST_WRITER_INTEGRITY;
if (name == "writer-stress")
return TEST_WRITER_STRESS;
if (name == "writer-trystress")
return TEST_WRITER_TRYSTRESS;
if (name == "reader-stress")
return TEST_READER_STRESS;
if (name == "reader-trystress")
return TEST_READER_TRYSTRESS;
if (name == "rw-integrity")
return TEST_RW_INTEGRITY;
if (name == "rw-stress")
return TEST_RW_STRESS;
if (name == "rw-trystress")
return TEST_RW_TRYSTRESS;
if (name == "semaphore")
return TEST_SEMAPHORE;
if (name == "trylocks")
return TEST_TRYLOCKS;
return TEST_INVALID;
}
set<THREADID> appThreads;
static VOID AppThreadStart(THREADID tid, PIN_REGISTER* regval)
{
// Give each worker thread a unique, contiguous ID.
//
static unsigned workerCount = 0;
regval->qword[0] = (UINT64)(reinterpret_cast<ADDRINT>(new THREAD_INFO(workerCount++)));
PIN_GetLock(&pinLock, PIN_GetTid());
appThreads.insert(tid);
PIN_ReleaseLock(&pinLock);
}
static VOID OnThreadFini(THREADID tid, const CONTEXT *ctxt, INT32, VOID *)
{
if (appThreads.find(tid) != appThreads.end())
{
appThreads.erase(appThreads.find(tid));
ADDRINT addrInfo = PIN_GetContextReg(ctxt, RegThreadInfo);
THREAD_INFO *info = reinterpret_cast<THREAD_INFO *>(addrInfo);
delete info;
}
}
static void InstrumentRtn(RTN rtn, VOID *)
{
if (RTN_Name(rtn) == "TellPinThreadStart" || RTN_Name(rtn) == "_TellPinThreadStart")
{
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(AppThreadStart),
IARG_THREAD_ID, IARG_REG_REFERENCE, RegThreadInfo,
IARG_END);
RTN_Close(rtn);
}
if (RTN_Name(rtn) == "TellPinThreadCount" || RTN_Name(rtn) == "_TellPinThreadCount")
{
FoundThreadCountFunc = TRUE;
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(GetThreadCount),
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
RTN_Close(rtn);
}
if (RTN_Name(rtn) == "InstrumentedWithPin" || RTN_Name(rtn) == "_InstrumentedWithPin")
{
FoundTestFunc = TRUE;
RTN_Open(rtn);
switch (TestType)
{
case TEST_LOCK_INTEGRITY:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestLockIntegrity),
IARG_THREAD_ID,
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_LOCK_STRESS:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestLockStress),
IARG_THREAD_ID,
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_MUTEX_INTEGRITY:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestMutexIntegrity),
IARG_THREAD_ID,
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_MUTEX_STRESS:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestMutexStress),
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_MUTEX_TRYSTRESS:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestMutexTryStress),
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_WRITER_INTEGRITY:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestWriterIntegrity),
IARG_THREAD_ID,
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_WRITER_STRESS:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestWriterStress),
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_WRITER_TRYSTRESS:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestWriterTryStress),
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_READER_STRESS:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestReaderStress),
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_READER_TRYSTRESS:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestReaderTryStress),
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_RW_INTEGRITY:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestReaderWriterIntegrity),
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_RW_STRESS:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestReaderWriterStress),
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_RW_TRYSTRESS:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestReaderWriterTryStress),
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_SEMAPHORE:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestSemaphore),
IARG_REG_VALUE, RegThreadInfo,
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
case TEST_TRYLOCKS:
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(DoTestTryLocks),
IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
IARG_END);
break;
default:
ASSERTX(0);
}
RTN_Close(rtn);
}
}
static void OnExit(INT32, VOID *)
{
if (!FoundTestFunc || !FoundThreadCountFunc)
{
std::cout << "Couldn't find instrumentation routine(s)" << std::endl;
PIN_ExitProcess(1);
}
// Destroy the lock variable.
//
switch (TestType)
{
case TEST_LOCK_INTEGRITY:
case TEST_LOCK_STRESS:
/* nothing to do */
break;
case TEST_MUTEX_INTEGRITY:
case TEST_MUTEX_STRESS:
case TEST_MUTEX_TRYSTRESS:
break;
case TEST_WRITER_INTEGRITY:
case TEST_WRITER_STRESS:
case TEST_WRITER_TRYSTRESS:
case TEST_READER_STRESS:
case TEST_READER_TRYSTRESS:
case TEST_RW_INTEGRITY:
case TEST_RW_STRESS:
case TEST_RW_TRYSTRESS:
break;
case TEST_SEMAPHORE:
PIN_SemaphoreFini(&Sem1);
PIN_SemaphoreFini(&Sem2);
break;
case TEST_TRYLOCKS:
PIN_SemaphoreFini(&Sem1);
break;
default:
ASSERTX(0);
}
}
static void GetThreadCount(ADDRINT threadCount)
{
// Capture the number of worker threads in the application.
// This is the number of threads that will execute DoLockTest().
//
RunningWorkers = threadCount;
if (TestType == TEST_SEMAPHORE)
{
if (threadCount != 2)
{
std::cout << "Test 'semaphore' requires exactly two worker threads" << std::endl;
PIN_ExitProcess(1);
}
}
if (TestType == TEST_TRYLOCKS)
{
if (threadCount != 1)
{
std::cout << "Test 'trylocks' requires exactly one worker thread" << std::endl;
PIN_ExitProcess(1);
}
}
}
// ----------------- Analysis routines that peform each test ----------------- //
static void DoTestLockIntegrity(THREADID tid, THREAD_INFO *info, UINT32 *done)
{
// This test checks to see if two threads can be in the PIN_LOCK mutex
// simultaneously.
PIN_GetLock(&Lock, tid);
THREADID owner = HasLock;
HasLock = tid;
if (owner != INVALID_THREADID)
{
std::cout << "Two theads in lock simultaneously: " << std::dec << tid <<
" and " << owner << std::endl;
PIN_ExitProcess(1);
}
ATOMIC::OPS::Delay(DELAY_COUNT);
HasLock = INVALID_THREADID;
THREADID ret = PIN_ReleaseLock(&Lock);
if (ret != tid)
{
std::cout << "PIN_ReleaseLock returned unexpected value " << std::dec << ret <<
" (expected " << tid << ")" << std::endl;
PIN_ExitProcess(1);
}
CheckIfDone(info, done);
}
static void DoTestLockStress(THREADID tid, THREAD_INFO *info, UINT32 *done)
{
// This test just tries to acquire and release PIN_LOCK as fast as possible
// to see if we can provoke a deadlock due to missing a wakeup.
PIN_GetLock(&Lock, tid);
PIN_ReleaseLock(&Lock);
CheckIfDone(info, done);
}
static void DoTestMutexIntegrity(THREADID tid, THREAD_INFO *info, UINT32 *done)
{
// This test checks to see if two threads can be in the PIN_MUTEX mutex
// simultaneously.
PIN_MutexLock(&Mutex);
THREADID owner = HasLock;
HasLock = tid;
if (owner != INVALID_THREADID)
{
std::cout << "Two theads in mutex simultaneously: " << std::dec << tid <<
" and " << owner << std::endl;
PIN_ExitProcess(1);
}
ATOMIC::OPS::Delay(DELAY_COUNT);
HasLock = INVALID_THREADID;
PIN_MutexUnlock(&Mutex);
CheckIfDone(info, done);
}
static void DoTestMutexStress(THREAD_INFO *info, UINT32 *done)
{
// This test just tries to acquire and release PIN_MUTEX as fast as possible
// to see if we can provoke a deadlock due to missing a wakeup.
PIN_MutexLock(&Mutex);
PIN_MutexUnlock(&Mutex);
CheckIfDone(info, done);
}
static void DoTestMutexTryStress(THREAD_INFO *info, UINT32 *done)
{
// Try to acquire / release PIN_MUTEX as fast as possible, using "try".
if (PIN_MutexTryLock(&Mutex))
PIN_MutexUnlock(&Mutex);
CheckIfDone(info, done);
}
static void DoTestWriterIntegrity(THREADID tid, THREAD_INFO *info, UINT32 *done)
{
// This test checks to see if two writer threads can be in the PIN_RWMUTEX mutex
// simultaneously.
PIN_RWMutexWriteLock(&RWMutex);
THREADID owner = HasLock;
HasLock = tid;
if (owner != INVALID_THREADID)
{
std::cout << "Two writer theads in rwmutex simultaneously: " << std::dec << tid <<
" and " << owner << std::endl;
PIN_ExitProcess(1);
}
ATOMIC::OPS::Delay(DELAY_COUNT);
HasLock = INVALID_THREADID;
PIN_RWMutexUnlock(&RWMutex);
CheckIfDone(info, done);
}
static void DoTestWriterStress(THREAD_INFO *info, UINT32 *done)
{
// This test just tries to acquire and release PIN_RWMUTEX as fast as possible
// as a writer lock to see if we can provoke a deadlock due to missing a wakeup.
PIN_RWMutexWriteLock(&RWMutex);
PIN_RWMutexUnlock(&RWMutex);
CheckIfDone(info, done);
}
static void DoTestWriterTryStress(THREAD_INFO *info, UINT32 *done)
{
// Try to acquire / release PIN_RWMUTEX as a writer using "try" as fast as possible.
if (PIN_RWMutexTryWriteLock(&RWMutex))
PIN_RWMutexUnlock(&RWMutex);
CheckIfDone(info, done);
}
static void DoTestReaderStress(THREAD_INFO *info, UINT32 *done)
{
// This test just tries to acquire and release PIN_RWMUTEX as fast as possible
// as a reader lock.
PIN_RWMutexReadLock(&RWMutex);
PIN_RWMutexUnlock(&RWMutex);
CheckIfDone(info, done);
}
static void DoTestReaderTryStress(THREAD_INFO *info, UINT32 *done)
{
// Try to acquire / release PIN_RWMUTEX as a reader using "try" as fast as possible.
if (PIN_RWMutexTryReadLock(&RWMutex))
PIN_RWMutexUnlock(&RWMutex);
CheckIfDone(info, done);
}
static void DoTestReaderWriterIntegrity(THREAD_INFO *info, UINT32 *done)
{
// This test checks that a "writer" thread can never hold the lock while
// there is an active reader.
if (info->_workerId & 1)
{
// Reader thread.
//
PIN_RWMutexReadLock(&RWMutex);
ATOMIC::OPS::Increment(&ActiveReaders, 1);
if (ATOMIC::OPS::Load(&IsActiveWriter))
{
std::cout << "Reader got lock while there is an active writer" << std::endl;
PIN_ExitProcess(1);
}
ATOMIC::OPS::Delay(DELAY_COUNT);
ATOMIC::OPS::Increment(&ActiveReaders, -1);
PIN_RWMutexUnlock(&RWMutex);
}
else
{
// Writer thread.
//
PIN_RWMutexWriteLock(&RWMutex);
ATOMIC::OPS::Store<BOOL>(&IsActiveWriter, TRUE);
if (ATOMIC::OPS::Load(&ActiveReaders) != 0)
{
std::cout << "Writer has lock while there are active readers" << std::endl;
PIN_ExitProcess(1);
}
ATOMIC::OPS::Delay(DELAY_COUNT);
ATOMIC::OPS::Store<BOOL>(&IsActiveWriter, FALSE);
PIN_RWMutexUnlock(&RWMutex);
}
CheckIfDone(info, done);
}
static void DoTestReaderWriterStress(THREAD_INFO *info, UINT32 *done)
{
// This test uses a mix of "reader" and "writer" threads to acquire and
// release the PIN_RWMUTEX as fast as possible.
if (info->_workerId & 1)
{
// Reader thread.
//
PIN_RWMutexReadLock(&RWMutex);
PIN_RWMutexUnlock(&RWMutex);
}
else
{
// Writer thread.
//
PIN_RWMutexWriteLock(&RWMutex);
PIN_RWMutexUnlock(&RWMutex);
}
CheckIfDone(info, done);
}
static void DoTestReaderWriterTryStress(THREAD_INFO *info, UINT32 *done)
{
// This test uses a mix of "reader" and "writer" threads to acquire and
// release the PIN_RWMUTEX as fast as possible. We acquire the lock using
// "try".
if (info->_workerId & 1)
{
// Reader thread.
//
if (PIN_RWMutexTryReadLock(&RWMutex))
PIN_RWMutexUnlock(&RWMutex);
}
else
{
// Writer thread.
//
if (PIN_RWMutexTryWriteLock(&RWMutex))
PIN_RWMutexUnlock(&RWMutex);
}
CheckIfDone(info, done);
}
static void DoTestSemaphore(THREAD_INFO *info, UINT32 *done)
{
// This test assumes exactly two threads. The two threads take turns pinging
// each other's semaphore. We make sure that a thread does not wake up from
// the semaphore unless it is set. We also check for deadlocks due to missing
// wakeups.
if (info->_workerId == 0)
{
PIN_SemaphoreWait(&Sem1);
if (!PIN_SemaphoreIsSet(&Sem1))
{
std::cout << "SemaphoreWait returned, but semaphore is not set" << std::endl;
PIN_ExitProcess(1);
}
PIN_MutexLock(&Mutex);
PIN_SemaphoreClear(&Sem1);
PIN_SemaphoreSet(&Sem2);
PIN_MutexUnlock(&Mutex);
}
else
{
PIN_SemaphoreWait(&Sem2);
if (!PIN_SemaphoreIsSet(&Sem2))
{
std::cout << "SemaphoreWait returned, but semaphore is not set" << std::endl;
PIN_ExitProcess(1);
}
PIN_MutexLock(&Mutex);
PIN_SemaphoreClear(&Sem2);
PIN_SemaphoreSet(&Sem1);
PIN_MutexUnlock(&Mutex);
}
if (CheckIfDone(info, done))
{
PIN_SemaphoreSet(&Sem1);
PIN_SemaphoreSet(&Sem2);
}
}
static void DoTestTryLocks(UINT32 *done)
{
// This is a collection of single-thread tests that make sure that the
// various "try" operations succeed or fail as expected.
BOOL gotLock = PIN_MutexTryLock(&Mutex);
if (!gotLock)
{
std::cout << "Failure on uncontended PIN_MutexTryLock" << std::endl;
PIN_ExitProcess(1);
}
gotLock = PIN_MutexTryLock(&Mutex);
if (gotLock)
{
std::cout << "PIN_MutexTryLock was able to get a lock twice" << std::endl;
PIN_ExitProcess(1);
}
gotLock = PIN_RWMutexTryWriteLock(&RWMutex);
if (!gotLock)
{
std::cout << "Failure on uncontended PIN_RWMutexTryWriteLock" << std::endl;
PIN_ExitProcess(1);
}
gotLock = PIN_RWMutexTryWriteLock(&RWMutex);
if (gotLock)
{
std::cout << "PIN_RWMutexTryWriteLock was able to get a lock twice" << std::endl;
PIN_ExitProcess(1);
}
gotLock = PIN_RWMutexTryReadLock(&RWMutex);
if (gotLock)
{
std::cout << "PIN_RWMutexTryReadLock was able to get a lock when writer owns it" << std::endl;
PIN_ExitProcess(1);
}
PIN_RWMutexUnlock(&RWMutex);
gotLock = PIN_RWMutexTryReadLock(&RWMutex);
if (!gotLock)
{
std::cout << "Failure on uncontended PIN_RWMutexTryReadLock" << std::endl;
PIN_ExitProcess(1);
}
gotLock = PIN_RWMutexTryReadLock(&RWMutex);
if (!gotLock)
{
std::cout << "Unable to get a reader lock twice" << std::endl;
PIN_ExitProcess(1);
}
PIN_SemaphoreSet(&Sem1);
if (!PIN_SemaphoreIsSet(&Sem1))
{
std::cout << "Expected 'set' status from PIN_SemaphoreIsSet" << std::endl;
PIN_ExitProcess(1);
}
PIN_SemaphoreClear(&Sem1);
if (PIN_SemaphoreIsSet(&Sem1))
{
std::cout << "Expected 'clear' status from PIN_SemaphoreIsSet" << std::endl;
PIN_ExitProcess(1);
}
if (PIN_SemaphoreTimedWait(&Sem1, 1))
{
std::cout << "Expected PIN_SemaphoreTimedWait to time-out" << std::endl;
PIN_ExitProcess(1);
}
*done = 1;
}
// Common routine to check if the test is done.
//
static bool CheckIfDone(THREAD_INFO *info, UINT32 *done)
{
// Each thread exits after all threads have reached their iteration count.
//
if (info->_count++ == ITERATION_COUNT)
{
if (ATOMIC::OPS::Increment(&RunningWorkers, -1) <= 1)
ATOMIC::OPS::Store<BOOL>(&AllExit, TRUE);
}
if (ATOMIC::OPS::Load(&AllExit))
{
*done = 1;
return true;
}
return false;
}