diff options
| author | Grissiom <[email protected]> | 2014-08-20 10:16:04 +0800 |
|---|---|---|
| committer | Grissiom <[email protected]> | 2014-08-20 10:26:06 +0800 |
| commit | 7354a9fb782c92001a26f6cefa914ad6a509d0eb (patch) | |
| tree | f8f8d7ab35e9a0b9d000e360e757579834400f46 /src | |
| parent | d57e880444aa63735e49c48b1e126f06e018ef74 (diff) | |
kernel: mempool: fix race condition in rt_mp_alloc
When thread wake up from waiting for memory, there is a chance that
there is no memory available in high pressure. So use a loop to check
again. Otherwise, there will be a NULL reference.
Commit b8bf6bef76bdabe76749c752217afbe5d24ba371 from master.
Diffstat (limited to 'src')
| -rw-r--r-- | src/mempool.c | 97 |
1 files changed, 49 insertions, 48 deletions
diff --git a/src/mempool.c b/src/mempool.c index 05904bfbc7..b6d83597bf 100644 --- a/src/mempool.c +++ b/src/mempool.c @@ -323,23 +323,15 @@ void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time) rt_uint8_t *block_ptr; register rt_base_t level; struct rt_thread *thread; + rt_uint32_t before_sleep = 0; + + /* get current thread */ + thread = rt_thread_self(); /* disable interrupt */ level = rt_hw_interrupt_disable(); - if (mp->block_free_count) - { - /* memory block is available. decrease the free block counter */ - mp->block_free_count --; - - /* get block from block list */ - block_ptr = mp->block_list; - mp->block_list = *(rt_uint8_t **)block_ptr; - - /* point to memory pool */ - *(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp; - } - else + while (mp->block_free_count == 0) { /* memory block is unavailable. */ if (time == 0) @@ -347,55 +339,64 @@ void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time) /* enable interrupt */ rt_hw_interrupt_enable(level); + rt_set_errno(-RT_ETIMEOUT); + return RT_NULL; } - else - { - RT_DEBUG_NOT_IN_INTERRUPT; - - /* get current thread */ - thread = rt_thread_self(); - - thread->error = RT_EOK; - /* need suspend thread */ - rt_thread_suspend(thread); - rt_list_insert_after(&(mp->suspend_thread), &(thread->tlist)); - mp->suspend_thread_count ++; + RT_DEBUG_NOT_IN_INTERRUPT; - if (time > 0) - { - /* init thread timer and start it */ - rt_timer_control(&(thread->thread_timer), - RT_TIMER_CTRL_SET_TIME, - &time); - rt_timer_start(&(thread->thread_timer)); - } + thread->error = RT_EOK; - /* enable interrupt */ - rt_hw_interrupt_enable(level); + /* need suspend thread */ + rt_thread_suspend(thread); + rt_list_insert_after(&(mp->suspend_thread), &(thread->tlist)); + mp->suspend_thread_count++; - /* do a schedule */ - rt_schedule(); + if (time > 0) + { + /* get the start tick of timer */ + before_sleep = rt_tick_get(); - if (thread->error != RT_EOK) - return RT_NULL; + /* init thread timer and start it */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &time); + rt_timer_start(&(thread->thread_timer)); + } - /* disable interrupt */ - level = rt_hw_interrupt_disable(); + /* enable interrupt */ + rt_hw_interrupt_enable(level); - /* decrease free block */ - mp->block_free_count --; + /* do a schedule */ + rt_schedule(); - /* get block from block list */ - block_ptr = mp->block_list; - mp->block_list = *(rt_uint8_t **)block_ptr; + if (thread->error != RT_EOK) + return RT_NULL; - /* point to memory pool */ - *(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp; + if (time > 0) + { + time -= rt_tick_get() - before_sleep; + if (time < 0) + time = 0; } + /* disable interrupt */ + level = rt_hw_interrupt_disable(); } + /* memory block is available. decrease the free block counter */ + mp->block_free_count--; + + /* get block from block list */ + block_ptr = mp->block_list; + RT_ASSERT(block_ptr != RT_NULL); + + /* Setup the next free node. */ + mp->block_list = *(rt_uint8_t **)block_ptr; + + /* point to memory pool */ + *(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp; + /* enable interrupt */ rt_hw_interrupt_enable(level); |
