1 /**************************************************************************/
2 /* */
3 /* Copyright (c) Microsoft Corporation. All rights reserved. */
4 /* */
5 /* This software is licensed under the Microsoft Software License */
6 /* Terms for Microsoft Azure RTOS. Full text of the license can be */
7 /* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
8 /* and in the root directory of this software. */
9 /* */
10 /**************************************************************************/
11
12
13 /**************************************************************************/
14 /**************************************************************************/
15 /** */
16 /** ThreadX Component */
17 /** */
18 /** Queue */
19 /** */
20 /**************************************************************************/
21 /**************************************************************************/
22
23 #define TX_SOURCE_CODE
24
25
26 /* Include necessary system files. */
27
28 #include "tx_api.h"
29 #include "tx_trace.h"
30 #include "tx_thread.h"
31 #include "tx_queue.h"
32
33
34 /**************************************************************************/
35 /* */
36 /* FUNCTION RELEASE */
37 /* */
38 /* _tx_queue_receive PORTABLE C */
39 /* 6.1 */
40 /* AUTHOR */
41 /* */
42 /* William E. Lamie, Microsoft Corporation */
43 /* */
44 /* DESCRIPTION */
45 /* */
46 /* This function receives a message from the specified queue. If there */
47 /* are no messages in the queue, this function waits according to the */
48 /* option specified. */
49 /* */
50 /* INPUT */
51 /* */
52 /* queue_ptr Pointer to queue control block */
53 /* destination_ptr Pointer to message destination */
54 /* **** MUST BE LARGE ENOUGH TO */
55 /* HOLD MESSAGE **** */
56 /* wait_option Suspension option */
57 /* */
58 /* OUTPUT */
59 /* */
60 /* status Completion status */
61 /* */
62 /* CALLS */
63 /* */
64 /* _tx_thread_system_resume Resume thread routine */
65 /* _tx_thread_system_ni_resume Non-interruptable resume thread */
66 /* _tx_thread_system_suspend Suspend thread routine */
67 /* _tx_thread_system_ni_suspend Non-interruptable suspend thread */
68 /* */
69 /* CALLED BY */
70 /* */
71 /* Application Code */
72 /* */
73 /* RELEASE HISTORY */
74 /* */
75 /* DATE NAME DESCRIPTION */
76 /* */
77 /* 05-19-2020 William E. Lamie Initial Version 6.0 */
78 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
79 /* resulting in version 6.1 */
80 /* */
81 /**************************************************************************/
_tx_queue_receive(TX_QUEUE * queue_ptr,VOID * destination_ptr,ULONG wait_option)82 UINT _tx_queue_receive(TX_QUEUE *queue_ptr, VOID *destination_ptr, ULONG wait_option)
83 {
84
85 TX_INTERRUPT_SAVE_AREA
86
87 TX_THREAD *thread_ptr;
88 ULONG *source;
89 ULONG *destination;
90 UINT size;
91 UINT suspended_count;
92 TX_THREAD *next_thread;
93 TX_THREAD *previous_thread;
94 UINT status;
95
96
97 /* Default the status to TX_SUCCESS. */
98 status = TX_SUCCESS;
99
100 /* Disable interrupts to receive message from queue. */
101 TX_DISABLE
102
103 #ifdef TX_QUEUE_ENABLE_PERFORMANCE_INFO
104
105 /* Increment the total messages received counter. */
106 _tx_queue_performance__messages_received_count++;
107
108 /* Increment the number of messages received from this queue. */
109 queue_ptr -> tx_queue_performance_messages_received_count++;
110
111 #endif
112
113 /* If trace is enabled, insert this event into the trace buffer. */
114 TX_TRACE_IN_LINE_INSERT(TX_TRACE_QUEUE_RECEIVE, queue_ptr, TX_POINTER_TO_ULONG_CONVERT(destination_ptr), wait_option, queue_ptr -> tx_queue_enqueued, TX_TRACE_QUEUE_EVENTS)
115
116 /* Log this kernel call. */
117 TX_EL_QUEUE_RECEIVE_INSERT
118
119 /* Pickup the thread suspension count. */
120 suspended_count = queue_ptr -> tx_queue_suspended_count;
121
122 /* Determine if there is anything in the queue. */
123 if (queue_ptr -> tx_queue_enqueued != TX_NO_MESSAGES)
124 {
125
126 /* Determine if there are any suspensions. */
127 if (suspended_count == TX_NO_SUSPENSIONS)
128 {
129
130 /* There is a message waiting in the queue and there are no suspensi. */
131
132 /* Setup source and destination pointers. */
133 source = queue_ptr -> tx_queue_read;
134 destination = TX_VOID_TO_ULONG_POINTER_CONVERT(destination_ptr);
135 size = queue_ptr -> tx_queue_message_size;
136
137 /* Copy message. Note that the source and destination pointers are
138 incremented by the macro. */
139 TX_QUEUE_MESSAGE_COPY(source, destination, size)
140
141 /* Determine if we are at the end. */
142 if (source == queue_ptr -> tx_queue_end)
143 {
144
145 /* Yes, wrap around to the beginning. */
146 source = queue_ptr -> tx_queue_start;
147 }
148
149 /* Setup the queue read pointer. */
150 queue_ptr -> tx_queue_read = source;
151
152 /* Increase the amount of available storage. */
153 queue_ptr -> tx_queue_available_storage++;
154
155 /* Decrease the enqueued count. */
156 queue_ptr -> tx_queue_enqueued--;
157
158 /* Restore interrupts. */
159 TX_RESTORE
160 }
161 else
162 {
163
164 /* At this point we know the queue is full. */
165
166 /* Pickup thread suspension list head pointer. */
167 thread_ptr = queue_ptr -> tx_queue_suspension_list;
168
169 /* Now determine if there is a queue front suspension active. */
170
171 /* Is the front suspension flag set? */
172 if (thread_ptr -> tx_thread_suspend_option == TX_TRUE)
173 {
174
175 /* Yes, a queue front suspension is present. */
176
177 /* Return the message associated with this suspension. */
178
179 /* Setup source and destination pointers. */
180 source = TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_additional_suspend_info);
181 destination = TX_VOID_TO_ULONG_POINTER_CONVERT(destination_ptr);
182 size = queue_ptr -> tx_queue_message_size;
183
184 /* Copy message. Note that the source and destination pointers are
185 incremented by the macro. */
186 TX_QUEUE_MESSAGE_COPY(source, destination, size)
187
188 /* Message is now in the caller's destination. See if this is the only suspended thread
189 on the list. */
190 suspended_count--;
191 if (suspended_count == TX_NO_SUSPENSIONS)
192 {
193
194 /* Yes, the only suspended thread. */
195
196 /* Update the head pointer. */
197 queue_ptr -> tx_queue_suspension_list = TX_NULL;
198 }
199 else
200 {
201
202 /* At least one more thread is on the same expiration list. */
203
204 /* Update the list head pointer. */
205 next_thread = thread_ptr -> tx_thread_suspended_next;
206 queue_ptr -> tx_queue_suspension_list = next_thread;
207
208 /* Update the links of the adjacent threads. */
209 previous_thread = thread_ptr -> tx_thread_suspended_previous;
210 next_thread -> tx_thread_suspended_previous = previous_thread;
211 previous_thread -> tx_thread_suspended_next = next_thread;
212 }
213
214 /* Decrement the suspension count. */
215 queue_ptr -> tx_queue_suspended_count = suspended_count;
216
217 /* Prepare for resumption of the first thread. */
218
219 /* Clear cleanup routine to avoid timeout. */
220 thread_ptr -> tx_thread_suspend_cleanup = TX_NULL;
221
222 /* Put return status into the thread control block. */
223 thread_ptr -> tx_thread_suspend_status = TX_SUCCESS;
224
225 #ifdef TX_NOT_INTERRUPTABLE
226
227 /* Resume the thread! */
228 _tx_thread_system_ni_resume(thread_ptr);
229
230 /* Restore interrupts. */
231 TX_RESTORE
232 #else
233
234 /* Temporarily disable preemption. */
235 _tx_thread_preempt_disable++;
236
237 /* Restore interrupts. */
238 TX_RESTORE
239
240 /* Resume thread. */
241 _tx_thread_system_resume(thread_ptr);
242 #endif
243 }
244 else
245 {
246
247 /* At this point, we know that the queue is full and there
248 are one or more threads suspended trying to send another
249 message to this queue. */
250
251 /* Setup source and destination pointers. */
252 source = queue_ptr -> tx_queue_read;
253 destination = TX_VOID_TO_ULONG_POINTER_CONVERT(destination_ptr);
254 size = queue_ptr -> tx_queue_message_size;
255
256 /* Copy message. Note that the source and destination pointers are
257 incremented by the macro. */
258 TX_QUEUE_MESSAGE_COPY(source, destination, size)
259
260 /* Determine if we are at the end. */
261 if (source == queue_ptr -> tx_queue_end)
262 {
263
264 /* Yes, wrap around to the beginning. */
265 source = queue_ptr -> tx_queue_start;
266 }
267
268 /* Setup the queue read pointer. */
269 queue_ptr -> tx_queue_read = source;
270
271 /* Disable preemption. */
272 _tx_thread_preempt_disable++;
273
274 #ifdef TX_NOT_INTERRUPTABLE
275
276 /* Restore interrupts. */
277 TX_RESTORE
278
279 /* Interrupts are enabled briefly here to keep the interrupt
280 lockout time deterministic. */
281
282 /* Disable interrupts again. */
283 TX_DISABLE
284 #endif
285
286 /* Decrement the preemption disable variable. */
287 _tx_thread_preempt_disable--;
288
289 /* Setup source and destination pointers. */
290 source = TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_additional_suspend_info);
291 destination = queue_ptr -> tx_queue_write;
292 size = queue_ptr -> tx_queue_message_size;
293
294 /* Copy message. Note that the source and destination pointers are
295 incremented by the macro. */
296 TX_QUEUE_MESSAGE_COPY(source, destination, size)
297
298 /* Determine if we are at the end. */
299 if (destination == queue_ptr -> tx_queue_end)
300 {
301
302 /* Yes, wrap around to the beginning. */
303 destination = queue_ptr -> tx_queue_start;
304 }
305
306 /* Adjust the write pointer. */
307 queue_ptr -> tx_queue_write = destination;
308
309 /* Pickup thread pointer. */
310 thread_ptr = queue_ptr -> tx_queue_suspension_list;
311
312 /* Message is now in the queue. See if this is the only suspended thread
313 on the list. */
314 suspended_count--;
315 if (suspended_count == TX_NO_SUSPENSIONS)
316 {
317
318 /* Yes, the only suspended thread. */
319
320 /* Update the head pointer. */
321 queue_ptr -> tx_queue_suspension_list = TX_NULL;
322 }
323 else
324 {
325
326 /* At least one more thread is on the same expiration list. */
327
328 /* Update the list head pointer. */
329 next_thread = thread_ptr -> tx_thread_suspended_next;
330 queue_ptr -> tx_queue_suspension_list = next_thread;
331
332 /* Update the links of the adjacent threads. */
333 previous_thread = thread_ptr -> tx_thread_suspended_previous;
334 next_thread -> tx_thread_suspended_previous = previous_thread;
335 previous_thread -> tx_thread_suspended_next = next_thread;
336 }
337
338 /* Decrement the suspension count. */
339 queue_ptr -> tx_queue_suspended_count = suspended_count;
340
341 /* Prepare for resumption of the first thread. */
342
343 /* Clear cleanup routine to avoid timeout. */
344 thread_ptr -> tx_thread_suspend_cleanup = TX_NULL;
345
346 /* Put return status into the thread control block. */
347 thread_ptr -> tx_thread_suspend_status = TX_SUCCESS;
348
349 #ifdef TX_NOT_INTERRUPTABLE
350
351 /* Resume the thread! */
352 _tx_thread_system_ni_resume(thread_ptr);
353
354 /* Restore interrupts. */
355 TX_RESTORE
356 #else
357
358 /* Temporarily disable preemption. */
359 _tx_thread_preempt_disable++;
360
361 /* Restore interrupts. */
362 TX_RESTORE
363
364 /* Resume thread. */
365 _tx_thread_system_resume(thread_ptr);
366 #endif
367 }
368 }
369 }
370
371 /* Determine if the request specifies suspension. */
372 else if (wait_option != TX_NO_WAIT)
373 {
374
375 /* Determine if the preempt disable flag is non-zero. */
376 if (_tx_thread_preempt_disable != ((UINT) 0))
377 {
378
379 /* Restore interrupts. */
380 TX_RESTORE
381
382 /* Suspension is not allowed if the preempt disable flag is non-zero at this point - return error completion. */
383 status = TX_QUEUE_EMPTY;
384 }
385 else
386 {
387
388 /* Prepare for suspension of this thread. */
389
390 #ifdef TX_QUEUE_ENABLE_PERFORMANCE_INFO
391
392 /* Increment the total queue empty suspensions counter. */
393 _tx_queue_performance_empty_suspension_count++;
394
395 /* Increment the number of empty suspensions on this queue. */
396 queue_ptr -> tx_queue_performance_empty_suspension_count++;
397 #endif
398
399 /* Pickup thread pointer. */
400 TX_THREAD_GET_CURRENT(thread_ptr)
401
402 /* Setup cleanup routine pointer. */
403 thread_ptr -> tx_thread_suspend_cleanup = &(_tx_queue_cleanup);
404
405 /* Setup cleanup information, i.e. this queue control
406 block and the source pointer. */
407 thread_ptr -> tx_thread_suspend_control_block = (VOID *) queue_ptr;
408 thread_ptr -> tx_thread_additional_suspend_info = (VOID *) destination_ptr;
409 thread_ptr -> tx_thread_suspend_option = TX_FALSE;
410
411 #ifndef TX_NOT_INTERRUPTABLE
412
413 /* Increment the suspension sequence number, which is used to identify
414 this suspension event. */
415 thread_ptr -> tx_thread_suspension_sequence++;
416 #endif
417
418 /* Setup suspension list. */
419 if (suspended_count == TX_NO_SUSPENSIONS)
420 {
421
422 /* No other threads are suspended. Setup the head pointer and
423 just setup this threads pointers to itself. */
424 queue_ptr -> tx_queue_suspension_list = thread_ptr;
425 thread_ptr -> tx_thread_suspended_next = thread_ptr;
426 thread_ptr -> tx_thread_suspended_previous = thread_ptr;
427 }
428 else
429 {
430
431 /* This list is not NULL, add current thread to the end. */
432 next_thread = queue_ptr -> tx_queue_suspension_list;
433 thread_ptr -> tx_thread_suspended_next = next_thread;
434 previous_thread = next_thread -> tx_thread_suspended_previous;
435 thread_ptr -> tx_thread_suspended_previous = previous_thread;
436 previous_thread -> tx_thread_suspended_next = thread_ptr;
437 next_thread -> tx_thread_suspended_previous = thread_ptr;
438 }
439
440 /* Increment the suspended thread count. */
441 queue_ptr -> tx_queue_suspended_count = suspended_count + ((UINT) 1);
442
443 /* Set the state to suspended. */
444 thread_ptr -> tx_thread_state = TX_QUEUE_SUSP;
445
446 #ifdef TX_NOT_INTERRUPTABLE
447
448 /* Call actual non-interruptable thread suspension routine. */
449 _tx_thread_system_ni_suspend(thread_ptr, wait_option);
450
451 /* Restore interrupts. */
452 TX_RESTORE
453 #else
454
455 /* Set the suspending flag. */
456 thread_ptr -> tx_thread_suspending = TX_TRUE;
457
458 /* Setup the timeout period. */
459 thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks = wait_option;
460
461 /* Temporarily disable preemption. */
462 _tx_thread_preempt_disable++;
463
464 /* Restore interrupts. */
465 TX_RESTORE
466
467 /* Call actual thread suspension routine. */
468 _tx_thread_system_suspend(thread_ptr);
469 #endif
470
471 /* Return the completion status. */
472 status = thread_ptr -> tx_thread_suspend_status;
473 }
474 }
475 else
476 {
477
478 /* Restore interrupts. */
479 TX_RESTORE
480
481 /* Immediate return, return error completion. */
482 status = TX_QUEUE_EMPTY;
483 }
484
485 /* Return completion status. */
486 return(status);
487 }
488
489