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