Last active
July 11, 2022 12:09
-
-
Save joycemaferko/34d8bb47dd064707af51c054387d30c6 to your computer and use it in GitHub Desktop.
pthread_cond_clockwait implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file | |
* | |
* @ingroup POSIXAPI | |
* | |
* @brief Waiting on a Condition | |
*/ | |
/* | |
* Copyright (C) 2021 Matthew Joyce | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
* POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
/* Defining to have access to function prototype in libc/include/pthread.h */ | |
#define _GNU_SOURCE | |
#ifdef HAVE_CONFIG_H | |
#include "config.h" | |
#endif | |
#include <rtems/posix/condimpl.h> | |
#include <rtems/score/todimpl.h> | |
/* | |
* pthread_cond_clockwait() appears in the Issue 8 POSIX Standard | |
*/ | |
int pthread_cond_clockwait( | |
pthread_cond_t *restrict cond, | |
pthread_mutex_t *restrict mutex, | |
clockid_t clock_id, | |
const struct timespec *restrict abstime | |
) | |
{ | |
/* | |
* POSIX Issue 8 does not specify that EINVAL is returned when abstime | |
* equals NULL. | |
*/ | |
if ( abstime == NULL ) { | |
return EINVAL; | |
} | |
/* | |
* POSIX Issue 8 specifies that EINVAL is returned when the clock is not | |
* supported. | |
*/ | |
if ( clock_id != CLOCK_REALTIME ) { | |
if ( clock_id != CLOCK_MONOTONIC ) { | |
return EINVAL; | |
} | |
} | |
return _POSIX_Condition_variables_Wait_support( | |
cond, | |
mutex, | |
clock_id, | |
abstime | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file | |
* | |
* @ingroup POSIX_COND_VARS | |
* | |
* @brief POSIX Condition Variables Wait Support | |
*/ | |
/* | |
* COPYRIGHT (c) 1989-2014. | |
* On-Line Applications Research Corporation (OAR). | |
* COPYRIGHT (c) 2021. | |
* Matthew Joyce. | |
* | |
* The license and distribution terms for this file may be | |
* found in the file LICENSE in this distribution or at | |
* http://www.rtems.org/license/LICENSE. | |
*/ | |
#ifdef HAVE_CONFIG_H | |
#include "config.h" | |
#endif | |
#include <rtems/posix/condimpl.h> | |
#include <rtems/posix/posixapi.h> | |
#include <rtems/score/assert.h> | |
#include <rtems/score/statesimpl.h> | |
#include <rtems/score/status.h> | |
#include <rtems/score/threaddispatch.h> | |
#ifdef CLOCK_REALTIME_COARSE | |
RTEMS_STATIC_ASSERT( | |
( CLOCK_NOT_SPECIFIED != CLOCK_REALTIME_COARSE), | |
"CLOCK_NOT_SPECIFIED conflicts with existing POSIX defined clock/n" ); | |
#endif | |
#ifdef CLOCK_PROCESS_CPUTIME_ID | |
RTEMS_STATIC_ASSERT( | |
( CLOCK_NOT_SPECIFIED != CLOCK_PROCESS_CPUTIME_ID), | |
"CLOCK_NOT_SPECIFIED conflicts with existing POSIX defined clock/n" ); | |
#endif | |
#ifdef CLOCK_REALTIME | |
RTEMS_STATIC_ASSERT( | |
( CLOCK_NOT_SPECIFIED != CLOCK_REALTIME), | |
"CLOCK_NOT_SPECIFIED conflicts with existing POSIX defined clock/n" ); | |
#endif | |
#ifdef CLOCK_THREAD_CPUTIME_ID | |
RTEMS_STATIC_ASSERT( | |
( CLOCK_NOT_SPECIFIED != CLOCK_THREAD_CPUTIME_ID), | |
"CLOCK_NOT_SPECIFIED conflicts with existing POSIX defined clock/n" ); | |
#endif | |
#ifdef CLOCK_MONOTONIC | |
RTEMS_STATIC_ASSERT( | |
( CLOCK_NOT_SPECIFIED != CLOCK_MONOTONIC), | |
"CLOCK_NOT_SPECIFIED conflicts with existing POSIX defined clock/n" ); | |
#endif | |
#ifdef CLOCK_MONOTONIC_RAW | |
RTEMS_STATIC_ASSERT( | |
( CLOCK_NOT_SPECIFIED != CLOCK_MONOTONIC_RAW), | |
"CLOCK_NOT_SPECIFIED conflicts with existing POSIX defined clock/n" ); | |
#endif | |
#ifdef CLOCK_MONOTONIC_COARSE | |
RTEMS_STATIC_ASSERT( | |
( CLOCK_NOT_SPECIFIED != CLOCK_MONOTONIC_COARSE), | |
"CLOCK_NOT_SPECIFIED conflicts with existing POSIX defined clock/n" ); | |
#endif | |
#ifdef CLOCK_BOOTTIME | |
RTEMS_STATIC_ASSERT( | |
( CLOCK_NOT_SPECIFIED != CLOCK_BOOTTIME), | |
"CLOCK_NOT_SPECIFIED conflicts with existing POSIX defined clock/n" ); | |
#endif | |
#ifdef CLOCK_REALTIME_ALARM | |
RTEMS_STATIC_ASSERT( | |
( CLOCK_NOT_SPECIFIED != CLOCK_REALTIME_ALARM), | |
"CLOCK_NOT_SPECIFIED conflicts with existing POSIX defined clock/n" ); | |
#endif | |
#ifdef CLOCK_BOOTTIME_ALARM | |
RTEMS_STATIC_ASSERT( | |
( CLOCK_NOT_SPECIFIED != CLOCK_BOOTTIME_ALARM), | |
"CLOCK_NOT_SPECIFIED conflicts with existing POSIX defined clock/n" ); | |
#endif | |
static void _POSIX_Condition_variables_Mutex_unlock( | |
Thread_queue_Queue *queue, | |
Thread_Control *the_thread, | |
Thread_queue_Context *queue_context | |
) | |
{ | |
POSIX_Condition_variables_Control *the_cond; | |
int mutex_error; | |
the_cond = POSIX_CONDITION_VARIABLE_OF_THREAD_QUEUE_QUEUE( queue ); | |
mutex_error = pthread_mutex_unlock( the_cond->mutex ); | |
if ( mutex_error != 0 ) { | |
/* | |
* Historically, we ignored the unlock status since the behavior | |
* is undefined by POSIX. But GNU/Linux returns EPERM in this | |
* case, so we follow their lead. | |
*/ | |
_Assert( mutex_error == EINVAL || mutex_error == EPERM ); | |
_Thread_Continue( the_thread, STATUS_NOT_OWNER ); | |
} | |
} | |
static void _POSIX_Condition_variables_Enqueue_no_timeout( | |
Thread_queue_Queue *queue, | |
Thread_Control *the_thread, | |
Per_CPU_Control *cpu_self, | |
Thread_queue_Context *queue_context | |
) | |
{ | |
_POSIX_Condition_variables_Mutex_unlock( queue, the_thread, queue_context ); | |
} | |
static void _POSIX_Condition_variables_Enqueue_with_timeout_monotonic( | |
Thread_queue_Queue *queue, | |
Thread_Control *the_thread, | |
Per_CPU_Control *cpu_self, | |
Thread_queue_Context *queue_context | |
) | |
{ | |
_Thread_queue_Add_timeout_monotonic_timespec( | |
queue, | |
the_thread, | |
cpu_self, | |
queue_context | |
); | |
_POSIX_Condition_variables_Mutex_unlock( queue, the_thread, queue_context ); | |
} | |
static void _POSIX_Condition_variables_Enqueue_with_timeout_realtime( | |
Thread_queue_Queue *queue, | |
Thread_Control *the_thread, | |
Per_CPU_Control *cpu_self, | |
Thread_queue_Context *queue_context | |
) | |
{ | |
_Thread_queue_Add_timeout_realtime_timespec( | |
queue, | |
the_thread, | |
cpu_self, | |
queue_context | |
); | |
_POSIX_Condition_variables_Mutex_unlock( queue, the_thread, queue_context ); | |
} | |
int _POSIX_Condition_variables_Wait_support( | |
pthread_cond_t *restrict cond, | |
pthread_mutex_t *restrict mutex, | |
clockid_t clock_id, | |
const struct timespec *restrict abstime | |
) | |
{ | |
POSIX_Condition_variables_Control *the_cond; | |
unsigned long flags; | |
Thread_queue_Context queue_context; | |
int error; | |
Thread_Control *executing; | |
pthread_condattr_t attr; | |
the_cond = _POSIX_Condition_variables_Get( cond ); | |
POSIX_CONDITION_VARIABLES_VALIDATE_OBJECT( the_cond, flags ); | |
_Thread_queue_Context_initialize( &queue_context ); | |
_Thread_queue_Context_set_timeout_argument( &queue_context, abstime, true ); | |
error = pthread_condattr_init( &attr ); | |
_Assert( error == 0 ); | |
/* | |
* If the parameter clock_id is not specified, then the clock from the | |
* condition attributes shall be used. | |
*/ | |
if ( clock_id == CLOCK_NOT_SPECIFIED ) { | |
error = pthread_condattr_getclock( &attr, &clock_id ); | |
_Assert( error == 0 ); | |
} | |
/* | |
* Now we have the desired clock_id, no matter whether it came from | |
* cond_timedwait or cond_clockwait. If abstime is specified, we use the | |
* clock_id to set the correct timeout. | |
*/ | |
if ( abstime != NULL ) { | |
/* | |
* We have already validated supported clocks in condclockwait.c. | |
* if clock_id is not CLOCK_MONOTONIC, then it is CLOCK_REALTIME. | |
*/ | |
if ( clock_id == CLOCK_MONOTONIC ) { | |
_Thread_queue_Context_set_enqueue_callout( | |
&queue_context, | |
_POSIX_Condition_variables_Enqueue_with_timeout_monotonic | |
); | |
} | |
else { | |
_Thread_queue_Context_set_enqueue_callout( | |
&queue_context, | |
_POSIX_Condition_variables_Enqueue_with_timeout_realtime | |
); | |
} | |
} | |
/* | |
* Otherwise, if the abstime parameter is NULL, this is a call to | |
* pthread_cond_wait and no timeout is necessary. | |
*/ | |
else { | |
_Thread_queue_Context_set_enqueue_callout( | |
&queue_context, | |
_POSIX_Condition_variables_Enqueue_no_timeout | |
); | |
} | |
executing = _POSIX_Condition_variables_Acquire( the_cond, &queue_context ); | |
if ( | |
the_cond->mutex != POSIX_CONDITION_VARIABLES_NO_MUTEX | |
&& the_cond->mutex != mutex | |
) { | |
_POSIX_Condition_variables_Release( the_cond, &queue_context ); | |
return EINVAL; | |
} | |
the_cond->mutex = mutex; | |
_Thread_queue_Context_set_thread_state( | |
&queue_context, | |
STATES_WAITING_FOR_CONDITION_VARIABLE | |
); | |
_Thread_queue_Enqueue( | |
&the_cond->Queue.Queue, | |
POSIX_CONDITION_VARIABLES_TQ_OPERATIONS, | |
executing, | |
&queue_context | |
); | |
error = _POSIX_Get_error_after_wait( executing ); | |
/* | |
* If the thread is interrupted, while in the thread queue, by | |
* a POSIX signal, then pthread_cond_wait returns spuriously, | |
* according to the POSIX standard. It means that pthread_cond_wait | |
* returns a success status, except for the fact that it was not | |
* woken up a pthread_cond_signal() or a pthread_cond_broadcast(). | |
*/ | |
if ( error == EINTR ) { | |
error = 0; | |
} | |
/* | |
* When we get here the dispatch disable level is 0. | |
*/ | |
if ( error != EPERM ) { | |
int mutex_error; | |
mutex_error = pthread_mutex_lock( mutex ); | |
if ( mutex_error != 0 ) { | |
_Assert( mutex_error == EINVAL ); | |
error = EINVAL; | |
} | |
} | |
return error; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
My implementation of the new POSIX standard method pthread_cond_clockwait for the RTEMS real-time operating system (RTOS).