Ticket #3334: 3334-Fix-pthread_once-deadlock.patch

File 3334-Fix-pthread_once-deadlock.patch, 3.7 KB (added by Stavros Passas, on 03/13/18 at 13:28:56)

Proposed fix

  • cpukit/score/src/once.c

    From 2e3a4e2fbba72eb9bd43d10d829800b25b4fdd9f Mon Sep 17 00:00:00 2001
    From: Alexandru-Sever Horin <alex-sever-h@users.noreply.github.com>
    Date: Fri, 9 Mar 2018 15:29:58 +0200
    Subject: Fix pthread_once deadlock. Fixes #3334
    
    ---
     cpukit/score/src/once.c              | 36 +++++++++++++++++++++++++++++-------
     testsuites/psxtests/psxonce01/init.c | 15 ---------------
     2 files changed, 29 insertions(+), 22 deletions(-)
    
    diff --git a/cpukit/score/src/once.c b/cpukit/score/src/once.c
    index 60ae7a7..7e0ff35 100644
    a b  
    1414#include <rtems/score/onceimpl.h>
    1515#include <rtems/score/apimutex.h>
    1616
     17#include <rtems/score/percpu.h>
     18#include <rtems/score/threaddispatch.h>
     19#include <rtems/score/threadimpl.h>
     20
    1721#include <errno.h>
    1822
    1923#define ONCE_STATE_NOT_RUN  0
    int _Once( int *once_state, void ( *init_routine )( void ) ) 
    3236     *  1. The init has not run and the state is ONCE_STATE_NOT_RUN.
    3337     *  2. The init has finished and the state is ONCE_STATE_COMPLETE (already
    3438     *     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.
     39     *  3. The init is being run by another thread in the state
     40     *     ONCE_STATE_RUNNING.
     41     *     We yield until the state becomes ONCE_STATE_COMPLETE
     42     *  4. The init is being run by this thread in the state ONCE_STATE_RUNNING
     43     *     so we are nesting. This is an undefined behavior by the API.
     44     *     The thread will block indefinitely
    3745     */
    38 
    3946    switch ( *once_state ) {
    4047      case ONCE_STATE_NOT_RUN:
    4148        *once_state = ONCE_STATE_RUNNING;
     49        _Once_Unlock();
     50        /*
     51         * Leave the init_routine to run with once unlocked
     52         * otherwise a dead-lock possible
     53         * if 2 threads run inter-dependent posix_once routines
     54         */
    4255        ( *init_routine )();
     56
     57        /* There is no read-modify-write here,
     58         * because we caught ownership when state when RUNNING was set
     59         */
    4360        *once_state = ONCE_STATE_COMPLETE;
    4461        break;
    4562      case ONCE_STATE_RUNNING:
    46         eno = EINVAL;
     63        _Once_Unlock();
     64        while( *once_state != ONCE_STATE_COMPLETE) {
     65          Per_CPU_Control *cpu_self;
     66
     67          cpu_self = _Thread_Dispatch_disable();
     68          _Thread_Yield( _Per_CPU_Get_executing( cpu_self ) );
     69          _Thread_Dispatch_direct( cpu_self );
     70        }
    4771        break;
    4872      default:
     73        _Once_Unlock();
    4974        break;
    5075    }
    51 
    52     _Once_Unlock();
    5376  }
    54 
    5577  return eno;
    5678}
  • testsuites/psxtests/psxonce01/init.c

    diff --git a/testsuites/psxtests/psxonce01/init.c b/testsuites/psxtests/psxonce01/init.c
    index 1c90769..0cd4921 100644
    a b  
    1616
    1717const char rtems_test_name[] = "PSXONCE 1";
    1818
    19 static pthread_once_t nesting_once = PTHREAD_ONCE_INIT;
    20 
    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 }
    29 
    3019static int test_init_routine_call_counter = 0;
    3120
    3221static void Test_init_routine( void )
    rtems_task Init(rtems_task_argument argument) 
    6251  printf( "Init: call counter: %d\n", test_init_routine_call_counter );
    6352  rtems_test_assert( test_init_routine_call_counter == 1 );
    6453
    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 );
    68 
    6954  TEST_END();
    7055  rtems_test_exit( 0 );
    7156}