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