[PATCH] score: Avoid some deadlocks in _Once()
Sebastian Huber
sebastian.huber at embedded-brains.de
Tue Feb 12 12:45:41 UTC 2019
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/include/rtems/score/onceimpl.h | 10 ++-
cpukit/rtems/src/timerserver.c | 5 +-
cpukit/score/src/once.c | 117 ++++++++++++++++++----------
testsuites/psxtests/psxonce01/init.c | 82 ++++++++++++++++---
testsuites/psxtests/psxonce01/psxonce01.scn | 15 ++--
testsuites/psxtests/psxonce01/system.h | 2 +-
testsuites/sptests/spcxx01/init.cc | 21 ++++-
testsuites/sptests/spcxx01/spcxx01.doc | 2 +
8 files changed, 183 insertions(+), 71 deletions(-)
diff --git a/cpukit/include/rtems/score/onceimpl.h b/cpukit/include/rtems/score/onceimpl.h
index 60f1378506..f3afe1cd13 100644
--- a/cpukit/include/rtems/score/onceimpl.h
+++ b/cpukit/include/rtems/score/onceimpl.h
@@ -7,7 +7,7 @@
*/
/*
- * Copyright (c) 2014 embedded brains GmbH. All rights reserved.
+ * Copyright (c) 2014, 2019 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
@@ -23,6 +23,8 @@
#ifndef _RTEMS_ONCE_H
#define _RTEMS_ONCE_H
+#include <rtems/score/thread.h>
+
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
@@ -37,11 +39,11 @@ extern "C" {
* @{
*/
-int _Once( unsigned char *once_state, void (*init_routine)(void) );
+int _Once( unsigned char *once_state, void ( *init_routine )( void ) );
-void _Once_Lock( void );
+Thread_Life_state _Once_Lock( void );
-void _Once_Unlock( void );
+void _Once_Unlock( Thread_Life_state thread_life_state );
/** @} */
diff --git a/cpukit/rtems/src/timerserver.c b/cpukit/rtems/src/timerserver.c
index 09e792aa1c..55c3d96b9b 100644
--- a/cpukit/rtems/src/timerserver.c
+++ b/cpukit/rtems/src/timerserver.c
@@ -228,10 +228,11 @@ rtems_status_code rtems_timer_initiate_server(
)
{
rtems_status_code status;
+ Thread_Life_state thread_life_state;
- _Once_Lock();
+ thread_life_state = _Once_Lock();
status = _Timer_server_Initiate( priority, stack_size, attribute_set );
- _Once_Unlock();
+ _Once_Unlock( thread_life_state );
return status;
}
diff --git a/cpukit/score/src/once.c b/cpukit/score/src/once.c
index 5237c11878..5df44c96e7 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 <rtems/score/onceimpl.h>
-#include <rtems/score/apimutex.h>
+#include <rtems/score/threadimpl.h>
+#include <rtems/thread.h>
+
+#define ONCE_STATE_INIT 0
-#include <errno.h>
+#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_state );
+ ( *init_routine )();
+ thread_life_state = _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 );
}
diff --git a/testsuites/psxtests/psxonce01/init.c b/testsuites/psxtests/psxonce01/init.c
index 1c90769d38..41ff5b146b 100644
--- a/testsuites/psxtests/psxonce01/init.c
+++ b/testsuites/psxtests/psxonce01/init.c
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2019 embedded brains GmbH
+ * Copyright (C) 2019 Sebastian Huber
+ *
* COPYRIGHT (c) 1989-2009.
* On-Line Applications Research Corporation (OAR).
*
@@ -16,16 +19,11 @@
const char rtems_test_name[] = "PSXONCE 1";
-static pthread_once_t nesting_once = PTHREAD_ONCE_INIT;
+static pthread_once_t once_a = PTHREAD_ONCE_INIT;
-static void Test_init_routine_nesting( void )
-{
- int status;
- puts( "Test_init_routine_nesting: invoked" );
- puts( "Test_init_routine_nesting: pthread_once - EINVAL (init_routine_nesting does not execute)" );
- status = pthread_once( &nesting_once, Test_init_routine_nesting );
- rtems_test_assert( status == EINVAL );
-}
+static pthread_once_t once_b = PTHREAD_ONCE_INIT;
+
+static rtems_id master;
static int test_init_routine_call_counter = 0;
@@ -35,6 +33,66 @@ static void Test_init_routine( void )
++test_init_routine_call_counter;
}
+static void routine_b( void )
+{
+ rtems_status_code sc;
+
+ rtems_test_assert( test_init_routine_call_counter == 2 );
+ ++test_init_routine_call_counter;
+
+ sc = rtems_event_send( master, RTEMS_EVENT_0 );
+ rtems_test_assert( sc == RTEMS_SUCCESSFUL );
+}
+
+static void use_b( rtems_task_argument arg )
+{
+ int status;
+
+ (void) arg;
+
+ status = pthread_once( &once_b, routine_b );
+ rtems_test_assert( status == 0 );
+
+ rtems_task_exit();
+}
+
+static void routine_a( void )
+{
+ rtems_status_code sc;
+ rtems_id id;
+ rtems_event_set events;
+
+ rtems_test_assert( test_init_routine_call_counter == 1 );
+ ++test_init_routine_call_counter;
+
+ master = rtems_task_self();
+
+ sc = rtems_task_create(
+ rtems_build_name( 'T', 'A', 'S', 'K' ),
+ RTEMS_MINIMUM_PRIORITY,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &id
+ );
+ assert(sc == RTEMS_SUCCESSFUL);
+
+ sc = rtems_task_start( id, use_b, 0 );
+ assert( sc == RTEMS_SUCCESSFUL );
+
+ events = 0;
+ sc = rtems_event_receive(
+ RTEMS_EVENT_0,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
+ rtems_test_assert( sc == RTEMS_SUCCESSFUL );
+ rtems_test_assert( events == RTEMS_EVENT_0 );
+
+ rtems_test_assert( test_init_routine_call_counter == 3 );
+}
+
rtems_task Init(rtems_task_argument argument)
{
int status;
@@ -62,9 +120,9 @@ rtems_task Init(rtems_task_argument argument)
printf( "Init: call counter: %d\n", test_init_routine_call_counter );
rtems_test_assert( test_init_routine_call_counter == 1 );
- puts( "Init: pthread_once - SUCCESSFUL (init_routine_nesting executes)" );
- status = pthread_once( &nesting_once, Test_init_routine_nesting );
- rtems_test_assert( !status );
+ status = pthread_once( &once_a, routine_a );
+ rtems_test_assert( status == 0 );
+ rtems_test_assert( test_init_routine_call_counter == 3 );
TEST_END();
rtems_test_exit( 0 );
diff --git a/testsuites/psxtests/psxonce01/psxonce01.scn b/testsuites/psxtests/psxonce01/psxonce01.scn
index 2c5d47d2d1..a7afa154cb 100644
--- a/testsuites/psxtests/psxonce01/psxonce01.scn
+++ b/testsuites/psxtests/psxonce01/psxonce01.scn
@@ -1,11 +1,14 @@
-
-
-*** TEST POSIX ONCE 01 ***
-Init: pthread_once - SUCCESSFUL (init_routine_nesting executes)
-Test_init_routine_nesting: invoked
+*** BEGIN OF TEST PSXONCE 1 ***
+*** TEST VERSION: 5.0.0.e214ff4b636011bd149e3683c89aa982e361fd1c
+*** TEST STATE: EXPECTED-PASS
+*** TEST BUILD:
+*** TEST TOOLS: 7.4.0 20181206 (RTEMS 5, RSB c41b9d0df7e5b4a5056ca50c2534380a44e92769, Newlib 3e24fbf6f)
Init: pthread_once - EINVAL (NULL once_control)
Init: pthread_once - EINVAL (NULL init_routine)
Init: pthread_once - SUCCESSFUL (init_routine executes)
Test_init_routine: invoked
+Init: call counter: 1
Init: pthread_once - SUCCESSFUL (init_routine does not execute)
-*** END OF TEST POSIX ONCE 01 ***
+Init: call counter: 1
+
+*** END OF TEST PSXONCE 1 ***
diff --git a/testsuites/psxtests/psxonce01/system.h b/testsuites/psxtests/psxonce01/system.h
index 10d611b86d..fe95285d5f 100644
--- a/testsuites/psxtests/psxonce01/system.h
+++ b/testsuites/psxtests/psxonce01/system.h
@@ -21,7 +21,7 @@
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
-#define CONFIGURE_MAXIMUM_TASKS 1
+#define CONFIGURE_MAXIMUM_TASKS 2
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
diff --git a/testsuites/sptests/spcxx01/init.cc b/testsuites/sptests/spcxx01/init.cc
index 7962810da0..9443cd0f73 100644
--- a/testsuites/sptests/spcxx01/init.cc
+++ b/testsuites/sptests/spcxx01/init.cc
@@ -29,6 +29,7 @@
#include "config.h"
#endif
+#include <future>
#include <new>
#include <rtems.h>
@@ -41,10 +42,8 @@ struct alignas(256) S {
int i;
};
-extern "C" void Init(rtems_task_argument arg)
+static void test_aligned_new(void)
{
- TEST_BEGIN();
-
int *i = static_cast<decltype(i)>(
::operator new(sizeof(*i), std::align_val_t(256))
);
@@ -58,6 +57,20 @@ extern "C" void Init(rtems_task_argument arg)
rtems_test_assert(s != nullptr);
rtems_test_assert(reinterpret_cast<uintptr_t>(s) % 256 == 0);
delete s;
+}
+
+static void test_future(void)
+{
+ std::future<bool> f = std::async(std::launch::async, []{ return true; });
+ f.get();
+}
+
+extern "C" void Init(rtems_task_argument arg)
+{
+ TEST_BEGIN();
+
+ test_aligned_new();
+ test_future();
TEST_END();
exit(0);
@@ -69,6 +82,8 @@ extern "C" void Init(rtems_task_argument arg)
#define CONFIGURE_MAXIMUM_TASKS 1
+#define CONFIGURE_MAXIMUM_POSIX_THREADS 1
+
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
diff --git a/testsuites/sptests/spcxx01/spcxx01.doc b/testsuites/sptests/spcxx01/spcxx01.doc
index 669e6820ab..73e6a3f83a 100644
--- a/testsuites/sptests/spcxx01/spcxx01.doc
+++ b/testsuites/sptests/spcxx01/spcxx01.doc
@@ -5,7 +5,9 @@ test set name: spcxx01
directives:
- ::operator new()
+ - std::async()
concepts:
- Ensure that the aligned new operator works.
+ - Ensure that std::async() works.
--
2.16.4
More information about the devel
mailing list