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_front_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 at the front of the specified queue. */
46 /*    If there is no room in the queue, this function returns the         */
47 /*    queue full status.                                                  */
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_front_send(TX_QUEUE * queue_ptr,VOID * source_ptr,ULONG wait_option)79 UINT  _tx_queue_front_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_FRONT_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_FRONT_SEND_INSERT
117 
118     /* Pickup the suspended count.  */
119     suspended_count =  queue_ptr -> tx_queue_suspended_count;
120 
121     /* Now check for room in the queue for placing the new message in front.  */
122     if (queue_ptr -> tx_queue_available_storage != ((UINT) 0))
123     {
124 
125         /* Yes there is room in the queue. Now determine if there is a thread waiting
126            for a message.  */
127         if (suspended_count == TX_NO_SUSPENSIONS)
128         {
129 
130             /* No thread suspended while waiting for a message from
131                this queue.  */
132 
133             /* Adjust the read pointer since we are adding to the front of the
134                queue.  */
135 
136             /* See if the read pointer is at the beginning of the queue area.  */
137             if (queue_ptr -> tx_queue_read == queue_ptr -> tx_queue_start)
138             {
139 
140                 /* Adjust the read pointer to the last message at the end of the
141                    queue.  */
142                 queue_ptr -> tx_queue_read =  TX_ULONG_POINTER_SUB(queue_ptr -> tx_queue_end, queue_ptr -> tx_queue_message_size);
143             }
144             else
145             {
146 
147                 /* Not at the beginning of the queue, just move back one message.  */
148                 queue_ptr -> tx_queue_read =  TX_ULONG_POINTER_SUB(queue_ptr -> tx_queue_read, queue_ptr -> tx_queue_message_size);
149             }
150 
151             /* Simply place the message in the queue.  */
152 
153             /* Reduce the amount of available storage.  */
154             queue_ptr -> tx_queue_available_storage--;
155 
156             /* Increase the enqueued count.  */
157             queue_ptr -> tx_queue_enqueued++;
158 
159             /* Setup source and destination pointers.  */
160             source =       TX_VOID_TO_ULONG_POINTER_CONVERT(source_ptr);
161             destination =  queue_ptr -> tx_queue_read;
162             size =         queue_ptr -> tx_queue_message_size;
163 
164             /* Copy message. Note that the source and destination pointers are
165                incremented by the macro.  */
166             TX_QUEUE_MESSAGE_COPY(source, destination, size)
167 
168 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
169 
170             /* Pickup the notify callback routine for this queue.  */
171             queue_send_notify =  queue_ptr -> tx_queue_send_notify;
172 #endif
173 
174             /* Restore interrupts.  */
175             TX_RESTORE
176 
177 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
178 
179             /* Determine if a notify callback is required.  */
180             if (queue_send_notify != TX_NULL)
181             {
182 
183                 /* Call application queue send notification.  */
184                 (queue_send_notify)(queue_ptr);
185             }
186 #endif
187         }
188         else
189         {
190 
191             /* Thread suspended waiting for a message.  Remove it and copy this message
192                into its storage area.  */
193             thread_ptr =  queue_ptr -> tx_queue_suspension_list;
194 
195             /* See if this is the only suspended thread on the list.  */
196             suspended_count--;
197             if (suspended_count == TX_NO_SUSPENSIONS)
198             {
199 
200                 /* Yes, the only suspended thread.  */
201 
202                 /* Update the head pointer.  */
203                 queue_ptr -> tx_queue_suspension_list =  TX_NULL;
204             }
205             else
206             {
207 
208                 /* At least one more thread is on the same expiration list.  */
209 
210                 /* Update the list head pointer.  */
211                 queue_ptr -> tx_queue_suspension_list =  thread_ptr -> tx_thread_suspended_next;
212 
213                 /* Update the links of the adjacent threads.  */
214                 next_thread =                            thread_ptr -> tx_thread_suspended_next;
215                 queue_ptr -> tx_queue_suspension_list =  next_thread;
216 
217                 /* Update the links of the adjacent threads.  */
218                 previous_thread =                               thread_ptr -> tx_thread_suspended_previous;
219                 next_thread -> tx_thread_suspended_previous =   previous_thread;
220                 previous_thread -> tx_thread_suspended_next =   next_thread;
221             }
222 
223             /* Decrement the suspension count.  */
224             queue_ptr -> tx_queue_suspended_count =  suspended_count;
225 
226             /* Prepare for resumption of the thread.  */
227 
228             /* Clear cleanup routine to avoid timeout.  */
229             thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
230 
231 
232 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
233 
234             /* Pickup the notify callback routine for this queue.  */
235             queue_send_notify =  queue_ptr -> tx_queue_send_notify;
236 #endif
237 
238             /* Setup source and destination pointers.  */
239             source =       TX_VOID_TO_ULONG_POINTER_CONVERT(source_ptr);
240             destination =  TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_additional_suspend_info);
241             size =         queue_ptr -> tx_queue_message_size;
242 
243             /* Copy message. Note that the source and destination pointers are
244                incremented by the macro.  */
245             TX_QUEUE_MESSAGE_COPY(source, destination, size)
246 
247             /* Put return status into the thread control block.  */
248             thread_ptr -> tx_thread_suspend_status =  TX_SUCCESS;
249 
250 #ifdef TX_NOT_INTERRUPTABLE
251 
252             /* Resume the thread!  */
253             _tx_thread_system_ni_resume(thread_ptr);
254 
255             /* Restore interrupts.  */
256             TX_RESTORE
257 #else
258 
259             /* Temporarily disable preemption.  */
260             _tx_thread_preempt_disable++;
261 
262             /* Restore interrupts.  */
263             TX_RESTORE
264 
265             /* Resume thread.  */
266             _tx_thread_system_resume(thread_ptr);
267 #endif
268 
269 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
270 
271             /* Determine if a notify callback is required.  */
272             if (queue_send_notify != TX_NULL)
273             {
274 
275                 /* Call application queue send notification.  */
276                 (queue_send_notify)(queue_ptr);
277             }
278 #endif
279         }
280     }
281 
282     /* Determine if the caller has requested suspension.  */
283     else if (wait_option != TX_NO_WAIT)
284     {
285 
286         /* Determine if the preempt disable flag is non-zero.  */
287         if (_tx_thread_preempt_disable != ((UINT) 0))
288         {
289 
290             /* Restore interrupts.  */
291             TX_RESTORE
292 
293             /* Suspension is not allowed if the preempt disable flag is non-zero at this point - return error completion.  */
294             status =  TX_QUEUE_FULL;
295         }
296         else
297         {
298 
299             /* Yes, suspension is requested.  */
300 
301             /* Prepare for suspension of this thread.  */
302 
303             /* Pickup thread pointer.  */
304             TX_THREAD_GET_CURRENT(thread_ptr)
305 
306             /* Setup cleanup routine pointer.  */
307             thread_ptr -> tx_thread_suspend_cleanup =  &(_tx_queue_cleanup);
308 
309             /* Setup cleanup information, i.e. this queue control
310                block and the source pointer.  */
311             thread_ptr -> tx_thread_suspend_control_block =    (VOID *) queue_ptr;
312             thread_ptr -> tx_thread_additional_suspend_info =  (VOID *) source_ptr;
313 
314             /* Set the flag to true to indicate a queue front send suspension.  */
315             thread_ptr -> tx_thread_suspend_option =           TX_TRUE;
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             /* Place this thread at the front of the suspension list, since it is a
325                queue front send suspension.  */
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                 /* Update the suspension list to put this thread in front, which will put
347                    the message that was removed in the proper relative order when room is
348                    made in the queue.  */
349                 queue_ptr -> tx_queue_suspension_list =         thread_ptr;
350             }
351 
352             /* Increment the suspended thread count.  */
353             queue_ptr -> tx_queue_suspended_count =  suspended_count + ((UINT) 1);
354 
355             /* Set the state to suspended.  */
356             thread_ptr -> tx_thread_state =    TX_QUEUE_SUSP;
357 
358 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
359 
360             /* Pickup the notify callback routine for this queue.  */
361             queue_send_notify =  queue_ptr -> tx_queue_send_notify;
362 #endif
363 
364 #ifdef TX_NOT_INTERRUPTABLE
365 
366             /* Call actual non-interruptable thread suspension routine.  */
367             _tx_thread_system_ni_suspend(thread_ptr, wait_option);
368 
369             /* Restore interrupts.  */
370             TX_RESTORE
371 #else
372 
373             /* Set the suspending flag.  */
374             thread_ptr -> tx_thread_suspending =  TX_TRUE;
375 
376             /* Setup the timeout period.  */
377             thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option;
378 
379             /* Temporarily disable preemption.  */
380             _tx_thread_preempt_disable++;
381 
382             /* Restore interrupts.  */
383             TX_RESTORE
384 
385             /* Call actual thread suspension routine.  */
386             _tx_thread_system_suspend(thread_ptr);
387 #endif
388 
389 #ifndef TX_DISABLE_NOTIFY_CALLBACKS
390 
391             /* Determine if a notify callback is required.  */
392             if (thread_ptr -> tx_thread_suspend_status == TX_SUCCESS)
393             {
394 
395                 /* Check for a notify callback.  */
396                 if (queue_send_notify != TX_NULL)
397                 {
398 
399                     /* Call application queue send notification.  */
400                     (queue_send_notify)(queue_ptr);
401                 }
402             }
403 #endif
404 
405             /* Return the completion status.  */
406             status =  thread_ptr -> tx_thread_suspend_status;
407         }
408     }
409     else
410     {
411 
412         /* Restore interrupts.  */
413         TX_RESTORE
414 
415         /* No room in queue and no suspension requested, return error completion.  */
416         status =  TX_QUEUE_FULL;
417     }
418 
419     /* Return completion status.  */
420     return(status);
421 }
422 
423