views:

655

answers:

3

Hello, I have a question concerning this code which I want to run on QNX:

class ConcreteThread : public Thread
{
public:
    ConcreteThread(int test)
    {
     testNumber = test;
    }

    void *start_routine() 
    { 
     for(int i = 0; i < 10; i++)
     {
      sleep(1);
      cout << testNumber << endl;
     } 
    }

private:
    int testNumber;
};




class Thread 
{
public:
    Thread(){};

    int Create()
    {
        pthread_t m_id;
        return pthread_create(&m_id, NULL, &(this->start_routine_trampoline), this);
    }

protected:
    virtual void *start_routine() = 0;

private:

    static void *start_routine_trampoline(void *p)
    {
        Thread *pThis = (Thread *)p;
        return pThis->start_routine();
    }
};

Now, when I run this code without the sleep in *start_routine, it will simply print the number 10 times, before continuing on to the next line of code (sequential instead of parallel). However, when I use a sleep like in the code, it doesn't print any numbers at all and simply goes on to the next line of code. Why doesn't sleep work and how can I make a thread like this work, instead of running sequential?

+3  A: 

Note 1: If you only have 1 processor the code can only be done sequentially no matter how many threads you create. Each thread is given a slice of processor time before it is swapped out for the next threads.

Note 2: If the main thread exits pthreads will kill all child threads before they have a chance to execute.

Now to answer you questions:

Without the sleep. The thread once started has enough time in the single slice it was given to execute the loop 10 times completely.

With the sleep: Your worker thread is going to sleep for a full second. So your main thread has time to do a lot of work. If the main thread exits in this time the worker will be killed.

I would make the following changes:

//  Remove the Create() method
//  Put thread creation in the constructor.
//  Make the thread variable part of the object

pthread_t m_id;

Thread()
{
    if (pthread_create(&m_id, NULL, &(this->start_routine_trampoline), this) != 0)
    {
        throw std::runtime_error("Thread was not created");
    }
}

// Make sure the destructor waits for the thread to exit.
~Thread()
{
    pthread_join(m_id);
}

If you go and look at boost threading library. you will find that all the little mistakes like this have already been taken care of; Thus making threading easier to use.

Also note. That using a static may work but it is non portable. This is because pthread's is a C library and is thus expecting a function pointer with a C ABI. You are just getting lucky for your platform here. You need to define this as a function and declare the ABI by using extern "C"

// This needs to be a standard function with C Interface.
extern "C" void *start_routine_trampoline(void *p)
{
}
Martin York
+1 for right answer. But I'm NOT so sure about *"avoiding using static methods as the pthread fucntion"*. See: http://stackoverflow.com/questions/433220/qnx-c-thread-question#433614
Mr.Ree
See the comments in your answer. It points at article at http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.2 that seconds my opinion.
Martin York
+2  A: 

Try to make the pthread_t id a class member instead of a function local variable. That way the caller can pthread_join it.

Not doing this is technically a resource leak (unless the thread is specifically not joinable). And joining will avoid the issue that Martin York described.

From man pthread_join:

The joined thread th must be in the joinable state: it must not have been detached using pthread_detach(3) or the PTHREAD_CREATE_DETACHED attribute to pthread_create(3).

When a joinable thread terminates, its memory resources (thread descriptor and stack) are not deallocated until another thread performs pthread_join on it. Therefore, pthread_join must be called once for each joinable thread created to avoid memory leaks.

Evan Teran
I usually find it easier to just Detach and synchronize through semaphores.
Mr.Ree
A: 
Mr.Ree
static class functions have no gaurantees on calling conventions, they just are usually implemented in such a way that is compatible with C convention: see http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.2 and read the note on the bottom.
Evan Teran
Fascinating. So it would be legal for a compiler to have additional storage tacked onto function pointers to indicate whether they pointed to a C vs C++ function.
Mr.Ree
Foolish, increases sizeof(p), impairs C/C++ compatiblity, adds unwanted pain to coding, etc. But it's legal. Thanks! And thanks for the reference! That's good to know!
Mr.Ree
extern "C" makes sure the function uses the "C" ABI. This is well defined and standard everywhere. The C++ ABI is deliberately not defined. The implementation is allowed to define an ABI as efficiently as possible, (this means parameters can be passed in registers instead of the stack)
Martin York
This is also why you can not link objects generated by different C++ compilers (as they may be using different ABIs). But no matter what C compiler you use the ABI will always by the same and thus compatible.
Martin York
A lot of things are unspecified when implementing C++. Order of base objects in a derived class. Location of vtable pointer. Name mangling to implement overloading. Etc. C lacks these features. But your saying we can link the output of any pair of C compilers? Color me skeptical.
Mr.Ree
Even if the ABI requires it, color me skeptical. I've seen too many people treat standards as little more than "suggested guidelines" to ever believe such a sweeping statement. Not to mention the issues of object/library file organization.
Mr.Ree
Practically speaking, there are only so many solutions to these problems. It's classic pigeonhole principle (try wiki). Given the limited options, 99.9% of the time, the compiler author will make it compatible. The outliers are eliminated by Darwinian evolution. But it's not required...
Mr.Ree