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