GNU libmicrohttpd  0.9.68
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)) \
66  / (ALIGN_SIZE) *(ALIGN_SIZE))
67 
68 #if defined(PAGE_SIZE)
69 #define MHD_DEF_PAGE_SIZE_ PAGE_SIZE
70 #elif defined(PAGESIZE)
71 #define MHD_DEF_PAGE_SIZE_ PAGE_SIZE
72 #else /* ! PAGESIZE */
73 #define MHD_DEF_PAGE_SIZE_ (4096)
74 #endif /* ! PAGESIZE */
75 
79 static size_t MHD_sys_page_size_ = MHD_DEF_PAGE_SIZE_; /* Default fallback value */
80 
84 void
86 {
87 #ifdef MHD_SC_PAGESIZE
88  long result;
89  result = sysconf (MHD_SC_PAGESIZE);
90  if (-1 != result)
91  MHD_sys_page_size_ = (size_t) result;
92  else
94 #elif defined(_WIN32)
95  SYSTEM_INFO si;
96  GetSystemInfo (&si);
97  MHD_sys_page_size_ = (size_t) si.dwPageSize;
98 #else
100 #endif /* _WIN32 */
101 }
102 
107 struct MemoryPool
108 {
109 
113  uint8_t *memory;
114 
118  size_t size;
119 
123  size_t pos;
124 
128  size_t end;
129 
133  bool is_mmap;
134 };
135 
136 
143 struct MemoryPool *
144 MHD_pool_create (size_t max)
145 {
146  struct MemoryPool *pool;
147  size_t alloc_size;
148 
149  pool = malloc (sizeof (struct MemoryPool));
150  if (NULL == pool)
151  return NULL;
152 #if defined(MAP_ANONYMOUS) || defined(_WIN32)
153  if ( (max <= 32 * 1024) ||
154  (max < MHD_sys_page_size_ * 4 / 3) )
155  {
156  pool->memory = MAP_FAILED;
157  }
158  else
159  {
160  /* Round up allocation to page granularity. */
161  alloc_size = max + MHD_sys_page_size_ - 1;
162  alloc_size -= alloc_size % MHD_sys_page_size_;
163 #if defined(MAP_ANONYMOUS) && ! defined(_WIN32)
164  pool->memory = mmap (NULL,
165  alloc_size,
166  PROT_READ | PROT_WRITE,
167  MAP_PRIVATE | MAP_ANONYMOUS,
168  -1,
169  0);
170 #elif defined(_WIN32)
171  pool->memory = VirtualAlloc (NULL,
172  alloc_size,
173  MEM_COMMIT | MEM_RESERVE,
174  PAGE_READWRITE);
175 #endif /* _WIN32 */
176  }
177 #else /* ! _WIN32 && ! MAP_ANONYMOUS */
178  pool->memory = MAP_FAILED;
179 #endif /* ! _WIN32 && ! MAP_ANONYMOUS */
180  if (MAP_FAILED == pool->memory)
181  {
182  alloc_size = ROUND_TO_ALIGN (max);
183  pool->memory = malloc (alloc_size);
184  if (NULL == pool->memory)
185  {
186  free (pool);
187  return NULL;
188  }
189  pool->is_mmap = false;
190  }
191 #if defined(MAP_ANONYMOUS) || defined(_WIN32)
192  else
193  {
194  pool->is_mmap = true;
195  }
196 #endif /* _WIN32 || MAP_ANONYMOUS */
197  pool->pos = 0;
198  pool->end = alloc_size;
199  pool->size = alloc_size;
200  return pool;
201 }
202 
203 
209 void
210 MHD_pool_destroy (struct MemoryPool *pool)
211 {
212  if (NULL == pool)
213  return;
214 
215  mhd_assert (pool->end >= pool->pos);
216  mhd_assert (pool->size >= pool->end - pool->pos);
217  if (! pool->is_mmap)
218  free (pool->memory);
219  else
220 #if defined(MAP_ANONYMOUS) && ! defined(_WIN32)
221  munmap (pool->memory,
222  pool->size);
223 #elif defined(_WIN32)
224  VirtualFree (pool->memory,
225  0,
226  MEM_RELEASE);
227 #else
228  abort ();
229 #endif
230  free (pool);
231 }
232 
233 
240 size_t
241 MHD_pool_get_free (struct MemoryPool *pool)
242 {
243  mhd_assert (pool->end >= pool->pos);
244  mhd_assert (pool->size >= pool->end - pool->pos);
245  return (pool->end - pool->pos);
246 }
247 
248 
260 void *
261 MHD_pool_allocate (struct MemoryPool *pool,
262  size_t size,
263  bool from_end)
264 {
265  void *ret;
266  size_t asize;
267 
268  mhd_assert (pool->end >= pool->pos);
269  mhd_assert (pool->size >= pool->end - pool->pos);
270  asize = ROUND_TO_ALIGN (size);
271  if ( (0 == asize) && (0 != size) )
272  return NULL; /* size too close to SIZE_MAX */
273  if ( (pool->pos + asize > pool->end) ||
274  (pool->pos + asize < pool->pos))
275  return NULL;
276  if (from_end)
277  {
278  ret = &pool->memory[pool->end - asize];
279  pool->end -= asize;
280  }
281  else
282  {
283  ret = &pool->memory[pool->pos];
284  pool->pos += asize;
285  }
286  return ret;
287 }
288 
289 
307 void *
308 MHD_pool_reallocate (struct MemoryPool *pool,
309  void *old,
310  size_t old_size,
311  size_t new_size)
312 {
313  size_t asize;
314  uint8_t *new_blc;
315 
316  mhd_assert (pool->end >= pool->pos);
317  mhd_assert (pool->size >= pool->end - pool->pos);
318  mhd_assert (old != NULL || old_size == 0);
319  mhd_assert (old == NULL || pool->memory <= (uint8_t*) old);
320  mhd_assert (old == NULL || pool->memory + pool->size >= (uint8_t*) old
321  + old_size);
322  /* Blocks "from the end" must not be reallocated */
323  mhd_assert (old == NULL || pool->memory + pool->pos > (uint8_t*) old);
324 
325  if (0 != old_size)
326  { /* Need to save some data */
327  const size_t old_offset = (uint8_t*) old - pool->memory;
328  const bool shrinking = (old_size > new_size);
329  /* Try resizing in-place */
330  if (shrinking)
331  { /* Shrinking in-place, zero-out freed part */
332  memset ((uint8_t*) old + new_size, 0, old_size - new_size);
333  }
334  if (pool->pos == ROUND_TO_ALIGN (old_offset + old_size))
335  { /* "old" block is the last allocated block */
336  const size_t new_apos = ROUND_TO_ALIGN (old_offset + new_size);
337  if (! shrinking)
338  { /* Grow in-place, check for enough space. */
339  if ( (new_apos > pool->end) ||
340  (new_apos < pool->pos) ) /* Value wrap */
341  return NULL; /* No space */
342  }
343  /* Resized in-place */
344  pool->pos = new_apos;
345  return old;
346  }
347  if (shrinking)
348  return old; /* Resized in-place, freed part remains allocated */
349  }
350  /* Need to allocate new block */
351  asize = ROUND_TO_ALIGN (new_size);
352  if ( ( (0 == asize) &&
353  (0 != new_size) ) || /* Value wrap, too large new_size. */
354  (asize > pool->end - pool->pos) ) /* Not enough space */
355  return NULL;
356 
357  new_blc = pool->memory + pool->pos;
358  pool->pos += asize;
359 
360  if (0 != old_size)
361  {
362  /* Move data to new block, old block remains allocated */
363  memcpy (new_blc, old, old_size);
364  /* Zero-out old block */
365  memset (old, 0, old_size);
366  }
367  return new_blc;
368 }
369 
370 
384 void *
385 MHD_pool_reset (struct MemoryPool *pool,
386  void *keep,
387  size_t copy_bytes,
388  size_t new_size)
389 {
390  mhd_assert (pool->end >= pool->pos);
391  mhd_assert (pool->size >= pool->end - pool->pos);
392  mhd_assert (copy_bytes < new_size);
393  mhd_assert (keep != NULL || copy_bytes == 0);
394  mhd_assert (keep == NULL || pool->memory <= (uint8_t*) keep);
395  mhd_assert (keep == NULL || pool->memory + pool->size >= (uint8_t*) keep
396  + copy_bytes);
397  if ( (NULL != keep) &&
398  (keep != pool->memory) )
399  {
400  if (0 != copy_bytes)
401  memmove (pool->memory,
402  keep,
403  copy_bytes);
404  }
405  /* technically not needed, but safer to zero out */
406  if (pool->size > copy_bytes)
407  {
408  size_t to_zero;
410  to_zero = pool->size - copy_bytes;
411 #ifdef _WIN32
412  if (pool->is_mmap)
413  {
414  size_t to_recommit;
415  uint8_t *recommit_addr;
416  /* Round down to page size */
417  to_recommit = to_zero - to_zero % MHD_sys_page_size_;
418  recommit_addr = pool->memory + pool->size - to_recommit;
419 
420  /* De-committing and re-committing again clear memory and make
421  * pages free / available for other needs until accessed. */
422  if (VirtualFree (recommit_addr,
423  to_recommit,
424  MEM_DECOMMIT))
425  {
426  to_zero -= to_recommit;
427 
428  if (recommit_addr != VirtualAlloc (recommit_addr,
429  to_recommit,
430  MEM_COMMIT,
431  PAGE_READWRITE))
432  abort (); /* Serious error, must never happen */
433  }
434  }
435 #endif /* _WIN32 */
436  memset (&pool->memory[copy_bytes],
437  0,
438  to_zero);
439  }
440  pool->pos = ROUND_TO_ALIGN (new_size);
441  pool->end = pool->size;
442  return pool->memory;
443 }
444 
445 
446 /* end of memorypool.c */
void MHD_init_mem_pools_(void)
Definition: memorypool.c:85
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:79
#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:73