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