|
Boost : |
From: Chris Byrne (chris_at_[hidden])
Date: 2003-10-01 19:50:29
After falling into this trap several times I was wondering if anyone else was aware of it.
Creating a boost::thread object and passing in an unnamed temporary can lead to undefined behaviour.
boost::thread thrd2(MyThread(idx));
thrd2.join()
Gets optimized out by both GCC 3.2.2 and VC 7.1 (so the thread silently won't be created). More worrying is that calling join() will result in an error:
GCC 3.2.2:
boostthreads.cpp: In function `int main()':
boostthreads.cpp:45: request for member `join' in `thrd2(MyThread)', which is of non-aggregate type `boost::thread ()(MyThread)
VC 7.1:
s:\src\experimental\gui_light\main.cpp(37): error C2228: left of '.join' must have class/struct/union type
However, it gets more scary:
boost::thread thrd1(MyThread(1));
boost::thread thrd3(MyThread((int)idx));
both work (as in a thread is created) but
boost::thread thrd4(MyThread::MyThread(idx));
only works with VC7.1 (GCC silently optimizes it out)
Not using an unnamed temporary appears to be safe, as well as allocating a thread object (by way of new).
// works
boost::thread* thrd5 = new boost::thread(MyThread(idx));
// doesn't work
boost::thread thrd2 = boost::thread(MyThread(idx));
// works
MyThread mythread(idx);
boost::thread thrd6 = boost::thread(mythread);
So some times it works, and other times it doesn't. Critically: it works *most* times, but *silently* fails other times. In other words the interface is broken and quite dangerous.
- Chris
Example code follows:
------------------------------------------------------------------------
/*** includes ***/
#include <boost/utility.hpp>
#include <boost/thread.hpp>
#include <iostream>
#ifdef _WIN32
#include <windows.h>
#else // assume Linux
#include <unistd.h>
#define Sleep(x) usleep(x * 1000)
#endif // _WIN32
using namespace std;
///////////////////////////////////////////////////////////////////////
struct MyThread : boost::noncopyable {
public:
explicit MyThread(int idx) : idx_(idx) {
cout << "MyThread(" << idx_ << ")\n";
}
MyThread(const MyThread &thread) : idx_(thread.idx_) {
cout << "MyThread(MyThread " << idx_ << ")\n";
}
~MyThread() { cout << "~MyThread(" << idx_ << ")\n"; }
void operator()(void) { cout << "operator(" << idx_ << ")\n"; }
private:
int idx_;
};
///////////////////////////////////////////////////////////////////////
int main() {
cout << "* main() entry\n";
// (GCC 3.2.2) works
// (MSVC 7.1) works
int idx = 1;
boost::thread thrd1(MyThread(1));
// (GCC 3.2.2) doesn't work (optimized out)
// (MSVC 7.1) doesn't work (optimized out)
idx = 2;
boost::thread thrd2(MyThread(idx));
// type casting an int to an int
// (GCC 3.2.2) works
// (MSVC 7.1) works
idx = 3;
boost::thread thrd3(MyThread((int)idx));
// calling the constructor explicitly
// (GCC 3.2.2) doesn't work
// (MSVC 7.1) works
idx = 4;
boost::thread thrd4(MyThread::MyThread(idx));
Sleep(1000);
cout << "* main() exiting\n";
return 0;
}
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk