GNU libmicrohttpd  0.9.66
memorypool.c
Go to the documentation of this file.
1 /*
2  This file is part of libmicrohttpd
3  Copyright (C) 2007--2019 Daniel Pittman, Christian Grothoff and
4  Karlson2k (Evgeny Grin)
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Lesser General Public
8  License as published by the Free Software Foundation; either
9  version 2.1 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public
17  License along with this library; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 
27 #include "memorypool.h"
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdint.h>
31 #include "mhd_assert.h"
32 #if HAVE_SYS_MMAN_H
33 #include <sys/mman.h>
34 #endif
35 #ifdef _WIN32
36 #include <windows.h>
37 #endif
38 #ifdef HAVE_SYSCONF
39 #include <unistd.h>
40 #if defined(_SC_PAGE_SIZE)
41 #define MHD_SC_PAGESIZE _SC_PAGE_SIZE
42 #elif defined(_SC_PAGESIZE)
43 #define MHD_SC_PAGESIZE _SC_PAGESIZE
44 #endif /* _SC_PAGESIZE */
45 #endif /* HAVE_SYSCONF */
46 
47 /* define MAP_ANONYMOUS for Mac OS X */
48 #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
49 #define MAP_ANONYMOUS MAP_ANON
50 #endif
51 #if defined(_WIN32)
52 #define MAP_FAILED NULL
53 #elif !defined(MAP_FAILED)
54 #define MAP_FAILED ((void*)-1)
55 #endif
56 
60 #define ALIGN_SIZE (2 * sizeof(void*))
61 
65 #define ROUND_TO_ALIGN(n) (((n)+(ALIGN_SIZE-1)) / (ALIGN_SIZE) * (ALIGN_SIZE))
66 
67 #if defined(PAGE_SIZE)
68 #define MHD_DEF_PAGE_SIZE_ PAGE_SIZE
69 #elif defined(PAGESIZE)
70 #define MHD_DEF_PAGE_SIZE_ PAGE_SIZE
71 #else /* ! PAGESIZE */
72 #define MHD_DEF_PAGE_SIZE_ (4096)
73 #endif /* ! PAGESIZE */
74 
78 static size_t MHD_sys_page_size_ = MHD_DEF_PAGE_SIZE_; /* Default fallback value */
79 
83 void
85 {
86 #ifdef MHD_SC_PAGESIZE
87  long result;
88  result = sysconf (MHD_SC_PAGESIZE);
89  if (-1 != result)
90  MHD_sys_page_size_ = (size_t) result;
91  else
93 #elif defined(_WIN32)
94  SYSTEM_INFO si;
95  GetSystemInfo (&si);
96  MHD_sys_page_size_ = (size_t)si.dwPageSize;
97 #else
99 #endif /* _WIN32 */
100 }
101 
106 struct MemoryPool
107 {
108 
112  uint8_t *memory;
113 
117  size_t size;
118 
122  size_t pos;
123 
127  size_t end;
128 
132  bool is_mmap;
133 };
134 
135 
142 struct MemoryPool *
143 MHD_pool_create (size_t max)
144 {
145  struct MemoryPool *pool;
146  size_t alloc_size;
147 
148  pool = malloc (sizeof (struct MemoryPool));
149  if (NULL == pool)
150  return NULL;
151 #if defined(MAP_ANONYMOUS) || defined(_WIN32)
152  if ( (max <= 32 * 1024) ||
153  (max < MHD_sys_page_size_ * 4 / 3) )
154  pool->memory = MAP_FAILED;
155  else
156  {
157  /* Round up allocation to page granularity. */
158  alloc_size = max + MHD_sys_page_size_ - 1;
159  alloc_size -= alloc_size % MHD_sys_page_size_;
160 #if defined(MAP_ANONYMOUS) && !defined(_WIN32)
161  pool->memory = mmap (NULL,
162  alloc_size,
163  PROT_READ | PROT_WRITE,
164  MAP_PRIVATE | MAP_ANONYMOUS,
165  -1,
166  0);
167 #elif defined(_WIN32)
168  pool->memory = VirtualAlloc (NULL,
169  alloc_size,
170  MEM_COMMIT | MEM_RESERVE,
171  PAGE_READWRITE);
172 #endif /* _WIN32 */
173  }
174 #else /* ! _WIN32 && ! MAP_ANONYMOUS */
175  pool->memory = MAP_FAILED;
176 #endif /* ! _WIN32 && ! MAP_ANONYMOUS */
177  if (MAP_FAILED == pool->memory)
178  {
179  alloc_size = ROUND_TO_ALIGN(max);
180  pool->memory = malloc (alloc_size);
181  if (NULL == pool->memory)
182  {
183  free (pool);
184  return NULL;
185  }
186  pool->is_mmap = false;
187  }
188 #if defined(MAP_ANONYMOUS) || defined(_WIN32)
189  else
190  {
191  pool->is_mmap = true;
192  }
193 #endif /* _WIN32 || MAP_ANONYMOUS */
194  pool->pos = 0;
195  pool->end = alloc_size;
196  pool->size = alloc_size;
197  return pool;
198 }
199 
200 
206 void
207 MHD_pool_destroy (struct MemoryPool *pool)
208 {
209  if (NULL == pool)
210  return;
211 
212  mhd_assert (pool->end >= pool->pos);
213  mhd_assert (pool->size >= pool->end - pool->pos);
214  if (!pool->is_mmap)
215  free (pool->memory);
216  else
217 #if defined(MAP_ANONYMOUS) && !defined(_WIN32)
218  munmap (pool->memory,
219  pool->size);
220 #elif defined(_WIN32)
221  VirtualFree (pool->memory,
222  0,
223  MEM_RELEASE);
224 #else
225  abort ();
226 #endif
227  free (pool);
228 }
229 
230 
237 size_t
238 MHD_pool_get_free (struct MemoryPool *pool)
239 {
240  mhd_assert (pool->end >= pool->pos);
241  mhd_assert (pool->size >= pool->end - pool->pos);
242  return (pool->end - pool->pos);
243 }
244 
245 
257 void *
258 MHD_pool_allocate (struct MemoryPool *pool,
259  size_t size,
260  bool from_end)
261 {
262  void *ret;
263  size_t asize;
264 
265  mhd_assert (pool->end >= pool->pos);
266  mhd_assert (pool->size >= pool->end - pool->pos);
267  asize = ROUND_TO_ALIGN (size);
268  if ( (0 == asize) && (0 != size) )
269  return NULL; /* size too close to SIZE_MAX */
270  if ( (pool->pos + asize > pool->end) ||
271  (pool->pos + asize < pool->pos))
272  return NULL;
273  if (from_end)
274  {
275  ret = &pool->memory[pool->end - asize];
276  pool->end -= asize;
277  }
278  else
279  {
280  ret = &pool->memory[pool->pos];
281  pool->pos += asize;
282  }
283  return ret;
284 }
285 
286 
304 void *
305 MHD_pool_reallocate (struct MemoryPool *pool,
306  void *old,
307  size_t old_size,
308  size_t new_size)
309 {
310  size_t asize;
311  uint8_t *new_blc;
312 
313  mhd_assert (pool->end >= pool->pos);
314  mhd_assert (pool->size >= pool->end - pool->pos);
315  mhd_assert (old != NULL || old_size == 0);
316  mhd_assert (old == NULL || pool->memory <= (uint8_t*)old);
317  mhd_assert (old == NULL || pool->memory + pool->size >= (uint8_t*)old + old_size);
318  /* Blocks "from the end" must not be reallocated */
319  mhd_assert (old == NULL || pool->memory + pool->pos > (uint8_t*)old);
320 
321  if (0 != old_size)
322  { /* Need to save some data */
323  const size_t old_offset = (uint8_t*)old - pool->memory;
324  const bool shrinking = (old_size > new_size);
325  /* Try resizing in-place */
326  if (shrinking)
327  { /* Shrinking in-place, zero-out freed part */
328  memset ((uint8_t*)old + new_size, 0, old_size - new_size);
329  }
330  if (pool->pos == ROUND_TO_ALIGN (old_offset + old_size))
331  { /* "old" block is the last allocated block */
332  const size_t new_apos = ROUND_TO_ALIGN (old_offset + new_size);
333  if (!shrinking)
334  { /* Grow in-place, check for enough space. */
335  if ( (new_apos > pool->end) ||
336  (new_apos < pool->pos) ) /* Value wrap */
337  return NULL; /* No space */
338  }
339  /* Resized in-place */
340  pool->pos = new_apos;
341  return old;
342  }
343  if (shrinking)
344  return old; /* Resized in-place, freed part remains allocated */
345  }
346  /* Need to allocate new block */
347  asize = ROUND_TO_ALIGN (new_size);
348  if ( ( (0 == asize) &&
349  (0 != new_size) ) || /* Value wrap, too large new_size. */
350  (asize > pool->end - pool->pos) ) /* Not enough space */
351  return NULL;
352 
353  new_blc = pool->memory + pool->pos;
354  pool->pos += asize;
355 
356  if (0 != old_size)
357  {
358  /* Move data to new block, old block remains allocated */
359  memcpy (new_blc, old, old_size);
360  /* Zero-out old block */
361  memset (old, 0, old_size);
362  }
363  return new_blc;
364 }
365 
366 
380 void *
381 MHD_pool_reset (struct MemoryPool *pool,
382  void *keep,
383  size_t copy_bytes,
384  size_t new_size)
385 {
386  mhd_assert (pool->end >= pool->pos);
387  mhd_assert (pool->size >= pool->end - pool->pos);
388  mhd_assert (copy_bytes < new_size);
389  mhd_assert (keep != NULL || copy_bytes == 0);
390  mhd_assert (keep == NULL || pool->memory <= (uint8_t*)keep);
391  mhd_assert (keep == NULL || pool->memory + pool->size >= (uint8_t*)keep + copy_bytes);
392  if ( (NULL != keep) &&
393  (keep != pool->memory) )
394  {
395  if (0 != copy_bytes)
396  memmove (pool->memory,
397  keep,
398  copy_bytes);
399  }
400  /* technically not needed, but safer to zero out */
401  if (pool->size > copy_bytes)
402  {
403  size_t to_zero;
405  to_zero = pool->size - copy_bytes;
406 #ifdef _WIN32
407  if (pool->is_mmap)
408  {
409  size_t to_recommit;
410  uint8_t *recommit_addr;
411  /* Round down to page size */
412  to_recommit = to_zero - to_zero % MHD_sys_page_size_;
413  recommit_addr = pool->memory + pool->size - to_recommit;
414 
415  /* De-committing and re-committing again clear memory and make
416  * pages free / available for other needs until accessed. */
417  if (VirtualFree (recommit_addr,
418  to_recommit,
419  MEM_DECOMMIT))
420  {
421  to_zero -= to_recommit;
422 
423  if (recommit_addr != VirtualAlloc (recommit_addr,
424  to_recommit,
425  MEM_COMMIT,
426  PAGE_READWRITE))
427  abort(); /* Serious error, must never happen */
428  }
429  }
430 #endif /* _WIN32 */
431  memset (&pool->memory[copy_bytes],
432  0,
433  to_zero);
434  }
435  pool->pos = ROUND_TO_ALIGN (new_size);
436  pool->end = pool->size;
437  return pool->memory;
438 }
439 
440 
441 /* end of memorypool.c */
void MHD_init_mem_pools_(void)
Definition: memorypool.c:84
size_t MHD_pool_get_free(struct MemoryPool *pool)
Definition: memorypool.c:185
void * MHD_pool_reset(struct MemoryPool *pool, void *keep, size_t copy_bytes, size_t new_size)
Definition: memorypool.c:314
static size_t MHD_sys_page_size_
Definition: memorypool.c:78
#define MAP_FAILED
Definition: memorypool.c:54
void * MHD_pool_allocate(struct MemoryPool *pool, size_t size, int from_end)
Definition: memorypool.c:203
macros for mhd_assert()
struct MemoryPool * MHD_pool_create(size_t max)
Definition: memorypool.c:102
#define ROUND_TO_ALIGN(n)
Definition: memorypool.c:65
void * MHD_pool_reallocate(struct MemoryPool *pool, void *old, size_t old_size, size_t new_size)
Definition: memorypool.c:248
#define NULL
Definition: reason_phrase.c:30
void MHD_pool_destroy(struct MemoryPool *pool)
Definition: memorypool.c:157
#define mhd_assert(CHK)
Definition: mhd_assert.h:39
#define MHD_DEF_PAGE_SIZE_
Definition: memorypool.c:72