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