1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2002-2003, 2005 Hewlett-Packard Co
3	Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4   Copyright (C) 2012 Tommi Rantala <tt.rantala@gmail.com>
5
6This file is part of libunwind.
7
8Permission is hereby granted, free of charge, to any person obtaining
9a copy of this software and associated documentation files (the
10"Software"), to deal in the Software without restriction, including
11without limitation the rights to use, copy, modify, merge, publish,
12distribute, sublicense, and/or sell copies of the Software, and to
13permit persons to whom the Software is furnished to do so, subject to
14the following conditions:
15
16The above copyright notice and this permission notice shall be
17included in all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
26
27#include "libunwind_i.h"
28
29/* From GCC docs: ``Gcc also provides a target specific macro
30 * __BIGGEST_ALIGNMENT__, which is the largest alignment ever used for any data
31 * type on the target machine you are compiling for.'' */
32#ifdef __BIGGEST_ALIGNMENT__
33# define MAX_ALIGN	__BIGGEST_ALIGNMENT__
34#else
35/* Crude hack to check that MAX_ALIGN is power-of-two.
36 * sizeof(long double) = 12 on i386. */
37# define MAX_ALIGN_(n)	(n < 8 ? 8 : \
38			 n < 16 ? 16 : n)
39# define MAX_ALIGN	MAX_ALIGN_(sizeof (long double))
40#endif
41
42static char sos_memory[SOS_MEMORY_SIZE] ALIGNED(MAX_ALIGN);
43static size_t sos_memory_freepos;
44static size_t pg_size;
45
46HIDDEN void *
47sos_alloc (size_t size)
48{
49  size_t pos;
50
51  size = UNW_ALIGN(size, MAX_ALIGN);
52
53#if defined(__GNUC__) && defined(HAVE_FETCH_AND_ADD)
54  /* Assume `sos_memory' is suitably aligned. */
55  assert(((uintptr_t) &sos_memory[0] & (MAX_ALIGN-1)) == 0);
56
57  pos = fetch_and_add (&sos_memory_freepos, size);
58#else
59  static define_lock (sos_lock);
60  intrmask_t saved_mask;
61
62  lock_acquire (&sos_lock, saved_mask);
63  {
64    /* No assumptions about `sos_memory' alignment. */
65    if (sos_memory_freepos == 0)
66      {
67	unsigned align = UNW_ALIGN((uintptr_t) &sos_memory[0], MAX_ALIGN)
68				- (uintptr_t) &sos_memory[0];
69	sos_memory_freepos = align;
70      }
71    pos = sos_memory_freepos;
72    sos_memory_freepos += size;
73  }
74  lock_release (&sos_lock, saved_mask);
75#endif
76
77  assert (((uintptr_t) &sos_memory[pos] & (MAX_ALIGN-1)) == 0);
78  assert ((pos+size) <= SOS_MEMORY_SIZE);
79
80  return &sos_memory[pos];
81}
82
83/* Must be called while holding the mempool lock. */
84
85static void
86free_object (struct mempool *pool, void *object)
87{
88  struct object *obj = object;
89
90  obj->next = pool->free_list;
91  pool->free_list = obj;
92  ++pool->num_free;
93}
94
95static void
96add_memory (struct mempool *pool, char *mem, size_t size, size_t obj_size)
97{
98  char *obj;
99
100  for (obj = mem; obj <= mem + size - obj_size; obj += obj_size)
101    free_object (pool, obj);
102}
103
104static void
105expand (struct mempool *pool)
106{
107  size_t size;
108  char *mem;
109
110  size = pool->chunk_size;
111  GET_MEMORY (mem, size);
112  if (!mem)
113    {
114      size = UNW_ALIGN(pool->obj_size, pg_size);
115      GET_MEMORY (mem, size);
116      if (!mem)
117	{
118	  /* last chance: try to allocate one object from the SOS memory */
119	  size = pool->obj_size;
120	  mem = sos_alloc (size);
121	}
122    }
123  add_memory (pool, mem, size, pool->obj_size);
124}
125
126HIDDEN void
127mempool_init (struct mempool *pool, size_t obj_size, size_t reserve)
128{
129  if (pg_size == 0)
130    pg_size = getpagesize ();
131
132  memset (pool, 0, sizeof (*pool));
133
134  lock_init (&pool->lock);
135
136  /* round object-size up to integer multiple of MAX_ALIGN */
137  obj_size = UNW_ALIGN(obj_size, MAX_ALIGN);
138
139  if (!reserve)
140    {
141      reserve = pg_size / obj_size / 4;
142      if (!reserve)
143	reserve = 16;
144    }
145
146  pool->obj_size = obj_size;
147  pool->reserve = reserve;
148  pool->chunk_size = UNW_ALIGN(2*reserve*obj_size, pg_size);
149
150  expand (pool);
151}
152
153HIDDEN void *
154mempool_alloc (struct mempool *pool)
155{
156  intrmask_t saved_mask;
157  struct object *obj;
158
159  lock_acquire (&pool->lock, saved_mask);
160  {
161    if (pool->num_free <= pool->reserve)
162      expand (pool);
163
164    assert (pool->num_free > 0);
165
166    --pool->num_free;
167    obj = pool->free_list;
168    pool->free_list = obj->next;
169  }
170  lock_release (&pool->lock, saved_mask);
171  return obj;
172}
173
174HIDDEN void
175mempool_free (struct mempool *pool, void *object)
176{
177  intrmask_t saved_mask;
178
179  lock_acquire (&pool->lock, saved_mask);
180  {
181    free_object (pool, object);
182  }
183  lock_release (&pool->lock, saved_mask);
184}
185