pThread Mutex Locking without Global Mutex

c++, mutex, pthreads, thread-safety

Solution

Yes, provided the mutex stays in scope while any thread is using it, it doesn't have to be global. You do have to tell the second thread where the mutex is, there's no way around that unfortunately.

And passing it is no different from passing any other variable.

So, simply define it and initialise it in your first thread then, when creating your second thread, pass its address as the thread argument.

The second thread can then use that address to access the mutex.

In terms of your comment where you want a function to be both usable as a thread and simply a normal function, I'd steer clear of that just because of the complexity.

What you can do is to put most of the work into a normal function and then make the thread function a simple wrapper around that. You can even pass in a mutex pointer which can be used if valid, or not used if NULL.

See the following complete program for details. First, some support stuff, needed headers and a logging function:

#include <pthread.h>
#include <stdio.h>
#include <time.h>

static void mylog (int indent, char *s) {
    int i;
    time_t now = time (NULL);
    struct tm *lt = localtime (&now);
    printf ("%02d:%02d:%02d ", lt->tm_hour, lt->tm_min, lt->tm_sec);
    putchar ('|');
    for (i = 0; i < indent; i++) printf ("%-20s|", "");
    printf ("%-20s|", s);
    for (i = indent + 1; i < 3; i++) printf ("%-20s|", "");
    putchar ('\n');
}

Next, the function which will do the work. This is built in such a way that it can be called from any thread, and can be passed a mutex pointer if you want it to use one:

static void *myfunction (void *ptr) {
    pthread_mutex_t *pMtx = ptr;

    mylog (2, "starting");

    if (pMtx != NULL) {
        mylog (2, "locking mutex");
        pthread_mutex_lock (pMtx);
        mylog (2, "locked mutex");
    }

    mylog (2, "sleeping");
    sleep (5);
    mylog (2, "finished sleeping");

    if (pMtx != NULL) {
        mylog (2, "unlocking mutex");
        pthread_mutex_unlock (pMtx);
    }

    mylog (2, "stopping");
}

Then an actual thread function, which is really a thin wrapper around the work function above. Note that it receives the mutex via the thread-specific parameter and passes that on to the work function:

static void *mythread (void *ptr) {
    mylog (1, "starting");

    mylog (1, "call fn with mutex");
    myfunction (ptr);
    mylog (1, "and back");

    mylog (1, "stopping");
}

And finally, the main function. This first calls the work function without a mutex, then creates a mutex for sharing with the other thread:

int main (void) {
    pthread_mutex_t mtx;
    pthread_t tid1;
    char buff[100];

    printf ("         |%-20s|%-20s|%-20s|\n", "main", "thread", "workfn");
    printf ("         |%-20s|%-20s|%-20s|\n", "====", "======", "======");

    mylog (0, "starting");

    mylog (0, "call fn, no mutex");
    myfunction (NULL);
    mylog (0, "and back");

    mylog (0, "initing mutex");
    pthread_mutex_init (&mtx, NULL);

    mylog (0, "locking mutex");
    pthread_mutex_lock (&mtx);
    mylog (0, "locked mutex");

    mylog (0, "starting thead");
    pthread_create (&tid1, NULL, mythread, &mtx);

    mylog (0, "sleeping");
    sleep (5);
    mylog (0, "sleep done");

    mylog (0, "unlocking mutex");
    pthread_mutex_unlock (&mtx);

    mylog (0, "joining thread");
    pthread_join (tid1, NULL);
    mylog (0, "joined thread");

    mylog (0, "exiting");
    return 0;
}

You can see in the output how the code sequences itself:

         |main                |thread              |workfn              |
         |====                |======              |======              |
15:07:10 |starting            |                    |                    |
15:07:10 |call fn, no mutex   |                    |                    |
15:07:10 |                    |                    |starting            |
15:07:10 |                    |                    |sleeping            |
15:07:15 |                    |                    |finished sleeping   |
15:07:15 |                    |                    |stopping            |
15:07:15 |and back            |                    |                    |
15:07:15 |initing mutex       |                    |                    |
15:07:15 |locking mutex       |                    |                    |
15:07:15 |locked mutex        |                    |                    |
15:07:15 |starting thead      |                    |                    |
15:07:15 |sleeping            |                    |                    |
15:07:15 |                    |starting            |                    |
15:07:15 |                    |call fn with mutex  |                    |
15:07:15 |                    |                    |starting            |
15:07:15 |                    |                    |locking mutex       |
15:07:20 |sleep done          |                    |                    |
15:07:20 |unlocking mutex     |                    |                    |
15:07:20 |joining thread      |                    |                    |
15:07:20 |                    |                    |locked mutex        |
15:07:20 |                    |                    |sleeping            |
15:07:25 |                    |                    |finished sleeping   |
15:07:25 |                    |                    |unlocking mutex     |
15:07:25 |                    |                    |stopping            |
15:07:25 |                    |and back            |                    |
15:07:25 |                    |stopping            |                    |
15:07:25 |joined thread       |                    |                    |
15:07:25 |exiting             |                    |                    |

Note especially how the direct call with no mutex acts, as compared to the call with the mutex.

Problem

All of the tutorials i have seen for Mutex locking with the pThread library have used a Global Mutex Lock: See: https://computing.llnl.gov/tutorials/pthreads/#Mutexes http://www.drdobbs.com/cpp/184401518?pgno=3 (for boost::thread but same context) What I would like to do is to use Mutex locking where the Global of the main file is out of scope for the function needing to lock the variable. here is an example: Main.cpp ``` int main() { some initilisation code. tree * tree1; *Start thread to visualise tree* (see code below) Mutex Lock: delete tree1; tree1 = newTree(); Mutex Unlock ``` visualiser.cpp ``` visualise(Tree *) { Forever: Mutex Lock: Check for tree change. Update tree image. Display tree image. Mutex Unlock ``` I would like to know if this is possible: - Without global scope mutex locks. - If possible without passing the mutex to the visualise function. I understand that this may not be plausable, if not how would i get the global scope variable to the visualiser.cpp, using an extern? Also if i need to pass the mutex to the function how would i go about doing that?

Original source