| /* libunwind - a platform-independent unwind library |
| Copyright (C) 2002-2003, 2005 Hewlett-Packard Co |
| Contributed by David Mosberger-Tang <davidm@hpl.hp.com> |
| Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com> |
| |
| This file is part of libunwind. |
| |
| Permission is hereby granted, free of charge, to any person obtaining |
| a copy of this software and associated documentation files (the |
| "Software"), to deal in the Software without restriction, including |
| without limitation the rights to use, copy, modify, merge, publish, |
| distribute, sublicense, and/or sell copies of the Software, and to |
| permit persons to whom the Software is furnished to do so, subject to |
| the following conditions: |
| |
| The above copyright notice and this permission notice shall be |
| included in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| |
| #include "libunwind_i.h" |
| |
| /* From GCC docs: ``Gcc also provides a target specific macro |
| * __BIGGEST_ALIGNMENT__, which is the largest alignment ever used for any data |
| * type on the target machine you are compiling for.'' */ |
| #ifdef __BIGGEST_ALIGNMENT__ |
| # define MAX_ALIGN __BIGGEST_ALIGNMENT__ |
| #else |
| /* Crude hack to check that MAX_ALIGN is power-of-two. |
| * sizeof(long double) = 12 on i386. */ |
| # define MAX_ALIGN_(n) (n < 8 ? 8 : \ |
| n < 16 ? 16 : n) |
| # define MAX_ALIGN MAX_ALIGN_(sizeof (long double)) |
| #endif |
| |
| static char sos_memory[SOS_MEMORY_SIZE] ALIGNED(MAX_ALIGN); |
| static size_t sos_memory_freepos; |
| static size_t pg_size; |
| |
| HIDDEN void * |
| sos_alloc (size_t size) |
| { |
| size_t pos; |
| |
| size = UNW_ALIGN(size, MAX_ALIGN); |
| |
| #if defined(__GNUC__) |
| /* Assume `sos_memory' is suitably aligned. */ |
| assert(((uintptr_t) &sos_memory[0] & (MAX_ALIGN-1)) == 0); |
| #endif |
| |
| #if defined(__GNUC__) && defined(HAVE_FETCH_AND_ADD) |
| pos = fetch_and_add (&sos_memory_freepos, size); |
| #else |
| static define_lock (sos_lock); |
| intrmask_t saved_mask; |
| |
| lock_acquire (&sos_lock, saved_mask); |
| { |
| # ifndef __GNUC__ |
| /* No assumptions about `sos_memory' alignment. */ |
| if (sos_memory_freepos == 0) |
| { |
| unsigned align = UNW_ALIGN((uintptr_t) &sos_memory[0], MAX_ALIGN) |
| - (uintptr_t) &sos_memory[0]; |
| sos_memory_freepos = align; |
| } |
| # endif |
| pos = sos_memory_freepos; |
| sos_memory_freepos += size; |
| } |
| lock_release (&sos_lock, saved_mask); |
| #endif |
| |
| assert (((uintptr_t) &sos_memory[pos] & (MAX_ALIGN-1)) == 0); |
| assert ((pos+size) <= SOS_MEMORY_SIZE); |
| |
| return &sos_memory[pos]; |
| } |
| |
| /* Must be called while holding the mempool lock. */ |
| |
| static void |
| free_object (struct mempool *pool, void *object) |
| { |
| struct object *obj = object; |
| |
| obj->next = pool->free_list; |
| pool->free_list = obj; |
| ++pool->num_free; |
| } |
| |
| static void |
| add_memory (struct mempool *pool, char *mem, size_t size, size_t obj_size) |
| { |
| char *obj; |
| |
| for (obj = mem; obj <= mem + size - obj_size; obj += obj_size) |
| free_object (pool, obj); |
| } |
| |
| static void |
| expand (struct mempool *pool) |
| { |
| size_t size; |
| char *mem; |
| |
| size = pool->chunk_size; |
| GET_MEMORY (mem, size); |
| if (!mem) |
| { |
| size = UNW_ALIGN(pool->obj_size, pg_size); |
| GET_MEMORY (mem, size); |
| if (!mem) |
| { |
| /* last chance: try to allocate one object from the SOS memory */ |
| size = pool->obj_size; |
| mem = sos_alloc (size); |
| } |
| } |
| add_memory (pool, mem, size, pool->obj_size); |
| } |
| |
| HIDDEN void |
| mempool_init (struct mempool *pool, size_t obj_size, size_t reserve) |
| { |
| if (pg_size == 0) |
| pg_size = getpagesize (); |
| |
| memset (pool, 0, sizeof (*pool)); |
| |
| lock_init (&pool->lock); |
| |
| /* round object-size up to integer multiple of MAX_ALIGN */ |
| obj_size = UNW_ALIGN(obj_size, MAX_ALIGN); |
| |
| if (!reserve) |
| { |
| reserve = pg_size / obj_size / 4; |
| if (!reserve) |
| reserve = 16; |
| } |
| |
| pool->obj_size = obj_size; |
| pool->reserve = reserve; |
| pool->chunk_size = UNW_ALIGN(2*reserve*obj_size, pg_size); |
| |
| expand (pool); |
| } |
| |
| HIDDEN void * |
| mempool_alloc (struct mempool *pool) |
| { |
| intrmask_t saved_mask; |
| struct object *obj; |
| |
| lock_acquire (&pool->lock, saved_mask); |
| { |
| if (pool->num_free <= pool->reserve) |
| expand (pool); |
| |
| assert (pool->num_free > 0); |
| |
| --pool->num_free; |
| obj = pool->free_list; |
| pool->free_list = obj->next; |
| } |
| lock_release (&pool->lock, saved_mask); |
| return obj; |
| } |
| |
| HIDDEN void |
| mempool_free (struct mempool *pool, void *object) |
| { |
| intrmask_t saved_mask; |
| |
| lock_acquire (&pool->lock, saved_mask); |
| { |
| free_object (pool, object); |
| } |
| lock_release (&pool->lock, saved_mask); |
| } |