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