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″]