From 7f4ee2b4ae39928ab5f449048e562ef6b2c5d17d Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 22 Apr 2016 14:37:13 +0200 Subject: posix: Avoid Giant lock for condition variables Update #2555. --- cpukit/posix/src/condwaitsupp.c | 188 ++++++++++++++++++++++------------------ 1 file changed, 103 insertions(+), 85 deletions(-) (limited to 'cpukit/posix/src/condwaitsupp.c') diff --git a/cpukit/posix/src/condwaitsupp.c b/cpukit/posix/src/condwaitsupp.c index c907f16cdd..b578a5f76f 100644 --- a/cpukit/posix/src/condwaitsupp.c +++ b/cpukit/posix/src/condwaitsupp.c @@ -18,15 +18,11 @@ #include "config.h" #endif -#include -#include - -#include -#include -#include -#include #include #include +#include +#include +#include THREAD_WAIT_QUEUE_OBJECT_ASSERT( POSIX_Condition_variables_Control, @@ -41,88 +37,110 @@ int _POSIX_Condition_variables_Wait_support( ) { POSIX_Condition_variables_Control *the_cond; - Objects_Locations location; + POSIX_Mutex_Control *the_mutex; + ISR_lock_Context lock_context; int status; int mutex_status; + CORE_mutex_Status core_mutex_status; + Per_CPU_Control *cpu_self; Thread_Control *executing; - the_cond = _POSIX_Condition_variables_Get( cond, &location ); - switch ( location ) { - - case OBJECTS_LOCAL: - - if ( the_cond->Mutex && ( the_cond->Mutex != *mutex ) ) { - _Objects_Put( &the_cond->Object ); - return EINVAL; - } - - - mutex_status = pthread_mutex_unlock( mutex ); - /* - * Historically, we ignored the return code since the behavior - * is undefined by POSIX. But GNU/Linux returns EPERM in this - * case, so we follow their lead. - */ - if ( mutex_status ) { - _Objects_Put( &the_cond->Object ); - return EPERM; - } - - if ( !already_timedout ) { - the_cond->Mutex = *mutex; - - executing = _Thread_Executing; - executing->Wait.return_code = 0; - - _Thread_queue_Enqueue( - &the_cond->Wait_queue, - POSIX_CONDITION_VARIABLES_TQ_OPERATIONS, - executing, - STATES_WAITING_FOR_CONDITION_VARIABLE - | STATES_INTERRUPTIBLE_BY_SIGNAL, - timeout, - ETIMEDOUT - ); - - _Objects_Put( &the_cond->Object ); - - /* - * Switch ourself out because we blocked as a result of the - * _Thread_queue_Enqueue. - */ - - /* - * 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. - */ - status = executing->Wait.return_code; - if ( status == EINTR ) - status = 0; - - } else { - _Objects_Put( &the_cond->Object ); - status = ETIMEDOUT; - } - - /* - * When we get here the dispatch disable level is 0. - */ - - mutex_status = pthread_mutex_lock( mutex ); - if ( mutex_status ) - return EINVAL; - - return status; - -#if defined(RTEMS_MULTIPROCESSING) - case OBJECTS_REMOTE: -#endif - case OBJECTS_ERROR: - break; + if ( mutex == NULL ) { + return EINVAL; + } + + the_cond = _POSIX_Condition_variables_Get( cond, &lock_context ); + + if ( the_cond == NULL ) { + return EINVAL; + } + + _POSIX_Condition_variables_Acquire_critical( the_cond, &lock_context ); + + if ( + the_cond->mutex != POSIX_CONDITION_VARIABLES_NO_MUTEX + && the_cond->mutex != *mutex + ) { + _POSIX_Condition_variables_Release( the_cond, &lock_context ); + return EINVAL; + } + + the_cond->mutex = *mutex; + + cpu_self = _Thread_Dispatch_disable_critical( &lock_context ); + executing = _Per_CPU_Get_executing( cpu_self ); + + /* + * 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. + */ + + the_mutex = _POSIX_Mutex_Get_no_protection( mutex ); + if ( + the_mutex == NULL + || !_CORE_mutex_Is_owner( &the_mutex->Mutex, executing ) + ) { + _POSIX_Condition_variables_Release( the_cond, &lock_context ); + _Thread_Dispatch_enable( cpu_self ); + return EPERM; + } + + if ( !already_timedout ) { + executing->Wait.return_code = 0; + _Thread_queue_Enqueue_critical( + &the_cond->Wait_queue.Queue, + POSIX_CONDITION_VARIABLES_TQ_OPERATIONS, + executing, + STATES_WAITING_FOR_CONDITION_VARIABLE, + timeout, + ETIMEDOUT, + &lock_context + ); + } else { + _POSIX_Condition_variables_Release( the_cond, &lock_context ); + executing->Wait.return_code = ETIMEDOUT; + } + + _ISR_lock_ISR_disable( &lock_context ); + core_mutex_status = _CORE_mutex_Surrender( + &the_mutex->Mutex, + NULL, + 0, + &lock_context + ); + _Assert( core_mutex_status == CORE_MUTEX_STATUS_SUCCESSFUL ); + (void) core_mutex_status; + + /* + * Switch ourself out because we blocked as a result of the + * _Thread_queue_Enqueue_critical(). + */ + + _Thread_Dispatch_enable( cpu_self ); + + status = (int) executing->Wait.return_code; + + /* + * 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 ( status == EINTR ) { + status = 0; + } + + /* + * When we get here the dispatch disable level is 0. + */ + + mutex_status = pthread_mutex_lock( mutex ); + if ( mutex_status != 0 ) { + return EINVAL; } - return EINVAL; + return status; } -- cgit v1.2.3