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 /** Byte Memory */
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_byte_pool.h"
31
32
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _tx_byte_release PORTABLE C */
38 /* 6.1 */
39 /* AUTHOR */
40 /* */
41 /* William E. Lamie, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function returns previously allocated memory to its */
46 /* associated memory byte pool. */
47 /* */
48 /* INPUT */
49 /* */
50 /* memory_ptr Pointer to allocated memory */
51 /* */
52 /* OUTPUT */
53 /* */
54 /* [TX_PTR_ERROR | TX_SUCCESS] Completion status */
55 /* */
56 /* CALLS */
57 /* */
58 /* _tx_thread_system_preempt_check Check for preemption */
59 /* _tx_thread_system_resume Resume thread service */
60 /* _tx_thread_system_ni_resume Non-interruptable resume thread */
61 /* _tx_byte_pool_search Search the byte pool for memory */
62 /* */
63 /* CALLED BY */
64 /* */
65 /* Application Code */
66 /* */
67 /* RELEASE HISTORY */
68 /* */
69 /* DATE NAME DESCRIPTION */
70 /* */
71 /* 05-19-2020 William E. Lamie Initial Version 6.0 */
72 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
73 /* resulting in version 6.1 */
74 /* */
75 /**************************************************************************/
_tx_byte_release(VOID * memory_ptr)76 UINT _tx_byte_release(VOID *memory_ptr)
77 {
78
79 TX_INTERRUPT_SAVE_AREA
80
81 UINT status;
82 TX_BYTE_POOL *pool_ptr;
83 TX_THREAD *thread_ptr;
84 UCHAR *work_ptr;
85 UCHAR *temp_ptr;
86 UCHAR *next_block_ptr;
87 TX_THREAD *susp_thread_ptr;
88 UINT suspended_count;
89 TX_THREAD *next_thread;
90 TX_THREAD *previous_thread;
91 ULONG memory_size;
92 ALIGN_TYPE *free_ptr;
93 TX_BYTE_POOL **byte_pool_ptr;
94 UCHAR **block_link_ptr;
95 UCHAR **suspend_info_ptr;
96
97
98 /* Default to successful status. */
99 status = TX_SUCCESS;
100
101 /* Set the pool pointer to NULL. */
102 pool_ptr = TX_NULL;
103
104 /* Lockout interrupts. */
105 TX_DISABLE
106
107 /* Determine if the memory pointer is valid. */
108 work_ptr = TX_VOID_TO_UCHAR_POINTER_CONVERT(memory_ptr);
109 if (work_ptr != TX_NULL)
110 {
111
112 /* Back off the memory pointer to pickup its header. */
113 work_ptr = TX_UCHAR_POINTER_SUB(work_ptr, ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE))));
114
115 /* There is a pointer, pickup the pool pointer address. */
116 temp_ptr = TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *)));
117 free_ptr = TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(temp_ptr);
118 if ((*free_ptr) != TX_BYTE_BLOCK_FREE)
119 {
120
121 /* Pickup the pool pointer. */
122 temp_ptr = TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *)));
123 byte_pool_ptr = TX_UCHAR_TO_INDIRECT_BYTE_POOL_POINTER(temp_ptr);
124 pool_ptr = *byte_pool_ptr;
125
126 /* See if we have a valid pool pointer. */
127 if (pool_ptr == TX_NULL)
128 {
129
130 /* Return pointer error. */
131 status = TX_PTR_ERROR;
132 }
133 else
134 {
135
136 /* See if we have a valid pool. */
137 if (pool_ptr -> tx_byte_pool_id != TX_BYTE_POOL_ID)
138 {
139
140 /* Return pointer error. */
141 status = TX_PTR_ERROR;
142
143 /* Reset the pool pointer is NULL. */
144 pool_ptr = TX_NULL;
145 }
146 }
147 }
148 else
149 {
150
151 /* Return pointer error. */
152 status = TX_PTR_ERROR;
153 }
154 }
155 else
156 {
157
158 /* Return pointer error. */
159 status = TX_PTR_ERROR;
160 }
161
162 /* Determine if the pointer is valid. */
163 if (pool_ptr == TX_NULL)
164 {
165
166 /* Restore interrupts. */
167 TX_RESTORE
168 }
169 else
170 {
171
172 /* At this point, we know that the pointer is valid. */
173
174 /* Pickup thread pointer. */
175 TX_THREAD_GET_CURRENT(thread_ptr)
176
177 /* Indicate that this thread is the current owner. */
178 pool_ptr -> tx_byte_pool_owner = thread_ptr;
179
180 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
181
182 /* Increment the total release counter. */
183 _tx_byte_pool_performance_release_count++;
184
185 /* Increment the number of releases on this pool. */
186 pool_ptr -> tx_byte_pool_performance_release_count++;
187 #endif
188
189 /* If trace is enabled, insert this event into the trace buffer. */
190 TX_TRACE_IN_LINE_INSERT(TX_TRACE_BYTE_RELEASE, pool_ptr, TX_POINTER_TO_ULONG_CONVERT(memory_ptr), pool_ptr -> tx_byte_pool_suspended_count, pool_ptr -> tx_byte_pool_available, TX_TRACE_BYTE_POOL_EVENTS)
191
192 /* Log this kernel call. */
193 TX_EL_BYTE_RELEASE_INSERT
194
195 /* Release the memory. */
196 temp_ptr = TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *)));
197 free_ptr = TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(temp_ptr);
198 *free_ptr = TX_BYTE_BLOCK_FREE;
199
200 /* Update the number of available bytes in the pool. */
201 block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr);
202 next_block_ptr = *block_link_ptr;
203 pool_ptr -> tx_byte_pool_available =
204 pool_ptr -> tx_byte_pool_available + TX_UCHAR_POINTER_DIF(next_block_ptr, work_ptr);
205
206 /* Determine if the free block is prior to current search pointer. */
207 if (work_ptr < (pool_ptr -> tx_byte_pool_search))
208 {
209
210 /* Yes, update the search pointer to the released block. */
211 pool_ptr -> tx_byte_pool_search = work_ptr;
212 }
213
214 /* Determine if there are threads suspended on this byte pool. */
215 if (pool_ptr -> tx_byte_pool_suspended_count != TX_NO_SUSPENSIONS)
216 {
217
218 /* Now examine the suspension list to find threads waiting for
219 memory. Maybe it is now available! */
220 while (pool_ptr -> tx_byte_pool_suspended_count != TX_NO_SUSPENSIONS)
221 {
222
223 /* Pickup the first suspended thread pointer. */
224 susp_thread_ptr = pool_ptr -> tx_byte_pool_suspension_list;
225
226 /* Pickup the size of the memory the thread is requesting. */
227 memory_size = susp_thread_ptr -> tx_thread_suspend_info;
228
229 /* Restore interrupts. */
230 TX_RESTORE
231
232 /* See if the request can be satisfied. */
233 work_ptr = _tx_byte_pool_search(pool_ptr, memory_size);
234
235 /* Optional processing extension. */
236 TX_BYTE_RELEASE_EXTENSION
237
238 /* Disable interrupts. */
239 TX_DISABLE
240
241 /* Indicate that this thread is the current owner. */
242 pool_ptr -> tx_byte_pool_owner = thread_ptr;
243
244 /* If there is not enough memory, break this loop! */
245 if (work_ptr == TX_NULL)
246 {
247
248 /* Break out of the loop. */
249 break;
250 }
251
252 /* Check to make sure the thread is still suspended. */
253 if (susp_thread_ptr == pool_ptr -> tx_byte_pool_suspension_list)
254 {
255
256 /* Also, makes sure the memory size is the same. */
257 if (susp_thread_ptr -> tx_thread_suspend_info == memory_size)
258 {
259
260 /* Remove the suspended thread from the list. */
261
262 /* Decrement the number of threads suspended. */
263 pool_ptr -> tx_byte_pool_suspended_count--;
264
265 /* Pickup the suspended count. */
266 suspended_count = pool_ptr -> tx_byte_pool_suspended_count;
267
268 /* See if this is the only suspended thread on the list. */
269 if (suspended_count == TX_NO_SUSPENSIONS)
270 {
271
272 /* Yes, the only suspended thread. */
273
274 /* Update the head pointer. */
275 pool_ptr -> tx_byte_pool_suspension_list = TX_NULL;
276 }
277 else
278 {
279
280 /* At least one more thread is on the same expiration list. */
281
282 /* Update the list head pointer. */
283 next_thread = susp_thread_ptr -> tx_thread_suspended_next;
284 pool_ptr -> tx_byte_pool_suspension_list = next_thread;
285
286 /* Update the links of the adjacent threads. */
287 previous_thread = susp_thread_ptr -> tx_thread_suspended_previous;
288 next_thread -> tx_thread_suspended_previous = previous_thread;
289 previous_thread -> tx_thread_suspended_next = next_thread;
290 }
291
292 /* Prepare for resumption of the thread. */
293
294 /* Clear cleanup routine to avoid timeout. */
295 susp_thread_ptr -> tx_thread_suspend_cleanup = TX_NULL;
296
297 /* Return this block pointer to the suspended thread waiting for
298 a block. */
299 suspend_info_ptr = TX_VOID_TO_INDIRECT_UCHAR_POINTER_CONVERT(susp_thread_ptr -> tx_thread_additional_suspend_info);
300 *suspend_info_ptr = work_ptr;
301
302 /* Clear the memory pointer to indicate that it was given to the suspended thread. */
303 work_ptr = TX_NULL;
304
305 /* Put return status into the thread control block. */
306 susp_thread_ptr -> tx_thread_suspend_status = TX_SUCCESS;
307
308 #ifdef TX_NOT_INTERRUPTABLE
309
310 /* Resume the thread! */
311 _tx_thread_system_ni_resume(susp_thread_ptr);
312
313 /* Restore interrupts. */
314 TX_RESTORE
315 #else
316 /* Temporarily disable preemption. */
317 _tx_thread_preempt_disable++;
318
319 /* Restore interrupts. */
320 TX_RESTORE
321
322 /* Resume thread. */
323 _tx_thread_system_resume(susp_thread_ptr);
324 #endif
325
326 /* Lockout interrupts. */
327 TX_DISABLE
328 }
329 }
330
331 /* Determine if the memory was given to the suspended thread. */
332 if (work_ptr != TX_NULL)
333 {
334
335 /* No, it wasn't given to the suspended thread. */
336
337 /* Put the memory back on the available list since this thread is no longer
338 suspended. */
339 work_ptr = TX_UCHAR_POINTER_SUB(work_ptr, (((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)))));
340 temp_ptr = TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *)));
341 free_ptr = TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(temp_ptr);
342 *free_ptr = TX_BYTE_BLOCK_FREE;
343
344 /* Update the number of available bytes in the pool. */
345 block_link_ptr = TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr);
346 next_block_ptr = *block_link_ptr;
347 pool_ptr -> tx_byte_pool_available =
348 pool_ptr -> tx_byte_pool_available + TX_UCHAR_POINTER_DIF(next_block_ptr, work_ptr);
349
350 /* Determine if the current pointer is before the search pointer. */
351 if (work_ptr < (pool_ptr -> tx_byte_pool_search))
352 {
353
354 /* Yes, update the search pointer. */
355 pool_ptr -> tx_byte_pool_search = work_ptr;
356 }
357 }
358 }
359
360 /* Restore interrupts. */
361 TX_RESTORE
362
363 /* Check for preemption. */
364 _tx_thread_system_preempt_check();
365 }
366 else
367 {
368
369 /* No, threads suspended, restore interrupts. */
370 TX_RESTORE
371 }
372 }
373
374 /* Return completion status. */
375 return(status);
376 }
377
378