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