1 /*
2 * Copyright (c) 2006-2018, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2006-05-27 Bernard implement memory pool
9 * 2006-06-03 Bernard fix the thread timer init bug
10 * 2006-06-30 Bernard fix the allocate/free block bug
11 * 2006-08-04 Bernard add hook support
12 * 2006-08-10 Bernard fix interrupt bug in rt_mp_alloc
13 * 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca
14 * 2010-10-26 yi.qiu add module support in rt_mp_delete
15 * 2011-01-24 Bernard add object allocation check.
16 * 2012-03-22 Bernard fix align issue in rt_mp_init and rt_mp_create.
17 */
18
19 #include <rthw.h>
20 #include <rtthread.h>
21
22 #ifdef RT_USING_MEMPOOL
23
24 #ifdef RT_USING_HOOK
25 static void (*rt_mp_alloc_hook)(struct rt_mempool *mp, void *block);
26 static void (*rt_mp_free_hook)(struct rt_mempool *mp, void *block);
27
28 /**
29 * @addtogroup Hook
30 */
31
32 /**@{*/
33
34 /**
35 * This function will set a hook function, which will be invoked when a memory
36 * block is allocated from memory pool.
37 *
38 * @param hook the hook function
39 */
rt_mp_alloc_sethook(void (* hook)(struct rt_mempool * mp,void * block))40 void rt_mp_alloc_sethook(void (*hook)(struct rt_mempool *mp, void *block))
41 {
42 rt_mp_alloc_hook = hook;
43 }
44
45 /**
46 * This function will set a hook function, which will be invoked when a memory
47 * block is released to memory pool.
48 *
49 * @param hook the hook function
50 */
rt_mp_free_sethook(void (* hook)(struct rt_mempool * mp,void * block))51 void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block))
52 {
53 rt_mp_free_hook = hook;
54 }
55
56 /**@}*/
57 #endif
58
59 /**
60 * @addtogroup MM
61 */
62
63 /**@{*/
64
65 /**
66 * This function will initialize a memory pool object, normally which is used
67 * for static object.
68 *
69 * @param mp the memory pool object
70 * @param name the name of memory pool
71 * @param start the star address of memory pool
72 * @param size the total size of memory pool
73 * @param block_size the size for each block
74 *
75 * @return RT_EOK
76 */
rt_mp_init(struct rt_mempool * mp,const char * name,void * start,rt_size_t size,rt_size_t block_size)77 rt_err_t rt_mp_init(struct rt_mempool *mp,
78 const char *name,
79 void *start,
80 rt_size_t size,
81 rt_size_t block_size)
82 {
83 rt_uint8_t *block_ptr;
84 register rt_size_t offset;
85
86 /* parameter check */
87 RT_ASSERT(mp != RT_NULL);
88
89 /* initialize object */
90 rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name);
91
92 /* initialize memory pool */
93 mp->start_address = start;
94 mp->size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
95
96 /* align the block size */
97 block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
98 mp->block_size = block_size;
99
100 /* align to align size byte */
101 mp->block_total_count = mp->size / (mp->block_size + sizeof(rt_uint8_t *));
102 mp->block_free_count = mp->block_total_count;
103
104 /* initialize suspended thread list */
105 rt_list_init(&(mp->suspend_thread));
106 mp->suspend_thread_count = 0;
107
108 /* initialize free block list */
109 block_ptr = (rt_uint8_t *)mp->start_address;
110 for (offset = 0; offset < mp->block_total_count; offset ++)
111 {
112 *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) =
113 (rt_uint8_t *)(block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *)));
114 }
115
116 *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) =
117 RT_NULL;
118
119 mp->block_list = block_ptr;
120
121 return RT_EOK;
122 }
123 RTM_EXPORT(rt_mp_init);
124
125 /**
126 * This function will detach a memory pool from system object management.
127 *
128 * @param mp the memory pool object
129 *
130 * @return RT_EOK
131 */
rt_mp_detach(struct rt_mempool * mp)132 rt_err_t rt_mp_detach(struct rt_mempool *mp)
133 {
134 struct rt_thread *thread;
135 register rt_ubase_t temp;
136
137 /* parameter check */
138 RT_ASSERT(mp != RT_NULL);
139 RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool);
140 RT_ASSERT(rt_object_is_systemobject(&mp->parent));
141
142 /* wake up all suspended threads */
143 while (!rt_list_isempty(&(mp->suspend_thread)))
144 {
145 /* disable interrupt */
146 temp = rt_hw_interrupt_disable();
147
148 /* get next suspend thread */
149 thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist);
150 /* set error code to RT_ERROR */
151 thread->error = -RT_ERROR;
152
153 /*
154 * resume thread
155 * In rt_thread_resume function, it will remove current thread from
156 * suspend list
157 */
158 rt_thread_resume(thread);
159
160 /* decrease suspended thread count */
161 mp->suspend_thread_count --;
162
163 /* enable interrupt */
164 rt_hw_interrupt_enable(temp);
165 }
166
167 /* detach object */
168 rt_object_detach(&(mp->parent));
169
170 return RT_EOK;
171 }
172 RTM_EXPORT(rt_mp_detach);
173
174 #ifdef RT_USING_HEAP
175 /**
176 * This function will create a mempool object and allocate the memory pool from
177 * heap.
178 *
179 * @param name the name of memory pool
180 * @param block_count the count of blocks in memory pool
181 * @param block_size the size for each block
182 *
183 * @return the created mempool object
184 */
rt_mp_create(const char * name,rt_size_t block_count,rt_size_t block_size)185 rt_mp_t rt_mp_create(const char *name,
186 rt_size_t block_count,
187 rt_size_t block_size)
188 {
189 rt_uint8_t *block_ptr;
190 struct rt_mempool *mp;
191 register rt_size_t offset;
192
193 RT_DEBUG_NOT_IN_INTERRUPT;
194
195 /* allocate object */
196 mp = (struct rt_mempool *)rt_object_allocate(RT_Object_Class_MemPool, name);
197 /* allocate object failed */
198 if (mp == RT_NULL)
199 return RT_NULL;
200
201 /* initialize memory pool */
202 block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
203 mp->block_size = block_size;
204 mp->size = (block_size + sizeof(rt_uint8_t *)) * block_count;
205
206 /* allocate memory */
207 mp->start_address = rt_malloc((block_size + sizeof(rt_uint8_t *)) *
208 block_count);
209 if (mp->start_address == RT_NULL)
210 {
211 /* no memory, delete memory pool object */
212 rt_object_delete(&(mp->parent));
213
214 return RT_NULL;
215 }
216
217 mp->block_total_count = block_count;
218 mp->block_free_count = mp->block_total_count;
219
220 /* initialize suspended thread list */
221 rt_list_init(&(mp->suspend_thread));
222 mp->suspend_thread_count = 0;
223
224 /* initialize free block list */
225 block_ptr = (rt_uint8_t *)mp->start_address;
226 for (offset = 0; offset < mp->block_total_count; offset ++)
227 {
228 *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *)))
229 = block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *));
230 }
231
232 *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *)))
233 = RT_NULL;
234
235 mp->block_list = block_ptr;
236
237 return mp;
238 }
239 RTM_EXPORT(rt_mp_create);
240
241 /**
242 * This function will delete a memory pool and release the object memory.
243 *
244 * @param mp the memory pool object
245 *
246 * @return RT_EOK
247 */
rt_mp_delete(rt_mp_t mp)248 rt_err_t rt_mp_delete(rt_mp_t mp)
249 {
250 struct rt_thread *thread;
251 register rt_ubase_t temp;
252
253 RT_DEBUG_NOT_IN_INTERRUPT;
254
255 /* parameter check */
256 RT_ASSERT(mp != RT_NULL);
257 RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool);
258 RT_ASSERT(rt_object_is_systemobject(&mp->parent) == RT_FALSE);
259
260 /* wake up all suspended threads */
261 while (!rt_list_isempty(&(mp->suspend_thread)))
262 {
263 /* disable interrupt */
264 temp = rt_hw_interrupt_disable();
265
266 /* get next suspend thread */
267 thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist);
268 /* set error code to RT_ERROR */
269 thread->error = -RT_ERROR;
270
271 /*
272 * resume thread
273 * In rt_thread_resume function, it will remove current thread from
274 * suspend list
275 */
276 rt_thread_resume(thread);
277
278 /* decrease suspended thread count */
279 mp->suspend_thread_count --;
280
281 /* enable interrupt */
282 rt_hw_interrupt_enable(temp);
283 }
284
285 /* release allocated room */
286 rt_free(mp->start_address);
287
288 /* detach object */
289 rt_object_delete(&(mp->parent));
290
291 return RT_EOK;
292 }
293 RTM_EXPORT(rt_mp_delete);
294 #endif
295
296 /**
297 * This function will allocate a block from memory pool
298 *
299 * @param mp the memory pool object
300 * @param time the waiting time
301 *
302 * @return the allocated memory block or RT_NULL on allocated failed
303 */
rt_mp_alloc(rt_mp_t mp,rt_int32_t time)304 void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time)
305 {
306 rt_uint8_t *block_ptr;
307 register rt_base_t level;
308 struct rt_thread *thread;
309 rt_uint32_t before_sleep = 0;
310
311 /* get current thread */
312 thread = rt_thread_self();
313
314 /* disable interrupt */
315 level = rt_hw_interrupt_disable();
316
317 while (mp->block_free_count == 0)
318 {
319 /* memory block is unavailable. */
320 if (time == 0)
321 {
322 /* enable interrupt */
323 rt_hw_interrupt_enable(level);
324
325 rt_set_errno(-RT_ETIMEOUT);
326
327 return RT_NULL;
328 }
329
330 RT_DEBUG_NOT_IN_INTERRUPT;
331
332 thread->error = RT_EOK;
333
334 /* need suspend thread */
335 rt_thread_suspend(thread);
336 rt_list_insert_after(&(mp->suspend_thread), &(thread->tlist));
337 mp->suspend_thread_count++;
338
339 if (time > 0)
340 {
341 /* get the start tick of timer */
342 before_sleep = rt_tick_get();
343
344 /* init thread timer and start it */
345 rt_timer_control(&(thread->thread_timer),
346 RT_TIMER_CTRL_SET_TIME,
347 &time);
348 rt_timer_start(&(thread->thread_timer));
349 }
350
351 /* enable interrupt */
352 rt_hw_interrupt_enable(level);
353
354 /* do a schedule */
355 rt_schedule();
356
357 if (thread->error != RT_EOK)
358 return RT_NULL;
359
360 if (time > 0)
361 {
362 time -= rt_tick_get() - before_sleep;
363 if (time < 0)
364 time = 0;
365 }
366 /* disable interrupt */
367 level = rt_hw_interrupt_disable();
368 }
369
370 /* memory block is available. decrease the free block counter */
371 mp->block_free_count--;
372
373 /* get block from block list */
374 block_ptr = mp->block_list;
375 RT_ASSERT(block_ptr != RT_NULL);
376
377 /* Setup the next free node. */
378 mp->block_list = *(rt_uint8_t **)block_ptr;
379
380 /* point to memory pool */
381 *(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp;
382
383 /* enable interrupt */
384 rt_hw_interrupt_enable(level);
385
386 RT_OBJECT_HOOK_CALL(rt_mp_alloc_hook,
387 (mp, (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *))));
388
389 return (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *));
390 }
391 RTM_EXPORT(rt_mp_alloc);
392
393 /**
394 * This function will release a memory block
395 *
396 * @param block the address of memory block to be released
397 */
rt_mp_free(void * block)398 void rt_mp_free(void *block)
399 {
400 rt_uint8_t **block_ptr;
401 struct rt_mempool *mp;
402 struct rt_thread *thread;
403 register rt_base_t level;
404
405 /* get the control block of pool which the block belongs to */
406 block_ptr = (rt_uint8_t **)((rt_uint8_t *)block - sizeof(rt_uint8_t *));
407 mp = (struct rt_mempool *)*block_ptr;
408
409 RT_OBJECT_HOOK_CALL(rt_mp_free_hook, (mp, block));
410
411 /* disable interrupt */
412 level = rt_hw_interrupt_disable();
413
414 /* increase the free block count */
415 mp->block_free_count ++;
416
417 /* link the block into the block list */
418 *block_ptr = mp->block_list;
419 mp->block_list = (rt_uint8_t *)block_ptr;
420
421 if (mp->suspend_thread_count > 0)
422 {
423 /* get the suspended thread */
424 thread = rt_list_entry(mp->suspend_thread.next,
425 struct rt_thread,
426 tlist);
427
428 /* set error */
429 thread->error = RT_EOK;
430
431 /* resume thread */
432 rt_thread_resume(thread);
433
434 /* decrease suspended thread count */
435 mp->suspend_thread_count --;
436
437 /* enable interrupt */
438 rt_hw_interrupt_enable(level);
439
440 /* do a schedule */
441 rt_schedule();
442
443 return;
444 }
445
446 /* enable interrupt */
447 rt_hw_interrupt_enable(level);
448 }
449 RTM_EXPORT(rt_mp_free);
450
451 /**@}*/
452
453 #endif
454
455