Boost logo

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