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_send                                      PORTABLE C      */
39 /*                                                           6.1          */
40 /*  AUTHOR                                                                */
41 /*                                                                        */
42 /*    William E. Lamie, Microsoft Corporation                             */
43 /*                                                                        */
44 /*  DESCRIPTION                                                           */
45 /*                                                                        */
46 /*    This function places a message into the specified queue.  If there  */
47 /*    is no room 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 /*    source_ptr                        Pointer to message source         */
54 /*    wait_option                       Suspension option                 */
55 /*                                                                        */
56 /*  OUTPUT                                                                */
57 /*                                                                        */
58 /*    status                            Completion status                 */
59 /*                                                                        */
60 /*  CALLS                                                                 */
61 /*                                                                        */
62 /*    _tx_thread_system_resume          Resume thread routine             */
63 /*    _tx_thread_system_ni_resume       Non-interruptable resume thread   */
64 /*    _tx_thread_system_suspend         Suspend thread routine            */
65 /*    _tx_thread_system_ni_suspend      Non-interruptable suspend thread  */
66 /*                                                                        */
67 /*  CALLED BY                                                             */
68 /*                                                                        */
69 /*    Application Code                                                    */
70 /*                                                                        */
71 /*  RELEASE HISTORY                                                       */
72 /*                                                                        */
73 /*    DATE              NAME                      DESCRIPTION             */
74 /*                                                                        */
75 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
76 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
77 /*                                            resulting in version 6.1    */
78 /*                                                                        */
79 /**************************************************************************/
_tx_queue_send(TX_QUEUE * queue_ptr,VOID * source_ptr,ULONG wait_option)80 UINT  _tx_queue_send(TX_QUEUE *queue_ptr, VOID *source_ptr, ULONG wait_option)
81 {
82 
83 TX_INTERRUPT_SAVE_AREA
84 
85 TX_THREAD       *thread_ptr;
86 ULONG           *source;
87 ULONG           *destination;
88 UINT            size;
89 UINT            suspended_count;
90 TX_THREAD       *next_thread;
91 TX_THREAD       *previous_thread;
92 UINT            status;
93 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
94 VOID            (*queue_send_notify)(struct TX_QUEUE_STRUCT *notify_queue_ptr);
95 #endif
96 
97 
98     /* Default the status to TX_SUCCESS.  */
99     status =  TX_SUCCESS;
100 
101     /* Disable interrupts to place message in the queue.  */
102     TX_DISABLE
103 
104 #ifdef TX_QUEUE_ENABLE_PERFORMANCE_INFO
105 
106     /* Increment the total messages sent counter.  */
107     _tx_queue_performance_messages_sent_count++;
108 
109     /* Increment the number of messages sent to this queue.  */
110     queue_ptr -> tx_queue_performance_messages_sent_count++;
111 #endif
112 
113     /* If trace is enabled, insert this event into the trace buffer.  */
114     TX_TRACE_IN_LINE_INSERT(TX_TRACE_QUEUE_SEND, queue_ptr, TX_POINTER_TO_ULONG_CONVERT(source_ptr), wait_option, queue_ptr -> tx_queue_enqueued, TX_TRACE_QUEUE_EVENTS)
115 
116     /* Log this kernel call.  */
117     TX_EL_QUEUE_SEND_INSERT
118 
119     /* Pickup the thread suspension count.  */
120     suspended_count =  queue_ptr -> tx_queue_suspended_count;
121 
122     /* Determine if there is room in the queue.  */
123     if (queue_ptr -> tx_queue_available_storage != TX_NO_MESSAGES)
124     {
125 
126         /* There is room for the message in the queue.  */
127 
128         /* Determine if there are suspended on this queue.  */
129         if (suspended_count == TX_NO_SUSPENSIONS)
130         {
131 
132             /* No suspended threads, simply place the message in the queue.  */
133 
134             /* Reduce the amount of available storage.  */
135             queue_ptr -> tx_queue_available_storage--;
136 
137             /* Increase the enqueued count.  */
138             queue_ptr -> tx_queue_enqueued++;
139 
140             /* Setup source and destination pointers.  */
141             source =       TX_VOID_TO_ULONG_POINTER_CONVERT(source_ptr);
142             destination =  queue_ptr -> tx_queue_write;
143             size =         queue_ptr -> tx_queue_message_size;
144 
145             /* Copy message. Note that the source and destination pointers are
146                incremented by the macro.  */
147             TX_QUEUE_MESSAGE_COPY(source, destination, size)
148 
149             /* Determine if we are at the end.  */
150             if (destination == queue_ptr -> tx_queue_end)
151             {
152 
153                 /* Yes, wrap around to the beginning.  */
154                 destination =  queue_ptr -> tx_queue_start;
155             }
156 
157             /* Adjust the write pointer.  */
158             queue_ptr -> tx_queue_write =  destination;
159 
160 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
161 
162             /* Pickup the notify callback routine for this queue.  */
163             queue_send_notify =  queue_ptr -> tx_queue_send_notify;
164 #endif
165 
166             /* No thread suspended, just return to caller.  */
167 
168             /* Restore interrupts.  */
169             TX_RESTORE
170 
171 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
172 
173             /* Determine if a notify callback is required.  */
174             if (queue_send_notify != TX_NULL)
175             {
176 
177                 /* Call application queue send notification.  */
178                 (queue_send_notify)(queue_ptr);
179             }
180 #endif
181         }
182         else
183         {
184 
185             /* There is a thread suspended on an empty queue. Simply
186                copy the message to the suspended thread's destination
187                pointer.  */
188 
189             /* Pickup the head of the suspension list.  */
190             thread_ptr =  queue_ptr -> tx_queue_suspension_list;
191 
192             /* See if this is the only suspended thread on the list.  */
193             suspended_count--;
194             if (suspended_count == TX_NO_SUSPENSIONS)
195             {
196 
197                 /* Yes, the only suspended thread.  */
198 
199                 /* Update the head pointer.  */
200                 queue_ptr -> tx_queue_suspension_list =  TX_NULL;
201             }
202             else
203             {
204 
205                 /* At least one more thread is on the same expiration list.  */
206 
207                 /* Update the list head pointer.  */
208                 queue_ptr -> tx_queue_suspension_list =  thread_ptr -> tx_thread_suspended_next;
209 
210                 /* Update the links of the adjacent threads.  */
211                 next_thread =                            thread_ptr -> tx_thread_suspended_next;
212                 queue_ptr -> tx_queue_suspension_list =  next_thread;
213 
214                 /* Update the links of the adjacent threads.  */
215                 previous_thread =                               thread_ptr -> tx_thread_suspended_previous;
216                 next_thread -> tx_thread_suspended_previous =   previous_thread;
217                 previous_thread -> tx_thread_suspended_next =   next_thread;
218             }
219 
220             /* Decrement the suspension count.  */
221             queue_ptr -> tx_queue_suspended_count =  suspended_count;
222 
223             /* Prepare for resumption of the thread.  */
224 
225             /* Clear cleanup routine to avoid timeout.  */
226             thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
227 
228             /* Setup source and destination pointers.  */
229             source =       TX_VOID_TO_ULONG_POINTER_CONVERT(source_ptr);
230             destination =  TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_additional_suspend_info);
231             size =         queue_ptr -> tx_queue_message_size;
232 
233             /* Copy message. Note that the source and destination pointers are
234                incremented by the macro.  */
235             TX_QUEUE_MESSAGE_COPY(source, destination, size)
236 
237             /* Put return status into the thread control block.  */
238             thread_ptr -> tx_thread_suspend_status =  TX_SUCCESS;
239 
240 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
241 
242             /* Pickup the notify callback routine for this queue.  */
243             queue_send_notify =  queue_ptr -> tx_queue_send_notify;
244 #endif
245 
246 #ifdef TX_NOT_INTERRUPTABLE
247 
248             /* Resume the thread!  */
249             _tx_thread_system_ni_resume(thread_ptr);
250 
251             /* Restore interrupts.  */
252             TX_RESTORE
253 #else
254 
255             /* Temporarily disable preemption.  */
256             _tx_thread_preempt_disable++;
257 
258             /* Restore interrupts.  */
259             TX_RESTORE
260 
261             /* Resume thread.  */
262             _tx_thread_system_resume(thread_ptr);
263 #endif
264 
265 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
266 
267             /* Determine if a notify callback is required.  */
268             if (queue_send_notify != TX_NULL)
269             {
270 
271                 /* Call application queue send notification.  */
272                 (queue_send_notify)(queue_ptr);
273             }
274 #endif
275         }
276     }
277 
278     /* At this point, the queue is full. Determine if suspension is requested.  */
279     else if (wait_option != TX_NO_WAIT)
280     {
281 
282         /* Determine if the preempt disable flag is non-zero.  */
283         if (_tx_thread_preempt_disable != ((UINT) 0))
284         {
285 
286             /* Restore interrupts.  */
287             TX_RESTORE
288 
289             /* Suspension is not allowed if the preempt disable flag is non-zero at this point - return error completion.  */
290             status =  TX_QUEUE_FULL;
291         }
292         else
293         {
294 
295             /* Yes, prepare for suspension of this thread.  */
296 
297 #ifdef TX_QUEUE_ENABLE_PERFORMANCE_INFO
298 
299             /* Increment the total number of queue full suspensions.  */
300             _tx_queue_performance_full_suspension_count++;
301 
302             /* Increment the number of full suspensions on this queue.  */
303             queue_ptr -> tx_queue_performance_full_suspension_count++;
304 #endif
305 
306             /* Pickup thread pointer.  */
307             TX_THREAD_GET_CURRENT(thread_ptr)
308 
309             /* Setup cleanup routine pointer.  */
310             thread_ptr -> tx_thread_suspend_cleanup =  &(_tx_queue_cleanup);
311 
312             /* Setup cleanup information, i.e. this queue control
313                block and the source pointer.  */
314             thread_ptr -> tx_thread_suspend_control_block =    (VOID *) queue_ptr;
315             thread_ptr -> tx_thread_additional_suspend_info =  (VOID *) source_ptr;
316             thread_ptr -> tx_thread_suspend_option =           TX_FALSE;
317 
318 #ifndef TX_NOT_INTERRUPTABLE
319 
320             /* Increment the suspension sequence number, which is used to identify
321                this suspension event.  */
322             thread_ptr -> tx_thread_suspension_sequence++;
323 #endif
324 
325             /* Setup suspension list.  */
326             if (suspended_count == TX_NO_SUSPENSIONS)
327             {
328 
329                 /* No other threads are suspended.  Setup the head pointer and
330                    just setup this threads pointers to itself.  */
331                 queue_ptr -> tx_queue_suspension_list =         thread_ptr;
332                 thread_ptr -> tx_thread_suspended_next =        thread_ptr;
333                 thread_ptr -> tx_thread_suspended_previous =    thread_ptr;
334             }
335             else
336             {
337 
338                 /* This list is not NULL, add current thread to the end. */
339                 next_thread =                                   queue_ptr -> tx_queue_suspension_list;
340                 thread_ptr -> tx_thread_suspended_next =        next_thread;
341                 previous_thread =                               next_thread -> tx_thread_suspended_previous;
342                 thread_ptr -> tx_thread_suspended_previous =    previous_thread;
343                 previous_thread -> tx_thread_suspended_next =   thread_ptr;
344                 next_thread -> tx_thread_suspended_previous =   thread_ptr;
345             }
346 
347             /* Increment the suspended thread count.  */
348             queue_ptr -> tx_queue_suspended_count =  suspended_count + ((UINT) 1);
349 
350             /* Set the state to suspended.  */
351             thread_ptr -> tx_thread_state =    TX_QUEUE_SUSP;
352 
353 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
354 
355             /* Pickup the notify callback routine for this queue.  */
356             queue_send_notify =  queue_ptr -> tx_queue_send_notify;
357 #endif
358 
359 #ifdef TX_NOT_INTERRUPTABLE
360 
361             /* Call actual non-interruptable thread suspension routine.  */
362             _tx_thread_system_ni_suspend(thread_ptr, wait_option);
363 
364             /* Restore interrupts.  */
365             TX_RESTORE
366 #else
367 
368             /* Set the suspending flag.  */
369             thread_ptr -> tx_thread_suspending =  TX_TRUE;
370 
371             /* Setup the timeout period.  */
372             thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option;
373 
374             /* Temporarily disable preemption.  */
375             _tx_thread_preempt_disable++;
376 
377             /* Restore interrupts.  */
378             TX_RESTORE
379 
380             /* Call actual thread suspension routine.  */
381             _tx_thread_system_suspend(thread_ptr);
382 #endif
383 
384 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
385 
386             /* Determine if a notify callback is required.  */
387             if (thread_ptr -> tx_thread_suspend_status == TX_SUCCESS)
388             {
389 
390                 /* Determine if there is a notify callback.  */
391                 if (queue_send_notify != TX_NULL)
392                 {
393 
394                     /* Call application queue send notification.  */
395                     (queue_send_notify)(queue_ptr);
396                 }
397             }
398 #endif
399 
400             /* Return the completion status.  */
401             status =  thread_ptr -> tx_thread_suspend_status;
402         }
403     }
404     else
405     {
406 
407         /* Otherwise, just return a queue full error message to the caller.  */
408 
409 #ifdef TX_QUEUE_ENABLE_PERFORMANCE_INFO
410 
411         /* Increment the number of full non-suspensions on this queue.  */
412         queue_ptr -> tx_queue_performance_full_error_count++;
413 
414         /* Increment the total number of full non-suspensions.  */
415         _tx_queue_performance_full_error_count++;
416 #endif
417 
418         /* Restore interrupts.  */
419         TX_RESTORE
420 
421         /* Return error completion.  */
422         status =  TX_QUEUE_FULL;
423     }
424 
425     /* Return completion status.  */
426     return(status);
427 }
428 
429