pthread: destroying global static mutex

2k views Asked by At

This code was taken from the 3rd edition of Advanced Programming in the UNIX Environment, written by Richard Stevens. This is an example of how to make a reentrant version of getenv(). It is demostrated here only for a learning purpose.

/* Copyright (c) W.R.Stevens */
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>

extern char **environ;

pthread_mutex_t env_mutex;

static pthread_once_t init_done = PTHREAD_ONCE_INIT;

static void
thread_init(void)
{
    pthread_mutexattr_t attr;

    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&env_mutex, &attr);
    pthread_mutexattr_destroy(&attr);
}

int
getenv_r(const char *name, char *buf, int buflen)
{
    int i, len, olen;

    pthread_once(&init_done, thread_init);
    len = strlen(name);
    pthread_mutex_lock(&env_mutex);
    for (i = 0; environ[i] != NULL; i++) {
        if ((strncmp(name, environ[i], len) == 0) &&
          (environ[i][len] == '=')) {
            olen = strlen(&environ[i][len+1]);
            if (olen >= buflen) {
                pthread_mutex_unlock(&env_mutex);
                return(ENOSPC);
            }
            strcpy(buf, &environ[i][len+1]);
            pthread_mutex_unlock(&env_mutex);
            return(0);
        }
    }
    pthread_mutex_unlock(&env_mutex);
    return(ENOENT);
}

This code is easy to understand. I have a question though. We're never calling pthread_mutex_destroy(), which means that there may be a memory leak on exit (I guess it may differ between platforms).

First thing that comes to mind is that one may use PTHREAD_MUTEX_INITIALIZER. Does it require pthread_mutex_init() call? If no, there is no need to call pthread_mutex_destroy(). However, the mutex will be non-recursive.

One could write a simple C++ class that could destroy mutex in destructor. However, it is not suitable for anyone who has only C compiler (and it seems to be a bullshit to use C++ compiler because of one function).

The other thing that comes to mind are compiler-specific extensions like __attribute__((destructor)) in GCC (and hopefully clang). However, it is non-portable.

Is it possible to avoid a memory leak? If yes, how can that be done in C?

UPDATE As appears in "Programming with POSIX threads" written by David Butenhof, we never need to destroy PTHREAD_MUTEX_INITIALIZER variant. How about mutexes with other attributes?

2

There are 2 answers

2
jim mcnamara On

No memory leak because pthread_mutex_t variables live in user memory, on process exit all user allocated memory is reclaimed. Memory leaks occur when something allocates heap memory, like strdup. And then you do not clean it up.

If it were allocated like

pthread_mutex_t *foo;
....
foo =malloc(sizeof(pthread_mutex_t);

And then not released with free that creates a memory leak -- if then foo got allocated to more new memory. However at process termination ALL memory the process requested including heap is reclaimed by the OS. Stevens explains this in the chapter on processes. That code does not leak.

1
R.. GitHub STOP HELPING ICE On

Resources that are still live at process termination are not memory leaks, despite some naive tools classifying them as such. A memory leak is an irreversible and unbounded growth of a program's resource requirements during its lifetime that's disproportionate to the actual working set.

Per POSIX (which is where you get POSIX threads), all process-local resources cease to exist at program termination. There is no need to destroy/free them explicitly, and in some cases, like yours, you cannot safely destroy/free them and you should not try to.