mempool.c 11.7 KB
Newer Older
1
/*
mysterywolf's avatar
mysterywolf 已提交
2
 * Copyright (c) 2006-2021, RT-Thread Development Team
B
Bernard Xiong 已提交
3
 *
4
 * SPDX-License-Identifier: Apache-2.0
5 6 7 8
 *
 * Change Logs:
 * Date           Author       Notes
 * 2006-05-27     Bernard      implement memory pool
B
bernard.xiong 已提交
9
 * 2006-06-03     Bernard      fix the thread timer init bug
10 11 12
 * 2006-06-30     Bernard      fix the allocate/free block bug
 * 2006-08-04     Bernard      add hook support
 * 2006-08-10     Bernard      fix interrupt bug in rt_mp_alloc
13
 * 2010-07-13     Bernard      fix RT_ALIGN issue found by kuronca
qiuyiuestc's avatar
qiuyiuestc 已提交
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 20 21 22 23 24
 */

#include <rthw.h>
#include <rtthread.h>

#ifdef RT_USING_MEMPOOL

#ifdef RT_USING_HOOK
D
dzzxzz 已提交
25 26
static void (*rt_mp_alloc_hook)(struct rt_mempool *mp, void *block);
static void (*rt_mp_free_hook)(struct rt_mempool *mp, void *block);
27 28 29 30

/**
 * @addtogroup Hook
 */
D
dzzxzz 已提交
31

D
dogandog 已提交
32
/**@{*/
33 34 35 36

/**
 * This function will set a hook function, which will be invoked when a memory
 * block is allocated from memory pool.
B
bernard.xiong 已提交
37
 *
38 39
 * @param hook the hook function
 */
D
dzzxzz 已提交
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 47
}

/**
 * This function will set a hook function, which will be invoked when a memory
 * block is released to memory pool.
B
bernard.xiong 已提交
48
 *
49 50
 * @param hook the hook function
 */
D
dzzxzz 已提交
51
void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block))
52
{
53
    rt_mp_free_hook = hook;
54 55
}

D
dogandog 已提交
56
/**@}*/
57
#endif /* RT_USING_HOOK */
58 59 60 61 62

/**
 * @addtogroup MM
 */

D
dogandog 已提交
63
/**@{*/
64 65

/**
66 67
 * This function will initialize a memory pool object, normally which is used
 * for static object.
68
 *
B
bernard.xiong@gmail.com 已提交
69
 * @param mp the memory pool object
70 71 72 73 74
 * @param name the name of memory pool
 * @param start the star address of memory pool
 * @param size the total size of memory pool
 * @param block_size the size for each block
 *
B
bernard.xiong@gmail.com 已提交
75
 * @return RT_EOK
76
 */
77 78 79 80 81
rt_err_t rt_mp_init(struct rt_mempool *mp,
                    const char        *name,
                    void              *start,
                    rt_size_t          size,
                    rt_size_t          block_size)
82
{
83
    rt_uint8_t *block_ptr;
84
    register rt_size_t offset;
85

86 87
    /* parameter check */
    RT_ASSERT(mp != RT_NULL);
88 89 90
    RT_ASSERT(name != RT_NULL);
    RT_ASSERT(start != RT_NULL);
    RT_ASSERT(size > 0 && block_size > 0);
91

92 93
    /* initialize object */
    rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name);
94

95 96 97
    /* initialize memory pool */
    mp->start_address = start;
    mp->size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
98

99 100 101
    /* align the block size */
    block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
    mp->block_size = block_size;
102

103 104 105
    /* align to align size byte */
    mp->block_total_count = mp->size / (mp->block_size + sizeof(rt_uint8_t *));
    mp->block_free_count  = mp->block_total_count;
106

107 108
    /* initialize suspended thread list */
    rt_list_init(&(mp->suspend_thread));
109

110 111 112 113 114 115 116
    /* initialize free block list */
    block_ptr = (rt_uint8_t *)mp->start_address;
    for (offset = 0; offset < mp->block_total_count; offset ++)
    {
        *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) =
            (rt_uint8_t *)(block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *)));
    }
117

118 119
    *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) =
        RT_NULL;
120

121
    mp->block_list = block_ptr;
122

123
    return RT_EOK;
124
}
125
RTM_EXPORT(rt_mp_init);
126

B
bernard.xiong@gmail.com 已提交
127 128 129 130 131 132 133
/**
 * This function will detach a memory pool from system object management.
 *
 * @param mp the memory pool object
 *
 * @return RT_EOK
 */
D
dzzxzz 已提交
134
rt_err_t rt_mp_detach(struct rt_mempool *mp)
135
{
136
    struct rt_thread *thread;
D
David Lin 已提交
137
    register rt_ubase_t level;
138

139 140
    /* parameter check */
    RT_ASSERT(mp != RT_NULL);
141 142
    RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool);
    RT_ASSERT(rt_object_is_systemobject(&mp->parent));
143

144 145 146 147
    /* wake up all suspended threads */
    while (!rt_list_isempty(&(mp->suspend_thread)))
    {
        /* disable interrupt */
D
David Lin 已提交
148
        level = rt_hw_interrupt_disable();
149

150 151 152 153
        /* get next suspend thread */
        thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist);
        /* set error code to RT_ERROR */
        thread->error = -RT_ERROR;
154

155 156 157 158 159 160
        /*
         * resume thread
         * In rt_thread_resume function, it will remove current thread from
         * suspend list
         */
        rt_thread_resume(thread);
161

162
        /* enable interrupt */
D
David Lin 已提交
163
        rt_hw_interrupt_enable(level);
164
    }
165

166 167
    /* detach object */
    rt_object_detach(&(mp->parent));
168

169
    return RT_EOK;
170
}
171
RTM_EXPORT(rt_mp_detach);
172 173 174

#ifdef RT_USING_HEAP
/**
175 176
 * This function will create a mempool object and allocate the memory pool from
 * heap.
177 178 179 180 181 182 183
 *
 * @param name the name of memory pool
 * @param block_count the count of blocks in memory pool
 * @param block_size the size for each block
 *
 * @return the created mempool object
 */
184 185 186
rt_mp_t rt_mp_create(const char *name,
                     rt_size_t   block_count,
                     rt_size_t   block_size)
187
{
188 189
    rt_uint8_t *block_ptr;
    struct rt_mempool *mp;
190
    register rt_size_t offset;
191 192 193

    RT_DEBUG_NOT_IN_INTERRUPT;

194 195 196 197
    /* parameter check */
    RT_ASSERT(name != RT_NULL);
    RT_ASSERT(block_count > 0 && block_size > 0);

198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
    /* allocate object */
    mp = (struct rt_mempool *)rt_object_allocate(RT_Object_Class_MemPool, name);
    /* allocate object failed */
    if (mp == RT_NULL)
        return RT_NULL;

    /* initialize memory pool */
    block_size     = RT_ALIGN(block_size, RT_ALIGN_SIZE);
    mp->block_size = block_size;
    mp->size       = (block_size + sizeof(rt_uint8_t *)) * block_count;

    /* allocate memory */
    mp->start_address = rt_malloc((block_size + sizeof(rt_uint8_t *)) *
                                  block_count);
    if (mp->start_address == RT_NULL)
    {
        /* no memory, delete memory pool object */
        rt_object_delete(&(mp->parent));

        return RT_NULL;
    }

    mp->block_total_count = block_count;
    mp->block_free_count  = mp->block_total_count;

    /* initialize suspended thread list */
    rt_list_init(&(mp->suspend_thread));

    /* initialize free block list */
    block_ptr = (rt_uint8_t *)mp->start_address;
    for (offset = 0; offset < mp->block_total_count; offset ++)
    {
        *(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *)))
            = block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *));
    }

    *(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *)))
        = RT_NULL;

    mp->block_list = block_ptr;

    return mp;
240
}
241
RTM_EXPORT(rt_mp_create);
242 243 244 245 246 247

/**
 * This function will delete a memory pool and release the object memory.
 *
 * @param mp the memory pool object
 *
B
bernard.xiong@gmail.com 已提交
248
 * @return RT_EOK
249 250 251
 */
rt_err_t rt_mp_delete(rt_mp_t mp)
{
252
    struct rt_thread *thread;
D
David Lin 已提交
253
    register rt_ubase_t level;
254

255
    RT_DEBUG_NOT_IN_INTERRUPT;
256

257 258
    /* parameter check */
    RT_ASSERT(mp != RT_NULL);
259 260
    RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool);
    RT_ASSERT(rt_object_is_systemobject(&mp->parent) == RT_FALSE);
261

262 263 264 265
    /* wake up all suspended threads */
    while (!rt_list_isempty(&(mp->suspend_thread)))
    {
        /* disable interrupt */
D
David Lin 已提交
266
        level = rt_hw_interrupt_disable();
267

268 269 270 271
        /* get next suspend thread */
        thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist);
        /* set error code to RT_ERROR */
        thread->error = -RT_ERROR;
272

273 274 275 276 277 278
        /*
         * resume thread
         * In rt_thread_resume function, it will remove current thread from
         * suspend list
         */
        rt_thread_resume(thread);
279

280
        /* enable interrupt */
D
David Lin 已提交
281
        rt_hw_interrupt_enable(level);
282
    }
283

284 285
    /* release allocated room */
    rt_free(mp->start_address);
B
bernard.xiong 已提交
286

287 288
    /* detach object */
    rt_object_delete(&(mp->parent));
289

290
    return RT_EOK;
291
}
292
RTM_EXPORT(rt_mp_delete);
293
#endif /* RT_USING_HEAP */
294 295 296 297 298 299 300

/**
 * This function will allocate a block from memory pool
 *
 * @param mp the memory pool object
 * @param time the waiting time
 *
B
bernard.xiong@gmail.com 已提交
301
 * @return the allocated memory block or RT_NULL on allocated failed
302
 */
D
dzzxzz 已提交
303
void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time)
304
{
305 306 307
    rt_uint8_t *block_ptr;
    register rt_base_t level;
    struct rt_thread *thread;
308 309
    rt_uint32_t before_sleep = 0;

310 311 312
    /* parameter check */
    RT_ASSERT(mp != RT_NULL);

313 314
    /* get current thread */
    thread = rt_thread_self();
315 316 317 318

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

319
    while (mp->block_free_count == 0)
320 321 322 323 324 325 326
    {
        /* memory block is unavailable. */
        if (time == 0)
        {
            /* enable interrupt */
            rt_hw_interrupt_enable(level);

327 328
            rt_set_errno(-RT_ETIMEOUT);

329 330 331
            return RT_NULL;
        }

332
        RT_DEBUG_NOT_IN_INTERRUPT;
333

334
        thread->error = RT_EOK;
335

336 337 338
        /* need suspend thread */
        rt_thread_suspend(thread);
        rt_list_insert_after(&(mp->suspend_thread), &(thread->tlist));
339

340 341 342 343 344 345 346 347 348 349 350
        if (time > 0)
        {
            /* get the start tick of timer */
            before_sleep = rt_tick_get();

            /* init thread timer and start it */
            rt_timer_control(&(thread->thread_timer),
                             RT_TIMER_CTRL_SET_TIME,
                             &time);
            rt_timer_start(&(thread->thread_timer));
        }
351

352 353
        /* enable interrupt */
        rt_hw_interrupt_enable(level);
354

355 356
        /* do a schedule */
        rt_schedule();
357

358 359 360 361 362 363 364 365 366 367 368 369
        if (thread->error != RT_EOK)
            return RT_NULL;

        if (time > 0)
        {
            time -= rt_tick_get() - before_sleep;
            if (time < 0)
                time = 0;
        }
        /* disable interrupt */
        level = rt_hw_interrupt_disable();
    }
370

371 372
    /* memory block is available. decrease the free block counter */
    mp->block_free_count--;
373

374 375 376
    /* get block from block list */
    block_ptr = mp->block_list;
    RT_ASSERT(block_ptr != RT_NULL);
377

378 379
    /* Setup the next free node. */
    mp->block_list = *(rt_uint8_t **)block_ptr;
380

381 382
    /* point to memory pool */
    *(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp;
383 384 385 386 387 388 389 390

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    RT_OBJECT_HOOK_CALL(rt_mp_alloc_hook,
                        (mp, (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *))));

    return (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *));
391
}
392
RTM_EXPORT(rt_mp_alloc);
393 394 395 396 397 398

/**
 * This function will release a memory block
 *
 * @param block the address of memory block to be released
 */
D
dzzxzz 已提交
399
void rt_mp_free(void *block)
400
{
401 402 403 404
    rt_uint8_t **block_ptr;
    struct rt_mempool *mp;
    struct rt_thread *thread;
    register rt_base_t level;
405

406 407 408
    /* parameter check */
    if (block == RT_NULL) return;

409 410 411
    /* get the control block of pool which the block belongs to */
    block_ptr = (rt_uint8_t **)((rt_uint8_t *)block - sizeof(rt_uint8_t *));
    mp        = (struct rt_mempool *)*block_ptr;
412

413
    RT_OBJECT_HOOK_CALL(rt_mp_free_hook, (mp, block));
414

415 416
    /* disable interrupt */
    level = rt_hw_interrupt_disable();
417

418 419
    /* increase the free block count */
    mp->block_free_count ++;
420

421 422 423
    /* link the block into the block list */
    *block_ptr = mp->block_list;
    mp->block_list = (rt_uint8_t *)block_ptr;
424

425
    if (!rt_list_isempty(&(mp->suspend_thread)))
426 427 428 429 430
    {
        /* get the suspended thread */
        thread = rt_list_entry(mp->suspend_thread.next,
                               struct rt_thread,
                               tlist);
431

432 433
        /* set error */
        thread->error = RT_EOK;
434

435 436
        /* resume thread */
        rt_thread_resume(thread);
437

438 439
        /* enable interrupt */
        rt_hw_interrupt_enable(level);
440

441 442
        /* do a schedule */
        rt_schedule();
443

444 445
        return;
    }
446

447 448
    /* enable interrupt */
    rt_hw_interrupt_enable(level);
449
}
450
RTM_EXPORT(rt_mp_free);
451

D
dogandog 已提交
452
/**@}*/
453

454
#endif /* RT_USING_MEMPOOL */