source: rtems/cpukit/score/src/futex.c @ bbd6d27a

5
Last change on this file since bbd6d27a was 8f96581, checked in by Sebastian Huber <sebastian.huber@…>, on 04/01/16 at 09:38:47

score: Rework MP thread queue callout support

The thread queue implementation was heavily reworked to support SMP.
This broke the multiprocessing support of the thread queues. This is
fixed by this patch.

A thread proxy is unblocked due to three reasons

1) timeout,
2) request satisfaction, and
3) extraction.

In case 1) no MPCI message must be sent. This is ensured via the
_Thread_queue_MP_callout_do_nothing() callout set during
_Thread_MP_Allocate_proxy().

In case 2) and 3) an MPCI message must be sent. In case we interrupt
the blocking operation during _Thread_queue_Enqueue_critical(), then
this message must be sent by the blocking thread. For this the new
fields Thread_Proxy_control::thread_queue_callout and
Thread_Proxy_control::thread_queue_id are used.

Delete the individual API MP callout types and use
Thread_queue_MP_callout throughout. This type is only defined in
multiprocessing configurations. Prefix the multiprocessing parameters
with mp_ to ease code review. Multiprocessing specific parameters are
optional due to use of a similar macro pattern. There is no overhead
for non-multiprocessing configurations.

  • Property mode set to 100644
File size: 4.5 KB
Line 
1/*
2 * Copyright (c) 2015 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#if HAVE_STRUCT__THREAD_QUEUE_QUEUE
20
21#include <sys/lock.h>
22#include <errno.h>
23
24#include <rtems/score/atomic.h>
25#include <rtems/score/chainimpl.h>
26#include <rtems/score/threadimpl.h>
27#include <rtems/score/threadqimpl.h>
28
29#define FUTEX_TQ_OPERATIONS &_Thread_queue_Operations_FIFO
30
31typedef struct {
32  Thread_queue_Syslock_queue Queue;
33} Futex_Control;
34
35RTEMS_STATIC_ASSERT(
36  offsetof( Futex_Control, Queue )
37    == offsetof( struct _Futex_Control, _Queue ),
38  FUTEX_CONTROL_QUEUE
39);
40
41RTEMS_STATIC_ASSERT(
42  sizeof( Futex_Control ) == sizeof( struct _Futex_Control ),
43  FUTEX_CONTROL_SIZE
44);
45
46static Futex_Control *_Futex_Get( struct _Futex_Control *_futex )
47{
48  return (Futex_Control *) _futex;
49}
50
51static Thread_Control *_Futex_Queue_acquire(
52  Futex_Control    *futex,
53  ISR_lock_Context *lock_context
54)
55{
56  Thread_Control *executing;
57
58  _ISR_lock_ISR_disable( lock_context );
59  executing = _Thread_Executing;
60  _Thread_queue_Queue_acquire_critical(
61    &futex->Queue.Queue,
62    &executing->Potpourri_stats,
63    lock_context
64  );
65
66  return executing;
67}
68
69static void _Futex_Queue_release(
70  Futex_Control    *futex,
71  ISR_lock_Context *lock_context
72)
73{
74  _Thread_queue_Queue_release( &futex->Queue.Queue, lock_context );
75}
76
77int _Futex_Wait( struct _Futex_Control *_futex, int *uaddr, int val )
78{
79  Futex_Control    *futex;
80  ISR_lock_Context  lock_context;
81  Thread_Control   *executing;
82  int               eno;
83
84  futex = _Futex_Get( _futex );
85  executing = _Futex_Queue_acquire( futex, &lock_context );
86
87  if ( *uaddr == val ) {
88    _Thread_queue_Enqueue_critical(
89      &futex->Queue.Queue,
90      FUTEX_TQ_OPERATIONS,
91      executing,
92      STATES_WAITING_FOR_SYS_LOCK_FUTEX,
93      0,
94      0,
95      &lock_context
96    );
97    eno = 0;
98  } else {
99    _Futex_Queue_release( futex, &lock_context );
100    eno = EWOULDBLOCK;
101  }
102
103  return eno;
104}
105
106/*
107 * Use a noinline function to force the compiler to set up and tear down the
108 * large stack frame only in the slow case.
109 */
110static __attribute__((noinline)) int _Futex_Wake_slow(
111  Futex_Control      *futex,
112  int                 count,
113  Thread_queue_Heads *heads,
114  ISR_lock_Context   *lock_context
115)
116{
117  Chain_Control  unblock;
118  Chain_Node    *node;
119  Chain_Node    *tail;
120  int            woken;
121
122  woken = 0;
123  _Chain_Initialize_empty( &unblock );
124
125  while ( count > 0 && heads != NULL ) {
126    const Thread_queue_Operations *operations;
127    Thread_Control                *first;
128    bool                           do_unblock;
129
130    operations = FUTEX_TQ_OPERATIONS;
131    first = ( *operations->first )( heads );
132
133    do_unblock = _Thread_queue_Extract_locked(
134      &futex->Queue.Queue,
135      operations,
136      first,
137      NULL,
138      0
139    );
140    if (do_unblock) {
141      _Chain_Append_unprotected( &unblock, &first->Wait.Node.Chain );
142    }
143
144    ++woken;
145    --count;
146    heads = futex->Queue.Queue.heads;
147  }
148
149  node = _Chain_First( &unblock );
150  tail = _Chain_Tail( &unblock );
151
152  if ( node != tail ) {
153    Per_CPU_Control *cpu_self;
154
155    cpu_self = _Thread_Dispatch_disable_critical( lock_context );
156    _Futex_Queue_release( futex, lock_context );
157
158    do {
159      Thread_Control *thread;
160      Chain_Node     *next;
161
162      next = _Chain_Next( node );
163      thread = THREAD_CHAIN_NODE_TO_THREAD( node );
164      _Thread_Unblock( thread );
165
166      node = next;
167    } while ( node != tail );
168
169    _Thread_Dispatch_enable( cpu_self );
170  } else {
171    _Futex_Queue_release( futex, lock_context );
172  }
173
174  return woken;
175}
176
177int _Futex_Wake( struct _Futex_Control *_futex, int count )
178{
179  Futex_Control      *futex;
180  ISR_lock_Context    lock_context;
181  Thread_queue_Heads *heads;
182
183  futex = _Futex_Get( _futex );
184  _Futex_Queue_acquire( futex, &lock_context );
185
186  /*
187   * For some synchronization objects like barriers the _Futex_Wake() must be
188   * called in the fast path.  Normally there are no threads on the queue, so
189   * check this condition early.
190   */
191  heads = futex->Queue.Queue.heads;
192  if ( __predict_true( heads == NULL ) ) {
193    _Futex_Queue_release( futex, &lock_context );
194
195    return 0;
196  }
197
198  return _Futex_Wake_slow( futex, count, heads, &lock_context );
199}
200
201#endif /* HAVE_STRUCT__THREAD_QUEUE_QUEUE */
Note: See TracBrowser for help on using the repository browser.