Changeset e4ad14cc in rtems


Ignore:
Timestamp:
Feb 12, 2019, 11:19:38 AM (2 months ago)
Author:
Sebastian Huber <sebastian.huber@…>
Branches:
master
Children:
3d65f45
Parents:
3ecb207
git-author:
Sebastian Huber <sebastian.huber@…> (02/12/19 11:19:38)
git-committer:
Sebastian Huber <sebastian.huber@…> (02/18/19 06:25:58)
Message:

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.

Files:
8 edited

Legend:

Unmodified
Added
Removed
  • cpukit/include/rtems/score/onceimpl.h

    r3ecb207 re4ad14cc  
    88
    99/*
    10  * Copyright (c) 2014 embedded brains GmbH.  All rights reserved.
     10 * Copyright (c) 2014, 2019 embedded brains GmbH.  All rights reserved.
    1111 *
    1212 *  embedded brains GmbH
     
    2424#define _RTEMS_ONCE_H
    2525
     26#include <rtems/score/thread.h>
     27
    2628#ifdef __cplusplus
    2729extern "C" {
     
    3840 */
    3941
    40 int _Once( unsigned char *once_state, void (*init_routine)(void) );
     42int _Once( unsigned char *once_state, void ( *init_routine )( void ) );
    4143
    42 void _Once_Lock( void );
     44Thread_Life_state _Once_Lock( void );
    4345
    44 void _Once_Unlock( void );
     46void _Once_Unlock( Thread_Life_state thread_life_state );
    4547
    4648/** @} */
  • cpukit/rtems/src/timerserver.c

    r3ecb207 re4ad14cc  
    229229{
    230230  rtems_status_code status;
    231 
    232   _Once_Lock();
     231  Thread_Life_state thread_life_state;
     232
     233  thread_life_state = _Once_Lock();
    233234  status = _Timer_server_Initiate( priority, stack_size, attribute_set );
    234   _Once_Unlock();
     235  _Once_Unlock( thread_life_state );
    235236
    236237  return status;
  • cpukit/score/src/once.c

    r3ecb207 re4ad14cc  
    11/*
    2  *  COPYRIGHT (c) 1989-1999.
    3  *  On-Line Applications Research Corporation (OAR).
     2 * SPDX-License-Identifier: BSD-2-Clause
    43 *
    5  *  The license and distribution terms for this file may be
    6  *  found in the file LICENSE in this distribution or at
    7  *  http://www.rtems.org/license/LICENSE.
     4 * Copyright (C) 2019 embedded brains GmbH
     5 * Copyright (C) 2019 Sebastian Huber
     6 *
     7 * Redistribution and use in source and binary forms, with or without
     8 * modification, are permitted provided that the following conditions
     9 * are met:
     10 * 1. Redistributions of source code must retain the above copyright
     11 *    notice, this list of conditions and the following disclaimer.
     12 * 2. Redistributions in binary form must reproduce the above copyright
     13 *    notice, this list of conditions and the following disclaimer in the
     14 *    documentation and/or other materials provided with the distribution.
     15 *
     16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26 * POSSIBILITY OF SUCH DAMAGE.
    827 */
    928
    1029#ifdef HAVE_CONFIG_H
    11   #include "config.h"
     30#include "config.h"
    1231#endif
    1332
    1433#include <rtems/score/onceimpl.h>
    15 #include <rtems/score/apimutex.h>
     34#include <rtems/score/threadimpl.h>
     35#include <rtems/thread.h>
    1636
    17 #include <errno.h>
     37#define ONCE_STATE_INIT 0
    1838
    19 #define ONCE_STATE_NOT_RUN  0
    20 #define ONCE_STATE_RUNNING  1
     39#define ONCE_STATE_RUNNING 1
     40
    2141#define ONCE_STATE_COMPLETE 2
     42
     43typedef struct {
     44  rtems_mutex              Mutex;
     45  rtems_condition_variable State;
     46} Once_Control;
     47
     48static Once_Control _Once_Information = {
     49  .Mutex = RTEMS_MUTEX_INITIALIZER( "_Once" ),
     50  .State = RTEMS_CONDITION_VARIABLE_INITIALIZER( "_Once" )
     51};
    2252
    2353int _Once( unsigned char *once_state, void ( *init_routine )( void ) )
    2454{
    25   int eno = 0;
     55  _Atomic_Fence( ATOMIC_ORDER_ACQUIRE );
    2656
    27   if ( *once_state != ONCE_STATE_COMPLETE ) {
    28     _Once_Lock();
     57  if ( RTEMS_PREDICT_FALSE( *once_state != ONCE_STATE_COMPLETE ) ) {
     58    Thread_Life_state thread_life_state;
    2959
    30     /*
    31      * Getting to here means the once_control is locked so we have:
    32      *  1. The init has not run and the state is ONCE_STATE_NOT_RUN.
    33      *  2. The init has finished and the state is ONCE_STATE_COMPLETE (already
    34      *     caught by the previous if).
    35      *  3. The init is being run by this thread and the state
    36      *     ONCE_STATE_RUNNING so we are nesting. This is an error.
    37      */
     60    thread_life_state = _Once_Lock();
    3861
    39     switch ( *once_state ) {
    40       case ONCE_STATE_NOT_RUN:
    41         *once_state = ONCE_STATE_RUNNING;
    42         ( *init_routine )();
    43         *once_state = ONCE_STATE_COMPLETE;
    44         break;
    45       case ONCE_STATE_RUNNING:
    46         eno = EINVAL;
    47         break;
    48       default:
    49         break;
     62    if ( *once_state == ONCE_STATE_INIT ) {
     63      *once_state = ONCE_STATE_RUNNING;
     64      _Once_Unlock( THREAD_LIFE_PROTECTED );
     65      ( *init_routine )();
     66      _Once_Lock();
     67      _Atomic_Fence( ATOMIC_ORDER_RELEASE );
     68      *once_state = ONCE_STATE_COMPLETE;
     69      rtems_condition_variable_broadcast( &_Once_Information.State );
     70    } else {
     71      while ( *once_state != ONCE_STATE_COMPLETE ) {
     72        rtems_condition_variable_wait(
     73          &_Once_Information.State,
     74          &_Once_Information.Mutex
     75        );
     76      }
    5077    }
    5178
    52     _Once_Unlock();
     79    _Once_Unlock( thread_life_state );
    5380  }
    5481
    55   return eno;
     82  return 0;
    5683}
    5784
    58 static API_Mutex_Control _Once_Mutex = API_MUTEX_INITIALIZER( "_Once" );
     85Thread_Life_state _Once_Lock( void )
     86{
     87  Thread_Life_state thread_life_state;
    5988
    60 void _Once_Lock( void )
    61 {
    62   _API_Mutex_Lock( &_Once_Mutex );
     89  thread_life_state = _Thread_Set_life_protection( THREAD_LIFE_PROTECTED );
     90  rtems_mutex_lock( &_Once_Information.Mutex );
     91
     92  return thread_life_state;
    6393}
    6494
    65 void _Once_Unlock( void )
     95void _Once_Unlock( Thread_Life_state thread_life_state )
    6696{
    67   _API_Mutex_Unlock( &_Once_Mutex );
     97  rtems_mutex_unlock( &_Once_Information.Mutex );
     98  _Thread_Set_life_protection( thread_life_state );
    6899}
  • testsuites/psxtests/psxonce01/init.c

    r3ecb207 re4ad14cc  
    11/*
     2 *  Copyright (C) 2019 embedded brains GmbH
     3 *  Copyright (C) 2019 Sebastian Huber
     4 *
    25 *  COPYRIGHT (c) 1989-2009.
    36 *  On-Line Applications Research Corporation (OAR).
     
    1720const char rtems_test_name[] = "PSXONCE 1";
    1821
    19 static pthread_once_t nesting_once = PTHREAD_ONCE_INIT;
     22static pthread_once_t once_a = PTHREAD_ONCE_INIT;
    2023
    21 static void Test_init_routine_nesting( void )
    22 {
    23   int status;
    24   puts( "Test_init_routine_nesting: invoked" );
    25   puts( "Test_init_routine_nesting: pthread_once - EINVAL (init_routine_nesting does not execute)" );
    26   status = pthread_once( &nesting_once, Test_init_routine_nesting );
    27   rtems_test_assert( status == EINVAL );
    28 }
     24static pthread_once_t once_b = PTHREAD_ONCE_INIT;
     25
     26static rtems_id master;
    2927
    3028static int test_init_routine_call_counter = 0;
     
    3432  puts( "Test_init_routine: invoked" );
    3533  ++test_init_routine_call_counter;
     34}
     35
     36static void routine_b( void )
     37{
     38  rtems_status_code sc;
     39
     40  rtems_test_assert( test_init_routine_call_counter == 2 );
     41  ++test_init_routine_call_counter;
     42
     43  sc = rtems_event_send( master, RTEMS_EVENT_0 );
     44  rtems_test_assert( sc == RTEMS_SUCCESSFUL );
     45}
     46
     47static void use_b( rtems_task_argument arg )
     48{
     49  int status;
     50
     51  (void) arg;
     52
     53  status = pthread_once( &once_b, routine_b );
     54  rtems_test_assert( status == 0 );
     55
     56  rtems_task_exit();
     57}
     58
     59static void routine_a( void )
     60{
     61  rtems_status_code sc;
     62  rtems_id id;
     63  rtems_event_set events;
     64
     65  rtems_test_assert( test_init_routine_call_counter == 1 );
     66  ++test_init_routine_call_counter;
     67
     68  master = rtems_task_self();
     69
     70  sc = rtems_task_create(
     71    rtems_build_name( 'T', 'A', 'S', 'K' ),
     72    RTEMS_MINIMUM_PRIORITY,
     73    RTEMS_MINIMUM_STACK_SIZE,
     74    RTEMS_DEFAULT_MODES,
     75    RTEMS_DEFAULT_ATTRIBUTES,
     76    &id
     77  );
     78  assert(sc == RTEMS_SUCCESSFUL);
     79
     80  sc = rtems_task_start( id, use_b, 0 );
     81  assert( sc == RTEMS_SUCCESSFUL );
     82
     83  events = 0;
     84  sc = rtems_event_receive(
     85    RTEMS_EVENT_0,
     86    RTEMS_EVENT_ANY | RTEMS_WAIT,
     87    RTEMS_NO_TIMEOUT,
     88    &events
     89  );
     90  rtems_test_assert( sc == RTEMS_SUCCESSFUL );
     91  rtems_test_assert( events == RTEMS_EVENT_0 );
     92
     93  rtems_test_assert( test_init_routine_call_counter == 3 );
    3694}
    3795
     
    63121  rtems_test_assert( test_init_routine_call_counter == 1 );
    64122
    65   puts( "Init: pthread_once - SUCCESSFUL (init_routine_nesting executes)" );
    66   status = pthread_once( &nesting_once, Test_init_routine_nesting );
    67   rtems_test_assert( !status );
     123  status = pthread_once( &once_a, routine_a );
     124  rtems_test_assert( status == 0 );
     125  rtems_test_assert( test_init_routine_call_counter == 3 );
    68126
    69127  TEST_END();
  • testsuites/psxtests/psxonce01/psxonce01.scn

    r3ecb207 re4ad14cc  
    1 
    2 
    3 *** TEST POSIX ONCE 01 ***
    4 Init: pthread_once - SUCCESSFUL (init_routine_nesting executes)
    5 Test_init_routine_nesting: invoked
     1*** BEGIN OF TEST PSXONCE 1 ***
     2*** TEST VERSION: 5.0.0.e214ff4b636011bd149e3683c89aa982e361fd1c
     3*** TEST STATE: EXPECTED-PASS
     4*** TEST BUILD:
     5*** TEST TOOLS: 7.4.0 20181206 (RTEMS 5, RSB c41b9d0df7e5b4a5056ca50c2534380a44e92769, Newlib 3e24fbf6f)
    66Init: pthread_once - EINVAL (NULL once_control)
    77Init: pthread_once - EINVAL (NULL init_routine)
    88Init: pthread_once - SUCCESSFUL (init_routine executes)
    99Test_init_routine: invoked
     10Init: call counter: 1
    1011Init: pthread_once - SUCCESSFUL (init_routine does not execute)
    11 *** END OF TEST POSIX ONCE 01 ***
     12Init: call counter: 1
     13
     14*** END OF TEST PSXONCE 1 ***
  • testsuites/psxtests/psxonce01/system.h

    r3ecb207 re4ad14cc  
    2222#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
    2323
    24 #define CONFIGURE_MAXIMUM_TASKS 1
     24#define CONFIGURE_MAXIMUM_TASKS 2
    2525
    2626#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
  • testsuites/sptests/spcxx01/init.cc

    r3ecb207 re4ad14cc  
    3030#endif
    3131
     32#include <future>
    3233#include <new>
    3334
     
    4243};
    4344
    44 extern "C" void Init(rtems_task_argument arg)
     45static void test_aligned_new(void)
    4546{
    46   TEST_BEGIN();
    47 
    4847  int *i = static_cast<decltype(i)>(
    4948    ::operator new(sizeof(*i), std::align_val_t(256))
     
    5958  rtems_test_assert(reinterpret_cast<uintptr_t>(s) % 256 == 0);
    6059  delete s;
     60}
     61
     62static void test_future(void)
     63{
     64  std::future<int> f = std::async(std::launch::async, []{ return 12358; });
     65  rtems_test_assert(f.get() == 12358);
     66}
     67
     68extern "C" void Init(rtems_task_argument arg)
     69{
     70  TEST_BEGIN();
     71
     72  test_aligned_new();
     73  test_future();
    6174
    6275  TEST_END();
     
    7083#define CONFIGURE_MAXIMUM_TASKS 1
    7184
     85#define CONFIGURE_MAXIMUM_POSIX_THREADS 1
     86
    7287#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
    7388
Note: See TracChangeset for help on using the changeset viewer.