From e4ad14cc789090290550e3aa9640e4969037e8b7 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Tue, 12 Feb 2019 12:19:38 +0100 Subject: score: Avoid some deadlocks in _Once() Recursive usage of the same pthread_once_t results now in a deadlock. Previously, an error of EINVAL was returned. This usage scenario is invalid according to the POSIX pthread_once() specification. Close #3334. --- cpukit/score/src/once.c | 117 ++++++++++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 43 deletions(-) (limited to 'cpukit/score') diff --git a/cpukit/score/src/once.c b/cpukit/score/src/once.c index 5237c11878..2b2cc0e89b 100644 --- a/cpukit/score/src/once.c +++ b/cpukit/score/src/once.c @@ -1,68 +1,99 @@ /* - * COPYRIGHT (c) 1989-1999. - * On-Line Applications Research Corporation (OAR). + * SPDX-License-Identifier: BSD-2-Clause * - * 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. + * Copyright (C) 2019 embedded brains GmbH + * Copyright (C) 2019 Sebastian Huber + * + * 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. */ #ifdef HAVE_CONFIG_H - #include "config.h" +#include "config.h" #endif #include -#include +#include +#include + +#define ONCE_STATE_INIT 0 -#include +#define ONCE_STATE_RUNNING 1 -#define ONCE_STATE_NOT_RUN 0 -#define ONCE_STATE_RUNNING 1 #define ONCE_STATE_COMPLETE 2 +typedef struct { + rtems_mutex Mutex; + rtems_condition_variable State; +} Once_Control; + +static Once_Control _Once_Information = { + .Mutex = RTEMS_MUTEX_INITIALIZER( "_Once" ), + .State = RTEMS_CONDITION_VARIABLE_INITIALIZER( "_Once" ) +}; + int _Once( unsigned char *once_state, void ( *init_routine )( void ) ) { - int eno = 0; - - if ( *once_state != ONCE_STATE_COMPLETE ) { - _Once_Lock(); - - /* - * Getting to here means the once_control is locked so we have: - * 1. The init has not run and the state is ONCE_STATE_NOT_RUN. - * 2. The init has finished and the state is ONCE_STATE_COMPLETE (already - * caught by the previous if). - * 3. The init is being run by this thread and the state - * ONCE_STATE_RUNNING so we are nesting. This is an error. - */ - - switch ( *once_state ) { - case ONCE_STATE_NOT_RUN: - *once_state = ONCE_STATE_RUNNING; - ( *init_routine )(); - *once_state = ONCE_STATE_COMPLETE; - break; - case ONCE_STATE_RUNNING: - eno = EINVAL; - break; - default: - break; + _Atomic_Fence( ATOMIC_ORDER_ACQUIRE ); + + if ( RTEMS_PREDICT_FALSE( *once_state != ONCE_STATE_COMPLETE ) ) { + Thread_Life_state thread_life_state; + + thread_life_state = _Once_Lock(); + + if ( *once_state == ONCE_STATE_INIT ) { + *once_state = ONCE_STATE_RUNNING; + _Once_Unlock( THREAD_LIFE_PROTECTED ); + ( *init_routine )(); + _Once_Lock(); + _Atomic_Fence( ATOMIC_ORDER_RELEASE ); + *once_state = ONCE_STATE_COMPLETE; + rtems_condition_variable_broadcast( &_Once_Information.State ); + } else { + while ( *once_state != ONCE_STATE_COMPLETE ) { + rtems_condition_variable_wait( + &_Once_Information.State, + &_Once_Information.Mutex + ); + } } - _Once_Unlock(); + _Once_Unlock( thread_life_state ); } - return eno; + return 0; } -static API_Mutex_Control _Once_Mutex = API_MUTEX_INITIALIZER( "_Once" ); - -void _Once_Lock( void ) +Thread_Life_state _Once_Lock( void ) { - _API_Mutex_Lock( &_Once_Mutex ); + Thread_Life_state thread_life_state; + + thread_life_state = _Thread_Set_life_protection( THREAD_LIFE_PROTECTED ); + rtems_mutex_lock( &_Once_Information.Mutex ); + + return thread_life_state; } -void _Once_Unlock( void ) +void _Once_Unlock( Thread_Life_state thread_life_state ) { - _API_Mutex_Unlock( &_Once_Mutex ); + rtems_mutex_unlock( &_Once_Information.Mutex ); + _Thread_Set_life_protection( thread_life_state ); } -- cgit v1.2.3