source: rtems/cpukit/score/src/threadqops.c @ 1fcac5ad

5
Last change on this file since 1fcac5ad was 1fcac5ad, checked in by Sebastian Huber <sebastian.huber@…>, on 07/25/16 at 14:35:37

score: Turn thread lock into thread wait lock

The _Thread_Lock_acquire() function had a potentially infinite run-time
due to the lack of fairness at atomic operations level.

Update #2412.
Update #2556.
Update #2765.

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