forked from Imagelibrary/rtems
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. Close #2471.
This commit is contained in:
@@ -100,6 +100,12 @@ void _Event_Seize(
|
|||||||
|
|
||||||
_Thread_Set_state( executing, block_state );
|
_Thread_Set_state( executing, block_state );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See _Event_Surrender() and _Thread_Timeout(), corresponding atomic
|
||||||
|
* variable is Thread_Control::Wait::flags.
|
||||||
|
*/
|
||||||
|
_Atomic_Fence( ATOMIC_ORDER_ACQUIRE );
|
||||||
|
|
||||||
success = _Thread_Wait_flags_try_change(
|
success = _Thread_Wait_flags_try_change(
|
||||||
executing,
|
executing,
|
||||||
intend_to_block,
|
intend_to_block,
|
||||||
|
|||||||
@@ -34,6 +34,20 @@ static void _Event_Satisfy(
|
|||||||
*(rtems_event_set *) the_thread->Wait.return_argument = seized_events;
|
*(rtems_event_set *) the_thread->Wait.return_argument = seized_events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool _Event_Is_blocking_on_event(
|
||||||
|
const Thread_Control *the_thread,
|
||||||
|
Thread_Wait_flags wait_class
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Thread_Wait_flags wait_flags;
|
||||||
|
Thread_Wait_flags wait_mask;
|
||||||
|
|
||||||
|
wait_flags = _Thread_Wait_flags_get( the_thread );
|
||||||
|
wait_mask = THREAD_WAIT_CLASS_MASK | THREAD_WAIT_STATE_READY_AGAIN;
|
||||||
|
|
||||||
|
return ( wait_flags & wait_mask ) == wait_class;
|
||||||
|
}
|
||||||
|
|
||||||
static bool _Event_Is_satisfied(
|
static bool _Event_Is_satisfied(
|
||||||
const Thread_Control *the_thread,
|
const Thread_Control *the_thread,
|
||||||
rtems_event_set pending_events,
|
rtems_event_set pending_events,
|
||||||
@@ -61,7 +75,6 @@ void _Event_Surrender(
|
|||||||
{
|
{
|
||||||
rtems_event_set pending_events;
|
rtems_event_set pending_events;
|
||||||
rtems_event_set seized_events;
|
rtems_event_set seized_events;
|
||||||
Thread_Wait_flags wait_flags;
|
|
||||||
bool unblock;
|
bool unblock;
|
||||||
|
|
||||||
_Thread_Lock_acquire_default_critical( the_thread, lock_context );
|
_Thread_Lock_acquire_default_critical( the_thread, lock_context );
|
||||||
@@ -69,36 +82,34 @@ void _Event_Surrender(
|
|||||||
_Event_sets_Post( event_in, &event->pending_events );
|
_Event_sets_Post( event_in, &event->pending_events );
|
||||||
pending_events = event->pending_events;
|
pending_events = event->pending_events;
|
||||||
|
|
||||||
wait_flags = _Thread_Wait_flags_get( the_thread );
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
( wait_flags & THREAD_WAIT_CLASS_MASK ) == wait_class
|
_Event_Is_blocking_on_event( the_thread, wait_class )
|
||||||
&& _Event_Is_satisfied( the_thread, pending_events, &seized_events )
|
&& _Event_Is_satisfied( the_thread, pending_events, &seized_events )
|
||||||
) {
|
) {
|
||||||
Thread_Wait_flags intend_to_block;
|
Thread_Wait_flags ready_again;
|
||||||
Thread_Wait_flags blocked;
|
|
||||||
bool success;
|
bool success;
|
||||||
|
|
||||||
intend_to_block = wait_class | THREAD_WAIT_STATE_INTEND_TO_BLOCK;
|
_Event_Satisfy( the_thread, event, pending_events, seized_events );
|
||||||
blocked = wait_class | THREAD_WAIT_STATE_BLOCKED;
|
|
||||||
|
|
||||||
|
/* See _Event_Seize() */
|
||||||
|
_Atomic_Fence( ATOMIC_ORDER_RELEASE );
|
||||||
|
|
||||||
|
ready_again = wait_class | THREAD_WAIT_STATE_READY_AGAIN;
|
||||||
success = _Thread_Wait_flags_try_change_critical(
|
success = _Thread_Wait_flags_try_change_critical(
|
||||||
the_thread,
|
the_thread,
|
||||||
intend_to_block,
|
wait_class | THREAD_WAIT_STATE_INTEND_TO_BLOCK,
|
||||||
wait_class | THREAD_WAIT_STATE_READY_AGAIN
|
ready_again
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( success ) {
|
if ( success ) {
|
||||||
_Event_Satisfy( the_thread, event, pending_events, seized_events );
|
|
||||||
unblock = false;
|
unblock = false;
|
||||||
} else if ( _Thread_Wait_flags_get( the_thread ) == blocked ) {
|
|
||||||
_Event_Satisfy( the_thread, event, pending_events, seized_events );
|
|
||||||
_Thread_Wait_flags_set(
|
|
||||||
the_thread,
|
|
||||||
wait_class | THREAD_WAIT_STATE_READY_AGAIN
|
|
||||||
);
|
|
||||||
unblock = true;
|
|
||||||
} else {
|
} else {
|
||||||
unblock = false;
|
_Assert(
|
||||||
|
_Thread_Wait_flags_get( the_thread )
|
||||||
|
== wait_class | THREAD_WAIT_STATE_BLOCKED
|
||||||
|
);
|
||||||
|
_Thread_Wait_flags_set( the_thread, ready_again );
|
||||||
|
unblock = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unblock = false;
|
unblock = false;
|
||||||
|
|||||||
@@ -39,35 +39,44 @@ void _Thread_Timeout( Objects_Id id, void *arg )
|
|||||||
ISR_lock_Control *thread_lock;
|
ISR_lock_Control *thread_lock;
|
||||||
ISR_lock_Context lock_context;
|
ISR_lock_Context lock_context;
|
||||||
Thread_Wait_flags wait_flags;
|
Thread_Wait_flags wait_flags;
|
||||||
Thread_Wait_flags wait_class;
|
|
||||||
Thread_Wait_flags intend_to_block;
|
|
||||||
Thread_Wait_flags blocked;
|
|
||||||
bool success;
|
|
||||||
bool unblock;
|
bool unblock;
|
||||||
|
|
||||||
the_thread = arg;
|
the_thread = arg;
|
||||||
thread_lock = _Thread_Lock_acquire( the_thread, &lock_context );
|
thread_lock = _Thread_Lock_acquire( the_thread, &lock_context );
|
||||||
|
|
||||||
wait_flags = _Thread_Wait_flags_get( the_thread );
|
wait_flags = _Thread_Wait_flags_get( the_thread );
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
wait_class = wait_flags & THREAD_WAIT_CLASS_MASK;
|
||||||
intend_to_block = wait_class | THREAD_WAIT_STATE_INTEND_TO_BLOCK;
|
ready_again = wait_class | THREAD_WAIT_STATE_READY_AGAIN;
|
||||||
blocked = wait_class | THREAD_WAIT_STATE_BLOCKED;
|
|
||||||
success = _Thread_Wait_flags_try_change_critical(
|
success = _Thread_Wait_flags_try_change_critical(
|
||||||
the_thread,
|
the_thread,
|
||||||
intend_to_block,
|
wait_class | THREAD_WAIT_STATE_INTEND_TO_BLOCK,
|
||||||
wait_class | THREAD_WAIT_STATE_READY_AGAIN
|
ready_again
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( success ) {
|
if ( success ) {
|
||||||
_Thread_Do_timeout( the_thread );
|
|
||||||
unblock = false;
|
unblock = false;
|
||||||
} else if ( _Thread_Wait_flags_get( the_thread ) == blocked ) {
|
} else {
|
||||||
_Thread_Wait_flags_set(
|
_Assert(
|
||||||
the_thread,
|
_Thread_Wait_flags_get( the_thread )
|
||||||
wait_class | THREAD_WAIT_STATE_READY_AGAIN
|
== wait_class | THREAD_WAIT_STATE_BLOCKED
|
||||||
);
|
);
|
||||||
_Thread_Do_timeout( the_thread );
|
_Thread_Wait_flags_set( the_thread, ready_again );
|
||||||
unblock = true;
|
unblock = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
unblock = false;
|
unblock = false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user