source: rtems/cpukit/score/src/threadqops.c @ 424ffe4d

Last change on this file since 424ffe4d was 424ffe4d, checked in by Sebastian Huber <sebastian.huber@…>, on Aug 11, 2016 at 8:26:57 AM

score: Introduce thread queue surrender operation

This is an optimization for _Thread_queue_Surrender(). It helps to
encapsulate the priority boosting in the priority inheritance thread
queue operations.

  • Property mode set to 100644
File size: 11.3 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_queue_Heads *heads,
75  Thread_Control     *the_thread,
76  void             ( *extract )( Thread_queue_Heads *, Thread_Control * )
77)
78{
79  _Assert( heads != NULL );
80
81  the_thread->Wait.spare_heads = RTEMS_CONTAINER_OF(
82    _Chain_Get_first_unprotected( &heads->Free_chain ),
83    Thread_queue_Heads,
84    Free_node
85  );
86
87  if ( _Chain_Is_empty( &heads->Free_chain ) ) {
88    queue->heads = NULL;
89  }
90
91  ( *extract )( heads, the_thread );
92}
93
94static void _Thread_queue_FIFO_do_initialize(
95  Thread_queue_Heads *heads
96)
97{
98  _Chain_Initialize_empty( &heads->Heads.Fifo );
99}
100
101static void _Thread_queue_FIFO_do_enqueue(
102  Thread_queue_Heads *heads,
103  Thread_Control     *the_thread
104)
105{
106  _Chain_Initialize_node( &the_thread->Wait.Node.Chain );
107  _Chain_Append_unprotected(
108    &heads->Heads.Fifo,
109    &the_thread->Wait.Node.Chain
110  );
111}
112
113static void _Thread_queue_FIFO_do_extract(
114  Thread_queue_Heads *heads,
115  Thread_Control     *the_thread
116)
117{
118  _Chain_Extract_unprotected( &the_thread->Wait.Node.Chain );
119}
120
121static void _Thread_queue_FIFO_enqueue(
122  Thread_queue_Queue *queue,
123  Thread_Control     *the_thread,
124  Thread_queue_Path  *path
125)
126{
127  path->update_priority = NULL;
128
129  _Thread_queue_Queue_enqueue(
130    queue,
131    the_thread,
132    _Thread_queue_FIFO_do_initialize,
133    _Thread_queue_FIFO_do_enqueue
134  );
135}
136
137static void _Thread_queue_FIFO_extract(
138  Thread_queue_Queue *queue,
139  Thread_Control     *the_thread
140)
141{
142  _Thread_queue_Queue_extract(
143    queue,
144    queue->heads,
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_Control *_Thread_queue_FIFO_surrender(
164  Thread_queue_Queue *queue,
165  Thread_queue_Heads *heads,
166  Thread_Control     *previous_owner
167)
168{
169  Thread_Control *first;
170
171  first = _Thread_queue_FIFO_first( heads );
172  _Thread_queue_Queue_extract(
173    queue,
174    heads,
175    first,
176    _Thread_queue_FIFO_do_extract
177  );
178
179  return first;
180}
181
182static Thread_queue_Priority_queue *_Thread_queue_Priority_queue(
183  Thread_queue_Heads   *heads,
184  const Thread_Control *the_thread
185)
186{
187#if defined(RTEMS_SMP)
188  return &heads->Priority[
189    _Scheduler_Get_index( _Scheduler_Get_own( the_thread ) )
190  ];
191#else
192  (void) the_thread;
193
194  return &heads->Heads.Priority;
195#endif
196}
197
198static bool _Thread_queue_Priority_less(
199  const void        *left,
200  const RBTree_Node *right
201)
202{
203  const Priority_Control *the_left;
204  const Thread_Control   *the_right;
205
206  the_left = left;
207  the_right = THREAD_RBTREE_NODE_TO_THREAD( right );
208
209  return *the_left < the_right->current_priority;
210}
211
212static void _Thread_queue_Priority_priority_change(
213  Thread_queue_Queue *queue,
214  Thread_Control     *the_thread,
215  Priority_Control    new_priority
216)
217{
218  Thread_queue_Heads          *heads = queue->heads;
219  Thread_queue_Priority_queue *priority_queue;
220
221  _Assert( heads != NULL );
222
223  priority_queue = _Thread_queue_Priority_queue( heads, the_thread );
224
225  _RBTree_Extract(
226    &priority_queue->Queue,
227    &the_thread->Wait.Node.RBTree
228  );
229  _RBTree_Insert_inline(
230    &priority_queue->Queue,
231    &the_thread->Wait.Node.RBTree,
232    &new_priority,
233    _Thread_queue_Priority_less
234  );
235}
236
237static void _Thread_queue_Priority_do_initialize(
238  Thread_queue_Heads *heads
239)
240{
241#if defined(RTEMS_SMP)
242  _Chain_Initialize_empty( &heads->Heads.Fifo );
243#else
244  _RBTree_Initialize_empty( &heads->Heads.Priority.Queue );
245#endif
246}
247
248static void _Thread_queue_Priority_do_enqueue(
249  Thread_queue_Heads *heads,
250  Thread_Control     *the_thread
251)
252{
253  Thread_queue_Priority_queue *priority_queue =
254    _Thread_queue_Priority_queue( heads, the_thread );
255  Priority_Control current_priority;
256
257#if defined(RTEMS_SMP)
258  if ( _RBTree_Is_empty( &priority_queue->Queue ) ) {
259    _Chain_Append_unprotected( &heads->Heads.Fifo, &priority_queue->Node );
260  }
261#endif
262
263  current_priority = the_thread->current_priority;
264  _RBTree_Initialize_node( &the_thread->Wait.Node.RBTree );
265  _RBTree_Insert_inline(
266    &priority_queue->Queue,
267    &the_thread->Wait.Node.RBTree,
268    &current_priority,
269    _Thread_queue_Priority_less
270  );
271}
272
273static void _Thread_queue_Priority_do_extract(
274  Thread_queue_Heads *heads,
275  Thread_Control     *the_thread
276)
277{
278  Thread_queue_Priority_queue *priority_queue =
279    _Thread_queue_Priority_queue( heads, the_thread );
280
281  _RBTree_Extract(
282    &priority_queue->Queue,
283    &the_thread->Wait.Node.RBTree
284  );
285
286#if defined(RTEMS_SMP)
287  _Chain_Extract_unprotected( &priority_queue->Node );
288
289  if ( !_RBTree_Is_empty( &priority_queue->Queue ) ) {
290    _Chain_Append_unprotected( &heads->Heads.Fifo, &priority_queue->Node );
291  }
292#endif
293}
294
295static void _Thread_queue_Priority_enqueue(
296  Thread_queue_Queue *queue,
297  Thread_Control     *the_thread,
298  Thread_queue_Path  *path
299)
300{
301  path->update_priority = NULL;
302
303  _Thread_queue_Queue_enqueue(
304    queue,
305    the_thread,
306    _Thread_queue_Priority_do_initialize,
307    _Thread_queue_Priority_do_enqueue
308  );
309}
310
311static void _Thread_queue_Priority_extract(
312  Thread_queue_Queue *queue,
313  Thread_Control     *the_thread
314)
315{
316  _Thread_queue_Queue_extract(
317    queue,
318    queue->heads,
319    the_thread,
320    _Thread_queue_Priority_do_extract
321  );
322}
323
324static Thread_Control *_Thread_queue_Priority_first(
325  Thread_queue_Heads *heads
326)
327{
328  Thread_queue_Priority_queue *priority_queue;
329  RBTree_Node                 *first;
330
331#if defined(RTEMS_SMP)
332  _Assert( !_Chain_Is_empty( &heads->Heads.Fifo ) );
333  priority_queue = (Thread_queue_Priority_queue *)
334    _Chain_First( &heads->Heads.Fifo );
335#else
336  priority_queue = &heads->Heads.Priority;
337#endif
338
339  _Assert( !_RBTree_Is_empty( &priority_queue->Queue ) );
340  first = _RBTree_Minimum( &priority_queue->Queue );
341
342  return THREAD_RBTREE_NODE_TO_THREAD( first );
343}
344
345static Thread_Control *_Thread_queue_Priority_surrender(
346  Thread_queue_Queue *queue,
347  Thread_queue_Heads *heads,
348  Thread_Control     *previous_owner
349)
350{
351  Thread_Control *first;
352
353  first = _Thread_queue_Priority_first( heads );
354  _Thread_queue_Queue_extract(
355    queue,
356    heads,
357    first,
358    _Thread_queue_Priority_do_extract
359  );
360
361  return first;
362}
363
364static void _Thread_queue_Priority_inherit_enqueue(
365  Thread_queue_Queue *queue,
366  Thread_Control     *the_thread,
367  Thread_queue_Path  *path
368)
369{
370  Thread_queue_Heads *heads;
371  Thread_Control     *owner;
372  Priority_Control    priority;
373
374  heads = _Thread_queue_Queue_enqueue(
375    queue,
376    the_thread,
377    _Thread_queue_Priority_do_initialize,
378    _Thread_queue_Priority_do_enqueue
379  );
380
381  owner = queue->owner;
382
383#if defined(RTEMS_SMP)
384  if ( _Chain_Has_only_one_node( &heads->Heads.Fifo ) ) {
385    priority = the_thread->current_priority;
386  } else {
387    priority = _Scheduler_Map_priority(
388      _Scheduler_Get_own( the_thread ),
389      PRIORITY_PSEUDO_ISR
390    );
391  }
392#else
393  (void) heads;
394
395  priority = the_thread->current_priority;
396#endif
397
398  if ( priority < owner->current_priority ) {
399    path->update_priority = owner;
400
401    owner->priority_restore_hint = true;
402    _Atomic_Fence( ATOMIC_ORDER_ACQ_REL );
403
404    _Scheduler_Thread_set_priority( owner, priority, false );
405
406    ( *owner->Wait.operations->priority_change )(
407      owner->Wait.queue,
408      owner,
409      priority
410    );
411  } else {
412    path->update_priority = NULL;
413  }
414}
415
416static void _Thread_queue_Boost_priority(
417  Thread_queue_Heads *heads,
418  Thread_Control     *the_thread
419)
420{
421#if defined(RTEMS_SMP)
422  if ( !_Chain_Has_only_one_node( &heads->Heads.Fifo ) ) {
423    const Scheduler_Control *scheduler;
424    Priority_Control         boost_priority;
425
426    the_thread->priority_restore_hint = true;
427    _Atomic_Fence( ATOMIC_ORDER_ACQ_REL );
428
429    scheduler = _Scheduler_Get_own( the_thread );
430    boost_priority = _Scheduler_Map_priority( scheduler, PRIORITY_PSEUDO_ISR );
431
432    _Scheduler_Thread_set_priority( the_thread, boost_priority, false );
433  }
434#else
435  (void) heads;
436  (void) the_thread;
437#endif
438}
439
440static Thread_Control *_Thread_queue_Priority_inherit_surrender(
441  Thread_queue_Queue *queue,
442  Thread_queue_Heads *heads,
443  Thread_Control     *previous_owner
444)
445{
446  Thread_Control *first;
447
448  first = _Thread_queue_Priority_first( heads );
449  _Thread_queue_Boost_priority( heads, first );
450  _Thread_queue_Queue_extract(
451    queue,
452    heads,
453    first,
454    _Thread_queue_Priority_do_extract
455  );
456
457  return first;
458}
459
460const Thread_queue_Operations _Thread_queue_Operations_default = {
461  .priority_change = _Thread_queue_Do_nothing_priority_change,
462  .extract = _Thread_queue_Do_nothing_extract
463  /*
464   * The default operations are only used in _Thread_Change_priority() and
465   * _Thread_Timeout() and don't have a thread queue associated with them, so
466   * the enqueue and first operations are superfluous.
467   */
468};
469
470const Thread_queue_Operations _Thread_queue_Operations_FIFO = {
471  .priority_change = _Thread_queue_Do_nothing_priority_change,
472  .enqueue = _Thread_queue_FIFO_enqueue,
473  .extract = _Thread_queue_FIFO_extract,
474  .surrender = _Thread_queue_FIFO_surrender,
475  .first = _Thread_queue_FIFO_first
476};
477
478const Thread_queue_Operations _Thread_queue_Operations_priority = {
479  .priority_change = _Thread_queue_Priority_priority_change,
480  .enqueue = _Thread_queue_Priority_enqueue,
481  .extract = _Thread_queue_Priority_extract,
482  .surrender = _Thread_queue_Priority_surrender,
483  .first = _Thread_queue_Priority_first
484};
485
486const Thread_queue_Operations _Thread_queue_Operations_priority_inherit = {
487  .priority_change = _Thread_queue_Priority_priority_change,
488  .enqueue = _Thread_queue_Priority_inherit_enqueue,
489  .extract = _Thread_queue_Priority_extract,
490  .surrender = _Thread_queue_Priority_inherit_surrender,
491  .first = _Thread_queue_Priority_first
492};
Note: See TracBrowser for help on using the repository browser.