source: rtems/cpukit/score/src/threadqops.c @ 2cb9b86e

5
Last change on this file since 2cb9b86e was ff2e6c64, checked in by Sebastian Huber <sebastian.huber@…>, on 08/02/16 at 09:26:56

score: Fix and simplify thread wait locks

There was a subtile race condition in _Thread_queue_Do_extract_locked().
It must first update the thread wait flags and then restore the default
thread wait state. In the previous implementation this could lead under
rare timing conditions to an ineffective _Thread_Wait_tranquilize()
resulting to a corrupt system state.

Update #2556.

  • Property mode set to 100644
File size: 10.0 KB
Line 
1/*
2 * Copyright (c) 2015, 2016 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <rtems@embedded-brains.de>
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15#if HAVE_CONFIG_H
16  #include "config.h"
17#endif
18
19#include <rtems/score/threadimpl.h>
20#include <rtems/score/assert.h>
21#include <rtems/score/chainimpl.h>
22#include <rtems/score/rbtreeimpl.h>
23#include <rtems/score/schedulerimpl.h>
24
25static void _Thread_queue_Do_nothing_priority_change(
26  Thread_queue_Queue *queue,
27  Thread_Control     *the_thread,
28  Priority_Control    new_priority
29)
30{
31  (void) queue;
32  (void) the_thread;
33  (void) new_priority;
34}
35
36static void _Thread_queue_Do_nothing_extract(
37  Thread_queue_Queue *queue,
38  Thread_Control     *the_thread
39)
40{
41  (void) queue;
42  (void) the_thread;
43}
44
45static Thread_queue_Heads *_Thread_queue_Queue_enqueue(
46  Thread_queue_Queue *queue,
47  Thread_Control     *the_thread,
48  void             ( *initialize )( Thread_queue_Heads * ),
49  void             ( *enqueue )( Thread_queue_Heads *, Thread_Control * )
50)
51{
52  Thread_queue_Heads *heads = queue->heads;
53  Thread_queue_Heads *spare_heads = the_thread->Wait.spare_heads;
54
55  the_thread->Wait.spare_heads = NULL;
56
57  if ( heads == NULL ) {
58    _Assert( spare_heads != NULL );
59    _Assert( _Chain_Is_empty( &spare_heads->Free_chain ) );
60    heads = spare_heads;
61    queue->heads = heads;
62    ( *initialize )( heads );
63  }
64
65  _Chain_Prepend_unprotected( &heads->Free_chain, &spare_heads->Free_node );
66
67  ( *enqueue )( heads, the_thread );
68
69  return heads;
70}
71
72static void _Thread_queue_Queue_extract(
73  Thread_queue_Queue *queue,
74  Thread_Control     *the_thread,
75  void             ( *extract )( Thread_queue_Heads *, Thread_Control * )
76)
77{
78  Thread_queue_Heads *heads = queue->heads;
79
80  _Assert( heads != NULL );
81
82  the_thread->Wait.spare_heads = RTEMS_CONTAINER_OF(
83    _Chain_Get_first_unprotected( &heads->Free_chain ),
84    Thread_queue_Heads,
85    Free_node
86  );
87
88  if ( _Chain_Is_empty( &heads->Free_chain ) ) {
89    queue->heads = NULL;
90  }
91
92  ( *extract )( heads, the_thread );
93}
94
95static void _Thread_queue_FIFO_do_initialize(
96  Thread_queue_Heads *heads
97)
98{
99  _Chain_Initialize_empty( &heads->Heads.Fifo );
100}
101
102static void _Thread_queue_FIFO_do_enqueue(
103  Thread_queue_Heads *heads,
104  Thread_Control     *the_thread
105)
106{
107  _Chain_Initialize_node( &the_thread->Wait.Node.Chain );
108  _Chain_Append_unprotected(
109    &heads->Heads.Fifo,
110    &the_thread->Wait.Node.Chain
111  );
112}
113
114static void _Thread_queue_FIFO_do_extract(
115  Thread_queue_Heads *heads,
116  Thread_Control     *the_thread
117)
118{
119  _Chain_Extract_unprotected( &the_thread->Wait.Node.Chain );
120}
121
122static void _Thread_queue_FIFO_enqueue(
123  Thread_queue_Queue *queue,
124  Thread_Control     *the_thread,
125  Thread_queue_Path  *path
126)
127{
128  path->update_priority = NULL;
129
130  _Thread_queue_Queue_enqueue(
131    queue,
132    the_thread,
133    _Thread_queue_FIFO_do_initialize,
134    _Thread_queue_FIFO_do_enqueue
135  );
136}
137
138static void _Thread_queue_FIFO_extract(
139  Thread_queue_Queue *queue,
140  Thread_Control     *the_thread
141)
142{
143  _Thread_queue_Queue_extract(
144    queue,
145    the_thread,
146    _Thread_queue_FIFO_do_extract
147  );
148}
149
150static Thread_Control *_Thread_queue_FIFO_first(
151  Thread_queue_Heads *heads
152)
153{
154  Chain_Control *fifo = &heads->Heads.Fifo;
155  Chain_Node    *first;
156
157  _Assert( !_Chain_Is_empty( fifo ) );
158  first = _Chain_First( fifo );
159
160  return THREAD_CHAIN_NODE_TO_THREAD( first );
161}
162
163static Thread_queue_Priority_queue *_Thread_queue_Priority_queue(
164  Thread_queue_Heads   *heads,
165  const Thread_Control *the_thread
166)
167{
168#if defined(RTEMS_SMP)
169  return &heads->Priority[
170    _Scheduler_Get_index( _Scheduler_Get_own( the_thread ) )
171  ];
172#else
173  (void) the_thread;
174
175  return &heads->Heads.Priority;
176#endif
177}
178
179static bool _Thread_queue_Priority_less(
180  const void        *left,
181  const RBTree_Node *right
182)
183{
184  const Priority_Control *the_left;
185  const Thread_Control   *the_right;
186
187  the_left = left;
188  the_right = THREAD_RBTREE_NODE_TO_THREAD( right );
189
190  return *the_left < the_right->current_priority;
191}
192
193static void _Thread_queue_Priority_priority_change(
194  Thread_queue_Queue *queue,
195  Thread_Control     *the_thread,
196  Priority_Control    new_priority
197)
198{
199  Thread_queue_Heads          *heads = queue->heads;
200  Thread_queue_Priority_queue *priority_queue;
201
202  _Assert( heads != NULL );
203
204  priority_queue = _Thread_queue_Priority_queue( heads, the_thread );
205
206  _RBTree_Extract(
207    &priority_queue->Queue,
208    &the_thread->Wait.Node.RBTree
209  );
210  _RBTree_Insert_inline(
211    &priority_queue->Queue,
212    &the_thread->Wait.Node.RBTree,
213    &new_priority,
214    _Thread_queue_Priority_less
215  );
216}
217
218static void _Thread_queue_Priority_do_initialize(
219  Thread_queue_Heads *heads
220)
221{
222#if defined(RTEMS_SMP)
223  _Chain_Initialize_empty( &heads->Heads.Fifo );
224#else
225  _RBTree_Initialize_empty( &heads->Heads.Priority.Queue );
226#endif
227}
228
229static void _Thread_queue_Priority_do_enqueue(
230  Thread_queue_Heads *heads,
231  Thread_Control     *the_thread
232)
233{
234  Thread_queue_Priority_queue *priority_queue =
235    _Thread_queue_Priority_queue( heads, the_thread );
236  Priority_Control current_priority;
237
238#if defined(RTEMS_SMP)
239  if ( _RBTree_Is_empty( &priority_queue->Queue ) ) {
240    _Chain_Append_unprotected( &heads->Heads.Fifo, &priority_queue->Node );
241  }
242#endif
243
244  current_priority = the_thread->current_priority;
245  _RBTree_Insert_inline(
246    &priority_queue->Queue,
247    &the_thread->Wait.Node.RBTree,
248    &current_priority,
249    _Thread_queue_Priority_less
250  );
251}
252
253static void _Thread_queue_Priority_do_extract(
254  Thread_queue_Heads *heads,
255  Thread_Control     *the_thread
256)
257{
258  Thread_queue_Priority_queue *priority_queue =
259    _Thread_queue_Priority_queue( heads, the_thread );
260
261  _RBTree_Extract(
262    &priority_queue->Queue,
263    &the_thread->Wait.Node.RBTree
264  );
265
266#if defined(RTEMS_SMP)
267  _Chain_Extract_unprotected( &priority_queue->Node );
268
269  if ( !_RBTree_Is_empty( &priority_queue->Queue ) ) {
270    _Chain_Append_unprotected( &heads->Heads.Fifo, &priority_queue->Node );
271  }
272#endif
273}
274
275static void _Thread_queue_Priority_enqueue(
276  Thread_queue_Queue *queue,
277  Thread_Control     *the_thread,
278  Thread_queue_Path  *path
279)
280{
281  path->update_priority = NULL;
282
283  _Thread_queue_Queue_enqueue(
284    queue,
285    the_thread,
286    _Thread_queue_Priority_do_initialize,
287    _Thread_queue_Priority_do_enqueue
288  );
289}
290
291static void _Thread_queue_Priority_extract(
292  Thread_queue_Queue *queue,
293  Thread_Control     *the_thread
294)
295{
296  _Thread_queue_Queue_extract(
297    queue,
298    the_thread,
299    _Thread_queue_Priority_do_extract
300  );
301}
302
303static Thread_Control *_Thread_queue_Priority_first(
304  Thread_queue_Heads *heads
305)
306{
307  Thread_queue_Priority_queue *priority_queue;
308  RBTree_Node                 *first;
309
310#if defined(RTEMS_SMP)
311  _Assert( !_Chain_Is_empty( &heads->Heads.Fifo ) );
312  priority_queue = (Thread_queue_Priority_queue *)
313    _Chain_First( &heads->Heads.Fifo );
314#else
315  priority_queue = &heads->Heads.Priority;
316#endif
317
318  _Assert( !_RBTree_Is_empty( &priority_queue->Queue ) );
319  first = _RBTree_Minimum( &priority_queue->Queue );
320
321  return THREAD_RBTREE_NODE_TO_THREAD( first );
322}
323
324static void _Thread_queue_Priority_inherit_enqueue(
325  Thread_queue_Queue *queue,
326  Thread_Control     *the_thread,
327  Thread_queue_Path  *path
328)
329{
330  Thread_queue_Heads *heads;
331  Thread_Control     *owner;
332  Priority_Control    priority;
333
334  heads = _Thread_queue_Queue_enqueue(
335    queue,
336    the_thread,
337    _Thread_queue_Priority_do_initialize,
338    _Thread_queue_Priority_do_enqueue
339  );
340
341  owner = queue->owner;
342
343#if defined(RTEMS_SMP)
344  if ( _Chain_Has_only_one_node( &heads->Heads.Fifo ) ) {
345    priority = the_thread->current_priority;
346  } else {
347    priority = _Scheduler_Map_priority(
348      _Scheduler_Get_own( the_thread ),
349      PRIORITY_PSEUDO_ISR
350    );
351  }
352#else
353  (void) heads;
354
355  priority = the_thread->current_priority;
356#endif
357
358  if ( priority < owner->current_priority ) {
359    path->update_priority = owner;
360
361    owner->priority_restore_hint = true;
362    _Atomic_Fence( ATOMIC_ORDER_ACQ_REL );
363
364    _Scheduler_Thread_set_priority( owner, priority, false );
365
366    ( *owner->Wait.operations->priority_change )(
367      owner->Wait.queue,
368      owner,
369      priority
370    );
371  } else {
372    path->update_priority = NULL;
373  }
374}
375
376#if defined(RTEMS_SMP)
377void _Thread_queue_Boost_priority(
378  Thread_queue_Queue *queue,
379  Thread_Control     *the_thread
380)
381{
382  Thread_queue_Heads *heads = queue->heads;
383
384  if ( !_Chain_Has_only_one_node( &heads->Heads.Fifo ) ) {
385    const Scheduler_Control *scheduler;
386    Priority_Control         boost_priority;
387
388    the_thread->priority_restore_hint = true;
389    _Atomic_Fence( ATOMIC_ORDER_ACQ_REL );
390
391    scheduler = _Scheduler_Get_own( the_thread );
392    boost_priority = _Scheduler_Map_priority( scheduler, PRIORITY_PSEUDO_ISR );
393
394    _Scheduler_Thread_set_priority( the_thread, boost_priority, false );
395  }
396}
397#endif
398
399const Thread_queue_Operations _Thread_queue_Operations_default = {
400  .priority_change = _Thread_queue_Do_nothing_priority_change,
401  .extract = _Thread_queue_Do_nothing_extract
402  /*
403   * The default operations are only used in _Thread_Change_priority() and
404   * _Thread_Timeout() and don't have a thread queue associated with them, so
405   * the enqueue and first operations are superfluous.
406   */
407};
408
409const Thread_queue_Operations _Thread_queue_Operations_FIFO = {
410  .priority_change = _Thread_queue_Do_nothing_priority_change,
411  .enqueue = _Thread_queue_FIFO_enqueue,
412  .extract = _Thread_queue_FIFO_extract,
413  .first = _Thread_queue_FIFO_first
414};
415
416const Thread_queue_Operations _Thread_queue_Operations_priority = {
417  .priority_change = _Thread_queue_Priority_priority_change,
418  .enqueue = _Thread_queue_Priority_enqueue,
419  .extract = _Thread_queue_Priority_extract,
420  .first = _Thread_queue_Priority_first
421};
422
423const Thread_queue_Operations _Thread_queue_Operations_priority_inherit = {
424  .priority_change = _Thread_queue_Priority_priority_change,
425  .enqueue = _Thread_queue_Priority_inherit_enqueue,
426  .extract = _Thread_queue_Priority_extract,
427  .first = _Thread_queue_Priority_first
428};
Note: See TracBrowser for help on using the repository browser.