summaryrefslogtreecommitdiffstats
path: root/cpukit/score/src/threadtimeout.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2015-11-10 17:23:12 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2015-11-12 08:21:45 +0100
commitb84a51c8a48b1bba6cf5ecea4a226f47e39ef22b (patch)
tree065e23c74513ff7c04b674cfbf675507073c881c /cpukit/score/src/threadtimeout.c
parentirq-server: Fix race condition on SMP systems (diff)
downloadrtems-b84a51c8a48b1bba6cf5ecea4a226f47e39ef22b.tar.bz2
score: Fix race condition on SMP
We must ensure that the Thread_Control::Wait information update is visible to the target thread before we update its wait flags, otherwise we may return out of date events or a wrong status.
Diffstat (limited to 'cpukit/score/src/threadtimeout.c')
-rw-r--r--cpukit/score/src/threadtimeout.c47
1 files changed, 28 insertions, 19 deletions
diff --git a/cpukit/score/src/threadtimeout.c b/cpukit/score/src/threadtimeout.c
index 0e04998b26..45c2292ffa 100644
--- a/cpukit/score/src/threadtimeout.c
+++ b/cpukit/score/src/threadtimeout.c
@@ -39,35 +39,44 @@ void _Thread_Timeout( Objects_Id id, void *arg )
void *thread_lock;
ISR_lock_Context lock_context;
Thread_Wait_flags wait_flags;
- Thread_Wait_flags wait_class;
- Thread_Wait_flags intend_to_block;
- Thread_Wait_flags blocked;
- bool success;
bool unblock;
the_thread = arg;
thread_lock = _Thread_Lock_acquire( the_thread, &lock_context );
wait_flags = _Thread_Wait_flags_get( the_thread );
- wait_class = wait_flags & THREAD_WAIT_CLASS_MASK;
- intend_to_block = wait_class | THREAD_WAIT_STATE_INTEND_TO_BLOCK;
- blocked = wait_class | THREAD_WAIT_STATE_BLOCKED;
- success = _Thread_Wait_flags_try_change_critical(
- the_thread,
- intend_to_block,
- wait_class | THREAD_WAIT_STATE_READY_AGAIN
- );
- if ( success ) {
+ if ( ( wait_flags & THREAD_WAIT_STATE_READY_AGAIN ) == 0 ) {
+ Thread_Wait_flags wait_class;
+ Thread_Wait_flags ready_again;
+ bool success;
+
_Thread_Do_timeout( the_thread );
- unblock = false;
- } else if ( _Thread_Wait_flags_get( the_thread ) == blocked ) {
- _Thread_Wait_flags_set(
+
+ /*
+ * This fence is only necessary for the events, see _Event_Seize(). The
+ * thread queues use the thread lock for synchronization.
+ */
+ _Atomic_Fence( ATOMIC_ORDER_RELEASE );
+
+ wait_class = wait_flags & THREAD_WAIT_CLASS_MASK;
+ ready_again = wait_class | THREAD_WAIT_STATE_READY_AGAIN;
+ success = _Thread_Wait_flags_try_change_critical(
the_thread,
- wait_class | THREAD_WAIT_STATE_READY_AGAIN
+ wait_class | THREAD_WAIT_STATE_INTEND_TO_BLOCK,
+ ready_again
);
- _Thread_Do_timeout( the_thread );
- unblock = true;
+
+ if ( success ) {
+ unblock = false;
+ } else {
+ _Assert(
+ _Thread_Wait_flags_get( the_thread )
+ == wait_class | THREAD_WAIT_STATE_BLOCKED
+ );
+ _Thread_Wait_flags_set( the_thread, ready_again );
+ unblock = true;
+ }
} else {
unblock = false;
}