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
|
|
14 | 14 | #include <rtems/score/onceimpl.h> |
15 | 15 | #include <rtems/score/apimutex.h> |
16 | 16 | |
| 17 | #include <rtems/score/percpu.h> |
| 18 | #include <rtems/score/threaddispatch.h> |
| 19 | #include <rtems/score/threadimpl.h> |
| 20 | |
17 | 21 | #include <errno.h> |
18 | 22 | |
19 | 23 | #define ONCE_STATE_NOT_RUN 0 |
… |
… |
int _Once( int *once_state, void ( *init_routine )( void ) ) |
32 | 36 | * 1. The init has not run and the state is ONCE_STATE_NOT_RUN. |
33 | 37 | * 2. The init has finished and the state is ONCE_STATE_COMPLETE (already |
34 | 38 | * 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 |
37 | 45 | */ |
38 | | |
39 | 46 | switch ( *once_state ) { |
40 | 47 | case ONCE_STATE_NOT_RUN: |
41 | 48 | *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 | */ |
42 | 55 | ( *init_routine )(); |
| 56 | |
| 57 | /* There is no read-modify-write here, |
| 58 | * because we caught ownership when state when RUNNING was set |
| 59 | */ |
43 | 60 | *once_state = ONCE_STATE_COMPLETE; |
44 | 61 | break; |
45 | 62 | 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 | } |
47 | 71 | break; |
48 | 72 | default: |
| 73 | _Once_Unlock(); |
49 | 74 | break; |
50 | 75 | } |
51 | | |
52 | | _Once_Unlock(); |
53 | 76 | } |
54 | | |
55 | 77 | return eno; |
56 | 78 | } |
diff --git a/testsuites/psxtests/psxonce01/init.c b/testsuites/psxtests/psxonce01/init.c
index 1c90769..0cd4921 100644
a
|
b
|
|
16 | 16 | |
17 | 17 | const char rtems_test_name[] = "PSXONCE 1"; |
18 | 18 | |
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 | | |
30 | 19 | static int test_init_routine_call_counter = 0; |
31 | 20 | |
32 | 21 | static void Test_init_routine( void ) |
… |
… |
rtems_task Init(rtems_task_argument argument) |
62 | 51 | printf( "Init: call counter: %d\n", test_init_routine_call_counter ); |
63 | 52 | rtems_test_assert( test_init_routine_call_counter == 1 ); |
64 | 53 | |
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 | | |
69 | 54 | TEST_END(); |
70 | 55 | rtems_test_exit( 0 ); |
71 | 56 | } |