Does boost::threads + wine threads == happiness?
I would like to use boost threads in my winelib application, but they
need to coexist with threads created using the windows APIs by 3rd party
plugin DLLs. I built a test app that uses the two and they appear to
share both a CRITICAL_SECTION and a boost::mutex fine, so it looks like
I am in the clear. But I don't have alot of confidence in my
understanding of how Wine implements threads, so I thought it would be
wise to ask.
Attached is my test program. To build and run:
$ wineg++ -c boost-win.cpp
$ wineg++ -o boost-win.exe.so boost-win.o -lboost_threads
$ wine ./boost-win.exe.so
Cheers... mo
PS: thanks to whoever is responsible for wineg++. What a timesaver!
boost-win.cpp:
#include "boost/thread.hpp"
#include <pthread.h> // pthread_create
#include <sys/resource.h> // PRIO_PROCESS
#include <stdio.h> // printf
#include <windows.h> // CreateThread
#define INDENT " "
DWORD WINAPI CSTestA(void* arg)
{
CRITICAL_SECTION* cs = (CRITICAL_SECTION*) arg;
puts("in, try CS");
::EnterCriticalSection(cs);
puts("CS locked");
sleep(2);
puts("wake up");
::LeaveCriticalSection(cs);
puts("unlock");
sleep(1);
puts("wake up, try CS");
::EnterCriticalSection(cs);
puts("CS locked");
sleep(1);
puts("wake up");
::LeaveCriticalSection(cs);
puts("unlock");
puts("out");
}
struct CSTestB
{
CSTestB(CRITICAL_SECTION* cs) :
m_criticalSection(cs)
{
}
void operator()()
{
puts(INDENT "in, try CS");
::EnterCriticalSection(m_criticalSection);
puts(INDENT "CS locked");
boost::xtime t;
boost::xtime_get(&t, boost::TIME_UTC);
t.sec += 3;
boost::thread::sleep(t);
puts(INDENT"wake up");
::LeaveCriticalSection(m_criticalSection);
puts(INDENT "unlocked, out");
boost::xtime_get(&t, boost::TIME_UTC);
t.sec += 3;
boost::thread::sleep(t);
puts(INDENT "out");
}
CRITICAL_SECTION* m_criticalSection;
};
struct MutexTestA
{
MutexTestA(boost::mutex* mutex) :
m_mutex(mutex)
{
}
void operator()()
{
puts("in - try mutex");
boost::xtime t;
{ // scope
boost::mutex::scoped_lock lock(*m_mutex);
puts("mutex locked");
boost::xtime_get(&t, boost::TIME_UTC);
t.sec += 2;
boost::thread::sleep(t);
puts("wake up");
}
puts("unlock");
boost::xtime_get(&t, boost::TIME_UTC);
t.sec += 1;
boost::thread::sleep(t);
puts("wake up, try mutex");
{ // scope
boost::mutex::scoped_lock lock(*m_mutex);
puts("mutex locked");
boost::xtime_get(&t, boost::TIME_UTC);
t.sec += 1;
boost::thread::sleep(t);
puts("wake up");
}
puts("unlock");
puts("out");
}
boost::mutex* m_mutex;
};
DWORD WINAPI MutexTestB(void* arg)
{
boost::mutex* mutex = (boost::mutex*) arg;
puts(INDENT "in - try mutex");
{ // scope
boost::mutex::scoped_lock lock(*mutex);
puts(INDENT "mutex locked");
boost::xtime t;
boost::xtime_get(&t, boost::TIME_UTC);
t.sec += 3;
boost::thread::sleep(t);
puts(INDENT "wake up");
}
puts(INDENT "unlock");
puts(INDENT "out");
}
int main(int argc, char *argv[])
{
// boost blocks on CS held by winthread
if (argc > 1 && memcmp(argv[1], "cs", 2) == 0) {
puts("Testing CRITICAL_SECTIONS...\n");
puts(" winThread and boostThread share a CRITICAL_SECTION.
winThread grabs");
puts(" it and sleeps, while boostThread waits for winThread to wake
up and");
puts(" release. Then vice-versa.");
puts("");
puts("winThread boostThread");
puts("----------------------------------");
CRITICAL_SECTION* criticalSection = new CRITICAL_SECTION();
::InitializeCriticalSection(criticalSection);
DWORD junk;
HANDLE winThread = ::CreateThread(NULL, 0, CSTestA, criticalSection,
0, &junk);
// delay 'cause boost starts faster
sleep(1);
CSTestB boostTest(criticalSection);
boost::thread boostThread(boostTest);
boostThread.join();
// todo: howto wait for winThread?
::DeleteCriticalSection(criticalSection);
delete(criticalSection);
}
else if (argc > 1 && memcmp(argv[1], "mu", 2) == 0) {
puts("Testing boost::mutex...\n");
puts(" winThread and boostThread share a boost::mutex. boostThread
grabs");
puts(" it and sleeps, while winThread waits for boostThread to wake
up and");
puts(" release. Then vice-versa.");
puts("");
puts("boostThread winThread");
puts("----------------------------------");
boost::mutex mutex;
MutexTestA testA(&mutex);
boost::thread boostThread(testA);
DWORD junk;
HANDLE winThread = ::CreateThread(NULL, 0, MutexTestB, &mutex, 0,
&junk);
boostThread.join();
// todo: howto wait for winThread?
}
else {
puts("usage: boost-win [boostcs]");
puts("options:");
puts(" cs - see if boost and win threads can both honor a
CRITICAL_SECTION");
return 1;
}
return 0;
}