mempool.c 11.7 KB
Newer Older
1
/*
2
 * Copyright (c) 2006-2018, 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 58 59 60 61 62
#endif

/**
 * @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
    /* initialize object */
    rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name);
91

92 93 94
    /* initialize memory pool */
    mp->start_address = start;
    mp->size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
95

96 97 98
    /* align the block size */
    block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
    mp->block_size = block_size;
99

100 101 102
    /* 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;
103

104 105 106
    /* initialize suspended thread list */
    rt_list_init(&(mp->suspend_thread));
    mp->suspend_thread_count = 0;
107

108 109 110 111 112 113 114
    /* 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 *)));
    }
115

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

119
    mp->block_list = block_ptr;
120

121
    return RT_EOK;
122
}
123
RTM_EXPORT(rt_mp_init);
124

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

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

142 143 144 145 146
    /* wake up all suspended threads */
    while (!rt_list_isempty(&(mp->suspend_thread)))
    {
        /* disable interrupt */
        temp = rt_hw_interrupt_disable();
147

148 149 150 151
        /* 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;
152

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

160 161
        /* decrease suspended thread count */
        mp->suspend_thread_count --;
162

163 164 165
        /* enable interrupt */
        rt_hw_interrupt_enable(temp);
    }
166

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

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

#ifdef RT_USING_HEAP
/**
176 177
 * This function will create a mempool object and allocate the memory pool from
 * heap.
178 179 180 181 182 183 184
 *
 * @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
 */
185 186 187
rt_mp_t rt_mp_create(const char *name,
                     rt_size_t   block_count,
                     rt_size_t   block_size)
188
{
189 190
    rt_uint8_t *block_ptr;
    struct rt_mempool *mp;
191
    register rt_size_t offset;
192 193 194 195 196 197 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

    RT_DEBUG_NOT_IN_INTERRUPT;

    /* 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));
    mp->suspend_thread_count = 0;

    /* 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;
238
}
239
RTM_EXPORT(rt_mp_create);
240 241 242 243 244 245

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

253
    RT_DEBUG_NOT_IN_INTERRUPT;
254

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

260 261 262 263 264
    /* wake up all suspended threads */
    while (!rt_list_isempty(&(mp->suspend_thread)))
    {
        /* disable interrupt */
        temp = rt_hw_interrupt_disable();
265

266 267 268 269
        /* 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;
270

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

278 279
        /* decrease suspended thread count */
        mp->suspend_thread_count --;
280

281 282 283
        /* enable interrupt */
        rt_hw_interrupt_enable(temp);
    }
284

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

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

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

/**
 * 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 已提交
302
 * @return the allocated memory block or RT_NULL on allocated failed
303
 */
D
dzzxzz 已提交
304
void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time)
305
{
306 307 308
    rt_uint8_t *block_ptr;
    register rt_base_t level;
    struct rt_thread *thread;
309 310 311 312
    rt_uint32_t before_sleep = 0;

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

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

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

325 326
            rt_set_errno(-RT_ETIMEOUT);

327 328 329
            return RT_NULL;
        }

330
        RT_DEBUG_NOT_IN_INTERRUPT;
331

332
        thread->error = RT_EOK;
333

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

339 340 341 342 343 344 345 346 347 348 349
        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));
        }
350

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

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

357 358 359 360 361 362 363 364 365 366 367 368
        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();
    }
369

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

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

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

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

    /* 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 *));
390
}
391
RTM_EXPORT(rt_mp_alloc);
392 393 394 395 396 397

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

405 406 407
    /* 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;
408

409
    RT_OBJECT_HOOK_CALL(rt_mp_free_hook, (mp, block));
410

411 412
    /* disable interrupt */
    level = rt_hw_interrupt_disable();
413

414 415
    /* increase the free block count */
    mp->block_free_count ++;
416

417 418 419
    /* link the block into the block list */
    *block_ptr = mp->block_list;
    mp->block_list = (rt_uint8_t *)block_ptr;
420

421 422 423 424 425 426
    if (mp->suspend_thread_count > 0)
    {
        /* get the suspended thread */
        thread = rt_list_entry(mp->suspend_thread.next,
                               struct rt_thread,
                               tlist);
427

428 429
        /* set error */
        thread->error = RT_EOK;
430

431 432
        /* resume thread */
        rt_thread_resume(thread);
433

434 435
        /* decrease suspended thread count */
        mp->suspend_thread_count --;
436

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

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

443 444
        return;
    }
445

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

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

D
dzzxzz 已提交
453 454
#endif