pthread_mutex_unlock


C: Full example of pthread_cond_timedwait() 2

The following code has two threads.
The main thread spawns a pthread and then blocks on a condition waiting for a signal from the pthread.
The pthread will perform its task and then signal the main thread.
Once the main thread receives its signal, it will join the pthread and terminate.

[download id=”2713″]


#include <stdio.h>
#include <sys/types.h>
#include <pthread.h>
#include <asm/errno.h>

#define MAX_WAIT_TIME_IN_SECONDS (6)

struct thread_info_t
{
    // Used to identify a thread.
    pthread_t thread_id;
    // A condition is a synchronization device that allows threads to suspend execution and relinquish the processors until some predicate on shared data is satisfied.
    // The basic operations on conditions are: signal the condition (when the predicate becomes true), and wait for the condition, suspending the thread execution until another thread signals the condition.
    pthread_cond_t condition;
    // A mutex is a MUTual EXclusion device, and is useful for protecting shared data structures from concurrent modifications, and implementing critical sections and monitors.
    // A mutex has two possible states: unlocked (not owned by any thread), and locked (owned by one thread).
    // A mutex can never be owned by two different threads simultaneously.
    // A thread attempting to lock a mutex that is already locked by another thread is suspended until the owning thread unlocks the mutex first.
    pthread_mutex_t mutex;
};

void error_pthread_mutex_unlock(const int unlock_rv)
{
    fprintf(stderr, "Failed to unlock mutex.\n");
    switch (unlock_rv)
    {
        case EINVAL:
            fprintf(stderr, "The value specified by mutex does not refer to an initialized mutex object.\n");
            break;
        case EAGAIN:
            fprintf(stderr, "The mutex could not be acquired because the maximum number of recursive locks for mutex has been exceeded.\n");
            break;
        case EPERM:
            fprintf(stderr, "The current thread does not own the mutex.\n");
            break;
        default:
            break;
    }
}

void error_pthread_mutex_lock(const int lock_rv)
{
    fprintf(stderr, "Failed to lock mutex.\n");
    switch (lock_rv)
    {
        case EINVAL:
            fprintf(stderr, "The value specified by mutex does not refer to an initialized mutex object or the mutex was created with the protocol attribute having the value PTHREAD_PRIO_PROTECT and the calling thread's priority is higher than the mutex's current priority ceiling.\n");
            break;
        case EAGAIN:
            fprintf(stderr, "The mutex could not be acquired because the maximum number of recursive locks for mutex has been exceeded.\n");
            break;
        case EDEADLK:
            fprintf(stderr, "A deadlock condition was detected or the current thread already owns the mutex.\n");
            break;
        default:
            break;
    }
}

void error_pthread_cond_signal(const int signal_rv)
{
    fprintf(stderr, "Could not signal.\n");
    if (signal_rv == EINVAL)
    {
        fprintf(stderr, "The value cond does not refer to an initialised condition variable.\n");
    }
}

void error_pthread_setcanceltype(const int setcanceltype_rv)
{
    fprintf(stderr, "Could not change cancelability type of thread.\n");
    if (setcanceltype_rv == EINVAL)
    {
        fprintf(stderr, "Invalid value for type.\n");
    }
}

void error_pthread_create(const int create_rv)
{
    fprintf(stderr, "Could not create thread.\n");
    switch (create_rv)
    {
        case EAGAIN:
            fprintf(stderr, "Insufficient resources to create another thread or a system-imposed limit on the number of threads was encountered.\n");
            break;
        case EINVAL:
            fprintf(stderr, "Invalid settings in attr.\n");
            break;
        case EPERM:
            fprintf(stderr, "No permission to set the scheduling policy and parameters specified in attr.\n");
            break;
        default:
            break;
    }
}

void error_pthread_cond_timedwait(const int timed_wait_rv)
{
    fprintf(stderr, "Conditional timed wait, failed.\n");
    switch (timed_wait_rv)
    {
        case ETIMEDOUT:
            fprintf(stderr, "The time specified by abstime to pthread_cond_timedwait() has passed.\n");
            break;
        case EINVAL:
            fprintf(stderr, "The value specified by abstime, cond or mutex is invalid.\n");
            break;
        case EPERM:
            fprintf(stderr, "The mutex was not owned by the current thread at the time of the call.\n");
            break;
        default:
            break;
    }
}

void error_pthread_join(const int join_rv)
{

    fprintf(stderr, "Could not join thread.\n");
    switch (join_rv)
    {
        case EINVAL:
            fprintf(stderr, "The implementation has detected that the value specified by thread does not refer to a joinable thread.\n");
            break;
        case ESRCH:
            fprintf(stderr, "No thread could be found corresponding to that specified by the given thread ID.\n");
            break;
        case EDEADLK:
            fprintf(stderr, "A deadlock was detected or the value of thread specifies the calling thread.\n");
            break;
        default:
            break;
    }
}

void error_clock_gettime(const int gettime_rv)
{
    fprintf(stderr, "Could not get time from clock.\n");
    switch (gettime_rv)
    {
        case EFAULT:
            fprintf(stderr, "tp points outside the accessible address space.\n");
            break;
        case EINVAL:
            fprintf(stderr, "The clk_id specified is not supported on this system.\n");
            break;
        case EPERM:
            fprintf(stderr, "clock_settime() does not have permission to set the clock indicated.\n");
            break;
        default:
            break;
    }
}

// This is the thread that will be called by pthread_create() and it will be executed by the new thread.
void *worker_thread(void *data)
{
    // We know that the input data pointer is pointing to a thread_info_t so we are casting it to the right type.
    struct thread_info_t *thread_info = (struct thread_info_t *) data;

    // We block this thread trying to lock the mutex, this way we will make sure that the parent thread had enough time to call pthread_cond_timedwait().
    // When the parent thread calls pthread_cond_timedwait() it will unlock the mutex and this thread will be able to proceed.
    const int lock_rv = pthread_mutex_lock(&(thread_info->mutex));
    if (lock_rv)
    {
        error_pthread_mutex_lock(lock_rv);
    }

    int oldtype;
    // The pthread_setcanceltype() sets the cancelability type of the calling thread to the value given in type.
    // The previous cancelability type of the thread is returned in the buffer pointed to by oldtype.
    // The argument PTHREAD_CANCEL_ASYNCHRONOUS means that the thread can be canceled at any time.
    const int setcanceltype_rv = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
    if (setcanceltype_rv)
    {
        error_pthread_setcanceltype(setcanceltype_rv);
    }

    // TODO: This is the place you should implement the functionality that is needed for this thread

    // The pthread_cond_signal() call unblocks at least one of the threads that are blocked on the specified condition variable cond (if any threads are blocked on cond).
    const int signal_rv = pthread_cond_signal(&(thread_info->condition));
    if (signal_rv)
    {
        error_pthread_cond_signal(signal_rv);
    }

    // The pthread_mutex_unlock() function shall release the mutex object referenced by mutex.
    const int unlock_rv = pthread_mutex_unlock(&(thread_info->mutex));
    if (unlock_rv)
    {
        error_pthread_mutex_unlock(unlock_rv);
    }
    return NULL;
}

int main()
{
    struct thread_info_t thread_info;
    pthread_cond_init(&thread_info.condition, NULL);
    pthread_mutex_init(&thread_info.mutex, NULL);

    const int lock_rv = pthread_mutex_lock(&thread_info.mutex);
    if (lock_rv)
    {
        error_pthread_mutex_lock(lock_rv);
    }

    const int create_rv = pthread_create(&(thread_info.thread_id), NULL, &worker_thread, (void *) &thread_info);
    if (create_rv)
    {
        error_pthread_create(create_rv);
        const int unlock_rv = pthread_mutex_unlock(&thread_info.mutex);
        if (unlock_rv)
        {
            error_pthread_mutex_unlock(unlock_rv);
        }
    }
    else
    {
        // timespec is a structure holding an interval broken down into seconds and nanoseconds.
        struct timespec max_wait = {0, 0};

        // The clock_gettime system call has higher precision than its successor the gettimeofday().
        // It has the ability to request specific clocks using the clock id.
        // It fills in a timespec structure with the seconds and nanosecond count of the time since the Epoch (00:00 1 January, 1970 UTC).
        // CLOCK_REALTIME argument represents a system-wide real-time clock. This clock is supported by all implementations and returns the number of seconds and nanoseconds since the Epoch.
        const int gettime_rv = clock_gettime(CLOCK_REALTIME, &max_wait);
        if (gettime_rv)
        {
            error_clock_gettime(gettime_rv);
        }
        max_wait.tv_sec += MAX_WAIT_TIME_IN_SECONDS;

        // The pthread_cond_timedwait() function blocks on a condition variable.
        // It must be called with a mutex locked by the calling thread or undefined behavior results will occur.
        // This function atomically releases the mutex and causes the calling thread to block on the condition variable cond;
        // atomically here means "atomically with respect to access by another thread to the mutex and then the condition variable".
        // That is, if another thread is able to acquire the mutex after the about-to-block thread has released it, then a subsequent call to pthread_cond_broadcast() or pthread_cond_signal() in that thread shall behave as if it were issued after the about-to-block thread has blocked.
        const int timed_wait_rv = pthread_cond_timedwait(&thread_info.condition, &thread_info.mutex, &max_wait);
        if (timed_wait_rv)
        {
            error_pthread_cond_timedwait(timed_wait_rv);
        }

        // The pthread_join() function suspends execution of the calling thread until the target thread terminates, unless the target thread has already terminated.
        const int join_rv = pthread_join(thread_info.thread_id, NULL);
        if (join_rv)
        {
            error_pthread_join(join_rv);
        }
    }
    return 0;
}

[download id=”2713″]