source: rtems/cpukit/score/src/threadqenqueue.c @ 3134eb8

4.115
Last change on this file since 3134eb8 was 3134eb8, checked in by Sebastian Huber <sebastian.huber@…>, on 03/24/15 at 09:47:02

score: Use a dedicated ISR lock for thread queues

This makes it possible to use the standard thread queues in combination
with objects using fine grained locking. There is no change for
uni-processor configurations.

Update #2273.

  • 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  _Thread_queue_Enter_critical_section( tq );
96  _RBTree_Extract( &tq->Queues.Priority, &the_thread->RBNode );
97  _RBTree_Insert(
98    &tq->Queues.Priority,
99    &the_thread->RBNode,
100    _Thread_queue_Compare_priority,
101    false
102  );
103}
104
105void _Thread_queue_Enqueue_with_handler(
106  Thread_queue_Control         *the_thread_queue,
107  Thread_Control               *the_thread,
108  Watchdog_Interval             timeout,
109  Thread_queue_Timeout_callout  handler
110)
111{
112  ISR_lock_Context                 lock_context;
113  Thread_blocking_operation_States sync_state;
114
115#if defined(RTEMS_MULTIPROCESSING)
116  if ( _Thread_MP_Is_receive( the_thread ) && the_thread->receive_packet )
117    the_thread = _Thread_MP_Allocate_proxy( the_thread_queue->state );
118  else
119#endif
120  /*
121   *  Set the blocking state for this thread queue in the thread.
122   */
123  _Thread_Set_state( the_thread, the_thread_queue->state );
124
125  /*
126   *  If the thread wants to timeout, then schedule its timer.
127   */
128  if ( timeout ) {
129    _Watchdog_Initialize(
130       &the_thread->Timer,
131       handler,
132       the_thread->Object.id,
133       NULL
134    );
135
136    _Watchdog_Insert_ticks( &the_thread->Timer, timeout );
137  }
138
139  /*
140   * Now initiate the enqueuing and checking if the blocking operation
141   * should be completed or the thread has had its blocking condition
142   * satisfied before we got here.
143   */
144  _Thread_queue_Acquire( &lock_context );
145
146  sync_state = the_thread_queue->sync_state;
147  the_thread_queue->sync_state = THREAD_BLOCKING_OPERATION_SYNCHRONIZED;
148
149  if ( sync_state == THREAD_BLOCKING_OPERATION_NOTHING_HAPPENED ) {
150    /*
151     * Invoke the discipline specific enqueue method.
152     */
153    if ( the_thread_queue->discipline == THREAD_QUEUE_DISCIPLINE_FIFO ) {
154      _Chain_Append_unprotected(
155        &the_thread_queue->Queues.Fifo,
156        &the_thread->Object.Node
157      );
158    } else { /* must be THREAD_QUEUE_DISCIPLINE_PRIORITY */
159      _Thread_Lock_set( the_thread, &_Thread_queue_Lock );
160      _Thread_Priority_set_change_handler(
161        the_thread,
162        _Thread_queue_Requeue_priority,
163        the_thread_queue
164      );
165      _RBTree_Insert(
166        &the_thread_queue->Queues.Priority,
167        &the_thread->RBNode,
168        _Thread_queue_Compare_priority,
169        false
170      );
171    }
172
173    the_thread->Wait.queue = the_thread_queue;
174    the_thread_queue->sync_state = THREAD_BLOCKING_OPERATION_SYNCHRONIZED;
175    _Thread_queue_Release( &lock_context );
176  } else {
177    /* Cancel a blocking operation due to ISR */
178
179    _Assert(
180      sync_state == THREAD_BLOCKING_OPERATION_TIMEOUT ||
181        sync_state == THREAD_BLOCKING_OPERATION_SATISFIED
182    );
183
184    _Thread_blocking_operation_Finalize( the_thread, &lock_context );
185  }
186}
187
188void _Thread_queue_Extract_with_return_code(
189  Thread_queue_Control *the_thread_queue,
190  Thread_Control       *the_thread,
191  uint32_t              return_code
192)
193{
194  ISR_lock_Context lock_context;
195
196  _Thread_queue_Acquire( &lock_context );
197
198  if ( !_States_Is_waiting_on_thread_queue( the_thread->current_state ) ) {
199    _Thread_queue_Release( &lock_context );
200    return;
201  }
202
203  if ( the_thread_queue->discipline == THREAD_QUEUE_DISCIPLINE_FIFO ) {
204    _Chain_Extract_unprotected( &the_thread->Object.Node );
205  } else { /* must be THREAD_QUEUE_DISCIPLINE_PRIORITY */
206    _RBTree_Extract(
207      &the_thread->Wait.queue->Queues.Priority,
208      &the_thread->RBNode
209    );
210    _Thread_Priority_restore_default_change_handler( the_thread );
211    _Thread_Lock_restore_default( the_thread );
212  }
213
214  the_thread->Wait.return_code = return_code;
215
216  /*
217   * We found a thread to unblock.
218   *
219   * NOTE: This is invoked with interrupts still disabled.
220   */
221  _Thread_blocking_operation_Finalize( the_thread, &lock_context );
222}
223
224void _Thread_queue_Extract(
225  Thread_queue_Control *the_thread_queue,
226  Thread_Control       *the_thread
227)
228{
229  _Thread_queue_Extract_with_return_code(
230    the_thread_queue,
231    the_thread,
232    the_thread->Wait.return_code
233  );
234}
235
236Thread_Control *_Thread_queue_Dequeue(
237  Thread_queue_Control *the_thread_queue
238)
239{
240  Thread_Control                   *the_thread;
241  ISR_lock_Context                  lock_context;
242  Thread_blocking_operation_States  sync_state;
243
244  the_thread = NULL;
245  _Thread_queue_Acquire( &lock_context );
246
247  /*
248   * Invoke the discipline specific dequeue method.
249   */
250  if ( the_thread_queue->discipline == THREAD_QUEUE_DISCIPLINE_FIFO ) {
251    if ( !_Chain_Is_empty( &the_thread_queue->Queues.Fifo ) ) {
252      the_thread = (Thread_Control *)
253       _Chain_Get_first_unprotected( &the_thread_queue->Queues.Fifo );
254    }
255  } else { /* must be THREAD_QUEUE_DISCIPLINE_PRIORITY */
256    RBTree_Node    *first;
257
258    first = _RBTree_Get( &the_thread_queue->Queues.Priority, RBT_LEFT );
259    if ( first ) {
260      the_thread = THREAD_RBTREE_NODE_TO_THREAD( first );
261      _Thread_Priority_restore_default_change_handler( the_thread );
262      _Thread_Lock_restore_default( the_thread );
263    }
264  }
265
266  if ( the_thread == NULL ) {
267    /*
268     * We did not find a thread to unblock in the queue.  Maybe the executing
269     * thread is about to block on this thread queue.
270     */
271    sync_state = the_thread_queue->sync_state;
272    if ( (sync_state == THREAD_BLOCKING_OPERATION_TIMEOUT) ||
273         (sync_state == THREAD_BLOCKING_OPERATION_NOTHING_HAPPENED) ) {
274      the_thread_queue->sync_state = THREAD_BLOCKING_OPERATION_SATISFIED;
275      the_thread = _Thread_Executing;
276    } else {
277      _Thread_queue_Release( &lock_context );
278      return NULL;
279    }
280  }
281
282  /*
283   * We found a thread to unblock.
284   *
285   * NOTE: This is invoked with interrupts still disabled.
286   */
287  _Thread_blocking_operation_Finalize( the_thread, &lock_context );
288
289  return the_thread;
290}
Note: See TracBrowser for help on using the repository browser.