Intel® oneAPI Threading Building Blocks
Ask questions and share information about adding parallelism to your applications when using this threading library.
2466 Discussions

compiler error intialising empty thread in TBB3.0

damienhocking
New Contributor I
304 Views
I have some cross-platform code that's compiling and running fine with VS2008 but not gcc 4.4.1. Here's the simplest example I can create:

#include

#ifndef __TBB_thread_H
#include "tbb/compat/thread"
#endif

#include "boost/function.hpp"

using namespace tbb;

class Wibble
{
public:
Wibble(int i){inti = i;}
~Wibble(){}

void Wotcha()
{
std::this_thread::sleep_for(tick_count::interval_t(1.5));
std::cout << "\\n WOTCHA, int is " << inti;
}

int inti;
};

int main()
{
Wibble wib2(6);
boost::function func;
func = &Wibble::Wotcha;

std::thread newthread;
newthread = std::thread(func, &wib2);

std::this_thread::yield();

char c;
std::cout << "\\n Hit a key...";
std::cin >> c;
std::cout << "\\n Finished. Bye";
return 1;
}

This compiles and runs fine on Windows with VS2008, but with gcc 4.1.1 on OpenSUSE I get an error on the assignment operator:

proto/threadtest2.cpp:33: error: no match for operator= in newthread = tbb::internal::tbb_thread_v3(boost::function(((const boost::function&)((const boost::function*)(& func)))), (& wib2))
/home/damien/projects/devframework/binaries/inteltbb/OSS/tbb30_018oss-lin/include/tbb/compat/../tbb_thread.h:165: note: candidates are: tbb::internal::tbb_thread_v3& tbb::internal::tbb_thread_v3::operator=(tbb::internal::tbb_thread_v3&)
scons: *** [bld/linux32debug/proto/bld/threadtest2.o] Error 1

Looking into tbb_thread.h I can see that the assignment operator is there. Have I missed a #define somewhere? I have TBB_IMPLEMENT_CPP0X defined so I don't think I'm conflicting with the C++0x in gcc 4.4 (and I don't have the C++0x flag on anyway).

If I change the assignment to:

std::thread newthread;
std::thread thr(func, &wib2);
newthread = thr;

the compiler is happy. I can do that in my production code too, but I thought the first style of assignment was also acceptable.

Damien
0 Kudos
1 Solution
Andrey_Marochko
New Contributor III
304 Views
My practical advice is simply don't copy thread objects. Use pointers or smart pointers if you really need to move about a reference to a thread.

But from the purely academic standpoint your case is rather curious. So let's talk a little bit about it.

If you take a closer look at the assignment operator declaration, you'll see that it accepts non-const reference to its source argument (right-hand side operand). In expression

newthread = std::thread(func, &wib2);

the source operand is rvalue, which makes the assignment invalid. MS compiler applies non-standard extension here, but issues a warning.

The reason for such a design is that std::thread is just an avatar of a kernel mode object, and copying it cannot create a copy of the actual thread. Therefore the assignmnt operation is destructive for the source operand. Its intestines are torn out and placed into the assignee, which now becomes the sole owner of the underlying kernel object.

But your observation is curious from another standpoint. Originally TBB provided class tbb_thread because there was not such class in the standard library. But when C++0x draft had matured enough, we've decided to masquerade TBB specific class as the upcoming standard std::thread one to simplify adoption of the new C++ Standard by our users.

However here a hitch awaited us. C++0x defines assignment operator in the following way:

thread& operator=(thread&& x);

It uses rvalue reference! Exactly for the same purpose I mentioned above - to pass ownership of the underlying OS entity to the new instance of the wrapper class. And the problem is that if at some moment in the future you try to compile your working variant with the real standard library, it won't compile! Because in the expression

newthread = thr;

source operand is lvalue, and lvalue cannot be bound to the rvalue reference.

To make if future proof, you could use move() helper:

tbb::move( newthread, thr );

But note that this move() is TBB specific.

View solution in original post

0 Kudos
2 Replies
Andrey_Marochko
New Contributor III
305 Views
My practical advice is simply don't copy thread objects. Use pointers or smart pointers if you really need to move about a reference to a thread.

But from the purely academic standpoint your case is rather curious. So let's talk a little bit about it.

If you take a closer look at the assignment operator declaration, you'll see that it accepts non-const reference to its source argument (right-hand side operand). In expression

newthread = std::thread(func, &wib2);

the source operand is rvalue, which makes the assignment invalid. MS compiler applies non-standard extension here, but issues a warning.

The reason for such a design is that std::thread is just an avatar of a kernel mode object, and copying it cannot create a copy of the actual thread. Therefore the assignmnt operation is destructive for the source operand. Its intestines are torn out and placed into the assignee, which now becomes the sole owner of the underlying kernel object.

But your observation is curious from another standpoint. Originally TBB provided class tbb_thread because there was not such class in the standard library. But when C++0x draft had matured enough, we've decided to masquerade TBB specific class as the upcoming standard std::thread one to simplify adoption of the new C++ Standard by our users.

However here a hitch awaited us. C++0x defines assignment operator in the following way:

thread& operator=(thread&& x);

It uses rvalue reference! Exactly for the same purpose I mentioned above - to pass ownership of the underlying OS entity to the new instance of the wrapper class. And the problem is that if at some moment in the future you try to compile your working variant with the real standard library, it won't compile! Because in the expression

newthread = thr;

source operand is lvalue, and lvalue cannot be bound to the rvalue reference.

To make if future proof, you could use move() helper:

tbb::move( newthread, thr );

But note that this move() is TBB specific.

0 Kudos
damienhocking
New Contributor I
304 Views
Thanks Andrey,

This is a really good description of thread move semantics with assignment. It makes a lot more sense now. I'll change my design to use pointers instead of assignment; the design is for long-running, fire-and-forget tasks. Extra news and deletes won't make any difference and I'd rather not use move.

Damien
0 Kudos
Reply