source: rtems/cpukit/score/src/threadqenqueue.c @ 54cf0e34

4.115
Last change on this file since 54cf0e34 was 41814fa6, checked in by Sebastian Huber <sebastian.huber@…>, on 04/09/15 at 11:54:17

score: Fix thread queue race condition

On uni-processor configurations the change of the thread blocking state
in _Thread_queue_Requeue_priority() did no harm and was simply useless.
However on SMP configurations this resulted in invalid state changes
leading to a wrong resource ownership.

  • Property mode set to 100644
File size: 8.1 KB
Line 
1/**
2 * @file
3 *
4 * @brief Thread Queue Operations
5 * @ingroup ScoreThreadQ
6 */
7
8/*
9 *  COPYRIGHT (c) 1989-2014.
10 *  On-Line Applications Research Corporation (OAR).
11 *
12 *  The license and distribution terms for this file may be
13 *  found in the file LICENSE in this distribution or at
14 *  http://www.rtems.org/license/LICENSE.
15 */
16
17#if HAVE_CONFIG_H
18#include "config.h"
19#endif
20
21#include <rtems/score/threadqimpl.h>
22#include <rtems/score/assert.h>
23#include <rtems/score/rbtreeimpl.h>
24#include <rtems/score/threadimpl.h>
25#include <rtems/score/watchdogimpl.h>
26
27ISR_LOCK_DEFINE( static, _Thread_queue_Lock, "Thread Queue" )
28
29static void _Thread_queue_Acquire( ISR_lock_Context *lock_context )
30{
31  _ISR_lock_ISR_disable_and_acquire( &_Thread_queue_Lock, lock_context );
32}
33
34static void _Thread_queue_Release( ISR_lock_Context *lock_context )
35{
36  _ISR_lock_Release_and_ISR_enable( &_Thread_queue_Lock, lock_context );
37}
38
39/**
40 *  @brief Finalize a blocking operation.
41 *
42 *  This method is used to finalize a blocking operation that was
43 *  satisfied. It may be used with thread queues or any other synchronization
44 *  object that uses the blocking states and watchdog times for timeout.
45 *
46 *  This method will restore the previous ISR disable level during the cancel
47 *  operation.  Thus it is an implicit _ISR_Enable().
48 *
49 *  @param[in] the_thread is the thread whose blocking is canceled
50 *  @param[in] lock_context is the previous ISR disable level
51 */
52static void _Thread_blocking_operation_Finalize(
53  Thread_Control   *the_thread,
54  ISR_lock_Context *lock_context
55)
56{
57  /*
58   * The thread is not waiting on anything after this completes.
59   */
60  the_thread->Wait.queue = NULL;
61
62  /*
63   *  If the sync state is timed out, this is very likely not needed.
64   *  But better safe than sorry when it comes to critical sections.
65   */
66  if ( _Watchdog_Is_active( &the_thread->Timer ) ) {
67    _Watchdog_Deactivate( &the_thread->Timer );
68    _Thread_queue_Release( lock_context );
69    (void) _Watchdog_Remove( &the_thread->Timer );
70  } else
71    _Thread_queue_Release( lock_context );
72
73  /*
74   *  Global objects with thread queue's should not be operated on from an
75   *  ISR.  But the sync code still must allow short timeouts to be processed
76   *  correctly.
77   */
78
79  _Thread_Unblock( the_thread );
80
81#if defined(RTEMS_MULTIPROCESSING)
82  if ( !_Objects_Is_local_id( the_thread->Object.id ) )
83    _Thread_MP_Free_proxy( the_thread );
84#endif
85}
86
87static void _Thread_queue_Requeue_priority(
88  Thread_Control   *the_thread,
89  Priority_Control  new_priority,
90  void             *context
91)
92{
93  Thread_queue_Control *tq = context;
94
95  _RBTree_Extract( &tq->Queues.Priority, &the_thread->RBNode );
96  _RBTree_Insert(
97    &tq->Queues.Priority,
98    &the_thread->RBNode,
99    _Thread_queue_Compare_priority,
100    false
101  );
102}
103
104void _Thread_queue_Enqueue_with_handler(
105  Thread_queue_Control         *the_thread_queue,
106  Thread_Control               *the_thread,
107  Watchdog_Interval             timeout,
108  Thread_queue_Timeout_callout  handler
109)
110{
111  ISR_lock_Context                 lock_context;
112  Thread_blocking_operation_States sync_state;
113
114#if defined(RTEMS_MULTIPROCESSING)
115  if ( _Thread_MP_Is_receive( the_thread ) && the_thread->receive_packet )
116    the_thread = _Thread_MP_Allocate_proxy( the_thread_queue->state );
117  else
118#endif
119  /*
120   *  Set the blocking state for this thread queue in the thread.
121   */
122  _Thread_Set_state( the_thread, the_thread_queue->state );
123
124  /*
125   *  If the thread wants to timeout, then schedule its timer.
126   */
127  if ( timeout ) {
128    _Watchdog_Initialize(
129       &the_thread->Timer,
130       handler,
131       the_thread->Object.id,
132       NULL
133    );
134
135    _Watchdog_Insert_ticks( &the_thread->Timer, timeout );
136  }
137
138  /*
139   * Now initiate the enqueuing and checking if the blocking operation
140   * should be completed or the thread has had its blocking condition
141   * satisfied before we got here.
142   */
143  _Thread_queue_Acquire( &lock_context );
144
145  sync_state = the_thread_queue->sync_state;
146  the_thread_queue->sync_state = THREAD_BLOCKING_OPERATION_SYNCHRONIZED;
147
148  if ( sync_state == THREAD_BLOCKING_OPERATION_NOTHING_HAPPENED ) {
149    /*
150     * Invoke the discipline specific enqueue method.
151     */
152    if ( the_thread_queue->discipline == THREAD_QUEUE_DISCIPLINE_FIFO ) {
153      _Chain_Append_unprotected(
154        &the_thread_queue->Queues.Fifo,
155        &the_thread->Object.Node
156      );
157    } else { /* must be THREAD_QUEUE_DISCIPLINE_PRIORITY */
158      _Thread_Lock_set( the_thread, &_Thread_queue_Lock );
159      _Thread_Priority_set_change_handler(
160        the_thread,
161        _Thread_queue_Requeue_priority,
162        the_thread_queue
163      );
164      _RBTree_Insert(
165        &the_thread_queue->Queues.Priority,
166        &the_thread->RBNode,
167        _Thread_queue_Compare_priority,
168        false
169      );
170    }
171
172    the_thread->Wait.queue = the_thread_queue;
173    the_thread_queue->sync_state = THREAD_BLOCKING_OPERATION_SYNCHRONIZED;
174    _Thread_queue_Release( &lock_context );
175  } else {
176    /* Cancel a blocking operation due to ISR */
177
178    _Assert(
179      sync_state == THREAD_BLOCKING_OPERATION_TIMEOUT ||
180        sync_state == THREAD_BLOCKING_OPERATION_SATISFIED
181    );
182
183    _Thread_blocking_operation_Finalize( the_thread, &lock_context );
184  }
185}
186
187void _Thread_queue_Extract_with_return_code(
188  Thread_queue_Control *the_thread_queue,
189  Thread_Control       *the_thread,
190  uint32_t              return_code
191)
192{
193  ISR_lock_Context lock_context;
194
195  _Thread_queue_Acquire( &lock_context );
196
197  if ( !_States_Is_waiting_on_thread_queue( the_thread->current_state ) ) {
198    _Thread_queue_Release( &lock_context );
199    return;
200  }
201
202  if ( the_thread_queue->discipline == THREAD_QUEUE_DISCIPLINE_FIFO ) {
203    _Chain_Extract_unprotected( &the_thread->Object.Node );
204  } else { /* must be THREAD_QUEUE_DISCIPLINE_PRIORITY */
205    _RBTree_Extract(
206      &the_thread->Wait.queue->Queues.Priority,
207      &the_thread->RBNode
208    );
209    _Thread_Priority_restore_default_change_handler( the_thread );
210    _Thread_Lock_restore_default( the_thread );
211  }
212
213  the_thread->Wait.return_code = return_code;
214
215  /*
216   * We found a thread to unblock.
217   *
218   * NOTE: This is invoked with interrupts still disabled.
219   */
220  _Thread_blocking_operation_Finalize( the_thread, &lock_context );
221}
222
223void _Thread_queue_Extract(
224  Thread_queue_Control *the_thread_queue,
225  Thread_Control       *the_thread
226)
227{
228  _Thread_queue_Extract_with_return_code(
229    the_thread_queue,
230    the_thread,
231    the_thread->Wait.return_code
232  );
233}
234
235Thread_Control *_Thread_queue_Dequeue(
236  Thread_queue_Control *the_thread_queue
237)
238{
239  Thread_Control                   *the_thread;
240  ISR_lock_Context                  lock_context;
241  Thread_blocking_operation_States  sync_state;
242
243  the_thread = NULL;
244  _Thread_queue_Acquire( &lock_context );
245
246  /*
247   * Invoke the discipline specific dequeue method.
248   */
249  if ( the_thread_queue->discipline == THREAD_QUEUE_DISCIPLINE_FIFO ) {
250    if ( !_Chain_Is_empty( &the_thread_queue->Queues.Fifo ) ) {
251      the_thread = (Thread_Control *)
252       _Chain_Get_first_unprotected( &the_thread_queue->Queues.Fifo );
253    }
254  } else { /* must be THREAD_QUEUE_DISCIPLINE_PRIORITY */
255    RBTree_Node    *first;
256
257    first = _RBTree_Get( &the_thread_queue->Queues.Priority, RBT_LEFT );
258    if ( first ) {
259      the_thread = THREAD_RBTREE_NODE_TO_THREAD( first );
260      _Thread_Priority_restore_default_change_handler( the_thread );
261      _Thread_Lock_restore_default( the_thread );
262    }
263  }
264
265  if ( the_thread == NULL ) {
266    /*
267     * We did not find a thread to unblock in the queue.  Maybe the executing
268     * thread is about to block on this thread queue.
269     */
270    sync_state = the_thread_queue->sync_state;
271    if ( (sync_state == THREAD_BLOCKING_OPERATION_TIMEOUT) ||
272         (sync_state == THREAD_BLOCKING_OPERATION_NOTHING_HAPPENED) ) {
273      the_thread_queue->sync_state = THREAD_BLOCKING_OPERATION_SATISFIED;
274      the_thread = _Thread_Executing;
275    } else {
276      _Thread_queue_Release( &lock_context );
277      return NULL;
278    }
279  }
280
281  /*
282   * We found a thread to unblock.
283   *
284   * NOTE: This is invoked with interrupts still disabled.
285   */
286  _Thread_blocking_operation_Finalize( the_thread, &lock_context );
287
288  return the_thread;
289}
Note: See TracBrowser for help on using the repository browser.