xref: /nrf52832-nimble/rt-thread/src/mempool.c (revision 104654410c56c573564690304ae786df310c91fc)
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