HomeContact

To use pthreads with C++ :

Yes, it was easy - but even before boost there has always been some C++ wrapper. So when you go to check this piece of code wriiten a couple of years ago using pure pthreads you are not always sure if everything is alright with the code.

So here are some interesting and not so interesting information about POSIX threads :

- POSIX 1003.1c deals with threads.

- The current posix threads library in linux is NPTL ( Native POSIX Thread Library). NPTL is 1:1, kernel supported threading library, it uses clone() system call to create a thread and uses the new system call futex() to resolve contention. By the way, futexes are kind of cool and fast because kernel’s involvement is required only when a contended case requires arbitrattion. On a 2.6.x linux kernel to know the latest pthread library you use

$ getconf GNU_LIBPTHREAD_VERSION

I find it interesting that you use opaque objects everywhere in pthreads library; it is a ‘C’ way to implement API constraints(and interedependency) with data hiding - it is pretty clean. For example when you want to set an attribute (setting an attribute is the way to set the stack size, to make it joinable etc) you initialize the attribute separately, set properties on it and then pass it to pthread_create() - pthread_create() is the function to create a new thread.

So it would look like :

pthread_t tid;
int arg = 10;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
rc = pthread_create(&tid, &attr, startFunction, &arg);
pthread_attr_destroy(&attr);

Whenever we are using C++, we have to remember that startFunction is a c function - hence no namemangling is allowed there. Your C++ compiler would always mangle the name to support overloading etc, so the globally visible name of a function may not be same as the name you declared it with.(Since you can have multiple functions with the same name, C++ compiler creates a unique name by using the signature of the function) Naturally, you cannot use a namemangled function as the callback function for C library. The solution is to use extern “C” linkage declaration - it can be done with global and static functions only.

The next point is to remember that you do not know when a thread is going to be scheduled, so manage your resources with care, for example you can pass an argument to the start routine ( as you can see in the above example) but the passed argument is going to be used by the newly created thread only when it is scheduled, so naturally the parent thread must not try to modify the argument before that !

As soon as you learn to use multiple threads sharing the same address space, using each others data freely, you have to learn to use guards to protect your data as well - it comes with the territory. For example you have the case where you want to implement some kind of transactional sanity in a situation where multiple threads are effectively jumbling up each others data to create a confusion … check the following code :

This could be the main.cpp :

#include "transaction.hpp"

using std::cout;
using std::endl;
using std::terminate;

extern "C"
{
    void* runTransactionA(void* arg)
    {
        Transaction* trans = static_cast<Transaction*>(arg);
        trans->transactAs();
        return 0;
    }

    void* runTransactionB(void* arg)
    {
        Transaction* trans = static_cast<Transaction*>(arg);
        trans->transactBs();
        return 0;
    }
}

int main()
{

    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    pthread_t firstHandle, secondHandle;
    Transaction transaction("Transaction");

    int rc;
    rc = pthread_create(&firstHandle, &attr, runTransactionA,
            &transaction);
    if(rc != 0)
    {
        cout << "Error - return code from pthread_create() ";
        cout << rc << endl;
        terminate();
    }
    rc = pthread_create(&secondHandle, &attr, runTransactionB,
            &transaction);
    if(rc != 0)
    {
        cout << "Error - return code from pthread_create() ";
        cout << rc << endl;
        terminate();
    }

    pthread_attr_destroy(&attr);

    void* status = 0;
    rc = pthread_join(firstHandle, &status);
    if(rc != 0)
    {
        cout << "Error - return code from pthread_join() ";
        cout << rc << endl;
        terminate();
    }

    rc = pthread_join(secondHandle, &status);
    if(rc != 0)
    {
        cout << "Error - return code from pthread_join() ";
        cout << rc << endl;
        terminate();
    }

    transaction.displayServerLink();
    pthread_exit(0);

}

Where the Transaction implementation would look like :

#include
#include
#include
#include

#include 

class Transaction
{
    public:

    //constructor
    Transaction(std::string name) : m_name(name)
    {
        pthread_mutex_init(&serverMutex, 0);
    }

    //destructor
    ~Transaction()
    {
        pthread_mutex_destroy(&serverMutex);
    }

    void transactAs()
    {
        struct timespec req;
        struct timespec rem;
        req.tv_sec = 0;
        req.tv_nsec = 10000;
        pthread_mutex_lock(&serverMutex);

        serverLink << std::endl;
        serverLink << m_name << " : ";
        for (int step = 0; step < 10; ++step)
        {
            serverLink << "<< step << ">";
            nanosleep(&req, &rem);
        }
        serverLink << " :" << std::endl;

        pthread_mutex_unlock(&serverMutex);
    }

    void transactBs()
    {
        struct timespec req;
        struct timespec rem;
        req.tv_sec = 0;
        req.tv_nsec = 10000;
        pthread_mutex_lock(&serverMutex);

        serverLink << std::endl;
        serverLink << m_name << " : ";
        for (int step = 0; step < 10; ++step)
        {
            serverLink << "<< step << ">";
            nanosleep(&req, &rem);
        }
        serverLink << " :" << std::endl;

        pthread_mutex_unlock(&serverMutex);
    }

    void displayServerLink()
    {
        std::cout << "Current Buffer in ServerLink is";
        std::cout << serverLink.str() << std::endl;
    }

    private:
        std::string m_name;
        std::stringstream serverLink;
        pthread_mutex_t serverMutex;
};

5 Comments »

  1. Using pthreads in C++…

    The Daily C++ weblog has posted a short piece on using pthreads in C++….

    Trackback by Linoleum — July 3, 2008 @ 2:38 pm

  2. we have to remember that startFunction is a c function - hence no namemangling is allowed there

    That’s not quite correct. Unless you are doing explicit dynamic linking (i.e. dlopen/dlsym), name mangling is not an issue. Provided both the startFunction and the call to pthread_create are both in C++ compiled code, the compiler/linker can handle name mangling.

    What could cause a problem is the calling convention of C++ functions as opposed to C functions (the calling convention handles issues such as how arguments are passed and the stack is manipulated between the caller and the callee). A C++ compiler is allowed to use a different calling convention for C++ functions and C functions. Since pthread_create() expects the startFunction to use the C calling convention, you can run into trouble.

    That said, all the compilers I am familiar with (gcc, Intel, Sun) use the same calling convention for C++ and C code. I have successfully had pthread_create() use a non extern “C” function as the start function.

    That said, you cannot use a member function as it does have a different calling convention (the implicit this pointer). But as long as the function is a static member function or a free function, you do not need to use extern “C”.

    Comment by R Samuel Klatchko — July 3, 2008 @ 11:01 pm

  3. Hey this was very useful! Thanks , would love to see more posts!

    Comment by Mukta — July 8, 2008 @ 2:08 pm

  4. Hi Samuel,
    Yes, but if you want write a piece of code without any portability issue then it is advisable that you keep the start function under extern c.

    Cheers,
    Milton

    Comment by Milton — July 29, 2008 @ 5:22 am

  5. C++, Isn’t that the hardest Programming language out there?!? Even VB is giving me a headache. 0.O

    Comment by Nate — September 9, 2008 @ 4:37 pm

RSS feed for comments on this post. | TrackBack URI
You can also bookmark this on del.icio.us or check the cosmos

Leave a comment



XHTML ( You can use these tags): <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> .