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; }