1

I am writing a little C library for the Raspberry Pi for controlling 433MHz transmitters/receivers. While receiving data the whole application would block, so I decided to put this code into a separate thread and synchronize it with the main thread.

#include <stdlib.h>
#include <stdbool.h>

#include <time.h>
#include <pthreads.h>

struct data {
    // data fields
    ...
    struct sync {
        pthread_t       thread;
        pthread_cond_t  wait;
        pthread_mutex_t lock;
        unsigned int    flags;
    } sync;
};

void *thread_func(void *arg) {
    struct data *data = (struct data *)arg;

    while (1) {
        if (pthread_mutex_lock(&data->sync.lock)) {
            ...
        }

        // receiving data

        ...

        if (flags & EXIT_THREAD) {
            flags |= THREAD_EXITED;
            pthread_exit(NULL);
        }

        if (pthread_cond_signal(&data->sync.wait)) {
            ...
        }

        if (pthread_mutex_unlock(&data->sync.lock)) {
            ...
        }
    }

    return (NULL);
}

int recv(struct data *data, bool exit_thread) {
    struct timespec tspec;
    pthread_t id;

    tspec.tv_sec  = 1;
    tspec.tv_nsec = 0;

    if (!data.sync.thread) {
        if (pthread_create(&id, NULL, &thread_func, data)) {
            return (-1);
        }

        data->sync.thread = id;
    }

    while (1) {
        if ((ret = pthread_mutex_timedlock(&data.sync.lock, &tspec))) {
            if (ret != TIMEDOUT) {
                ...
            }
        }

        while (!(data->sync.flags & DATA_AVAILABLE)) {
            ret = pthread_cond_timedwait(&data.sync.wait, &data.sync.lock,
                                         &tspec);
            if (ret && ret != ETIMEDOUT && ret != EINTR) {
                ...
            }
        }

        // process received data
        ...

        if (exit_thread) {
            data->sync.flags |= EXIT_THREAD;
        }

        if (pthread_mutex_unlock(&data.sync.lock)) {
            ...
        }
    }
}

int main(int argc, char *argv) {
    struct data data;
    bool exit_thread = false;
    int ret;

    if (pthread_cond_init(&data.sync.wait))
        return (1);

    if (pthread_mutex_init(&data.sync.lock))
        return (1);

    data.sync.thread = 0;
    data.sync.flags  = 0;

    while (!exit_thread) {
        if (recv(&data, exit_thread) == -1) {
            ...
        }

        ...
    }

    return (0);
}

This is a very simplified version of my code and maybe there are some flaws in locking and unlocking the right way but this code should illustrate my actual question. How should the error handling of phtread_mutex_lock, phtread_mutex_timedlock and pthread_mutex_unlock be done? If one of those functions would fail in a critical way, the fundamental synchronization would be broken. I wouldn't be able to communicate the exit of the receiving thread to main thread, when I simply exit after a critical error. So what would be the proper way to handle such errors?

2 Answers 2

2

Nobody handles this error conditions. That they return errors is a typical DesignByComitee problem. The only way to handle them is to crash your process and move error handling to a whole other level. All code I have seen don't even do that and higher language libraries don't offer APIs for for checking.

1

Read the pthread documentation and see what errors each function can return. Hopefully there is also a hint there what the best way is to handle such an error.

Depending on the kind of error, that handling can range from retrying the operation to just terminating the entire application (and let the OS deal with any left-over threads that were not properly notified).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.