GNU libmicrohttpd  0.9.72
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) && (0 < (PAGE_SIZE + 0))
69 #define MHD_DEF_PAGE_SIZE_ PAGE_SIZE
70 #elif defined(PAGESIZE) && (0 < (PAGESIZE + 0))
71 #define MHD_DEF_PAGE_SIZE_ PAGESIZE
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 */
102 }
103 
104 
109 struct MemoryPool
110 {
111 
115  uint8_t *memory;
116 
120  size_t size;
121 
125  size_t pos;
126 
130  size_t end;
131 
135  bool is_mmap;
136 };
137 
138 
145 struct MemoryPool *
146 MHD_pool_create (size_t max)
147 {
148  struct MemoryPool *pool;
149  size_t alloc_size;
150 
151  mhd_assert (max > 0);
152  pool = malloc (sizeof (struct MemoryPool));
153  if (NULL == pool)
154  return NULL;
155 #if defined(MAP_ANONYMOUS) || defined(_WIN32)
156  if ( (max <= 32 * 1024) ||
157  (max < MHD_sys_page_size_ * 4 / 3) )
158  {
159  pool->memory = MAP_FAILED;
160  }
161  else
162  {
163  /* Round up allocation to page granularity. */
164  alloc_size = max + MHD_sys_page_size_ - 1;
165  alloc_size -= alloc_size % MHD_sys_page_size_;
166 #if defined(MAP_ANONYMOUS) && ! defined(_WIN32)
167  pool->memory = mmap (NULL,
168  alloc_size,
169  PROT_READ | PROT_WRITE,
170  MAP_PRIVATE | MAP_ANONYMOUS,
171  -1,
172  0);
173 #elif defined(_WIN32)
174  pool->memory = VirtualAlloc (NULL,
175  alloc_size,
176  MEM_COMMIT | MEM_RESERVE,
177  PAGE_READWRITE);
178 #endif /* _WIN32 */
179  }
180 #else /* ! _WIN32 && ! MAP_ANONYMOUS */
181  pool->memory = MAP_FAILED;
182 #endif /* ! _WIN32 && ! MAP_ANONYMOUS */
183  if (MAP_FAILED == pool->memory)
184  {
185  alloc_size = ROUND_TO_ALIGN (max);
186  pool->memory = malloc (alloc_size);
187  if (NULL == pool->memory)
188  {
189  free (pool);
190  return NULL;
191  }
192  pool->is_mmap = false;
193  }
194 #if defined(MAP_ANONYMOUS) || defined(_WIN32)
195  else
196  {
197  pool->is_mmap = true;
198  }
199 #endif /* _WIN32 || MAP_ANONYMOUS */
200  mhd_assert (0 == (((uintptr_t) pool->memory) % ALIGN_SIZE));
201  pool->pos = 0;
202  pool->end = alloc_size;
203  pool->size = alloc_size;
204  return pool;
205 }
206 
207 
213 void
214 MHD_pool_destroy (struct MemoryPool *pool)
215 {
216  if (NULL == pool)
217  return;
218 
219  mhd_assert (pool->end >= pool->pos);
220  mhd_assert (pool->size >= pool->end - pool->pos);
221  if (! pool->is_mmap)
222  free (pool->memory);
223  else
224 #if defined(MAP_ANONYMOUS) && ! defined(_WIN32)
225  munmap (pool->memory,
226  pool->size);
227 #elif defined(_WIN32)
228  VirtualFree (pool->memory,
229  0,
230  MEM_RELEASE);
231 #else
232  abort ();
233 #endif
234  free (pool);
235 }
236 
237 
244 size_t
245 MHD_pool_get_free (struct MemoryPool *pool)
246 {
247  mhd_assert (pool->end >= pool->pos);
248  mhd_assert (pool->size >= pool->end - pool->pos);
249  return (pool->end - pool->pos);
250 }
251 
252 
264 void *
265 MHD_pool_allocate (struct MemoryPool *pool,
266  size_t size,
267  bool from_end)
268 {
269  void *ret;
270  size_t asize;
271 
272  mhd_assert (pool->end >= pool->pos);
273  mhd_assert (pool->size >= pool->end - pool->pos);
274  asize = ROUND_TO_ALIGN (size);
275  if ( (0 == asize) && (0 != size) )
276  return NULL; /* size too close to SIZE_MAX */
277  if ( (pool->pos + asize > pool->end) ||
278  (pool->pos + asize < pool->pos))
279  return NULL;
280  if (from_end)
281  {
282  ret = &pool->memory[pool->end - asize];
283  pool->end -= asize;
284  }
285  else
286  {
287  ret = &pool->memory[pool->pos];
288  pool->pos += asize;
289  }
290  return ret;
291 }
292 
293 
311 void *
312 MHD_pool_reallocate (struct MemoryPool *pool,
313  void *old,
314  size_t old_size,
315  size_t new_size)
316 {
317  size_t asize;
318  uint8_t *new_blc;
319 
320  mhd_assert (pool->end >= pool->pos);
321  mhd_assert (pool->size >= pool->end - pool->pos);
322  mhd_assert (old != NULL || old_size == 0);
323  mhd_assert (old == NULL || pool->memory <= (uint8_t*) old);
324  mhd_assert (old == NULL || pool->memory + pool->size >= (uint8_t*) old
325  + old_size);
326  /* Blocks "from the end" must not be reallocated */
327  mhd_assert (old == NULL || old_size == 0 || \
328  pool->memory + pool->pos > (uint8_t*) old);
329 
330  if (0 != old_size)
331  { /* Need to save some data */
332  const size_t old_offset = (uint8_t*) old - pool->memory;
333  const bool shrinking = (old_size > new_size);
334  /* Try resizing in-place */
335  if (shrinking)
336  { /* Shrinking in-place, zero-out freed part */
337  memset ((uint8_t*) old + new_size, 0, old_size - new_size);
338  }
339  if (pool->pos == ROUND_TO_ALIGN (old_offset + old_size))
340  { /* "old" block is the last allocated block */
341  const size_t new_apos = ROUND_TO_ALIGN (old_offset + new_size);
342  if (! shrinking)
343  { /* Grow in-place, check for enough space. */
344  if ( (new_apos > pool->end) ||
345  (new_apos < pool->pos) ) /* Value wrap */
346  return NULL; /* No space */
347  }
348  /* Resized in-place */
349  pool->pos = new_apos;
350  return old;
351  }
352  if (shrinking)
353  return old; /* Resized in-place, freed part remains allocated */
354  }
355  /* Need to allocate new block */
356  asize = ROUND_TO_ALIGN (new_size);
357  if ( ( (0 == asize) &&
358  (0 != new_size) ) || /* Value wrap, too large new_size. */
359  (asize > pool->end - pool->pos) ) /* Not enough space */
360  return NULL;
361 
362  new_blc = pool->memory + pool->pos;
363  pool->pos += asize;
364 
365  if (0 != old_size)
366  {
367  /* Move data to new block, old block remains allocated */
368  memcpy (new_blc, old, old_size);
369  /* Zero-out old block */
370  memset (old, 0, old_size);
371  }
372  return new_blc;
373 }
374 
375 
389 void *
390 MHD_pool_reset (struct MemoryPool *pool,
391  void *keep,
392  size_t copy_bytes,
393  size_t new_size)
394 {
395  mhd_assert (pool->end >= pool->pos);
396  mhd_assert (pool->size >= pool->end - pool->pos);
397  mhd_assert (copy_bytes < new_size);
398  mhd_assert (keep != NULL || copy_bytes == 0);
399  mhd_assert (keep == NULL || pool->memory <= (uint8_t*) keep);
400  mhd_assert (keep == NULL || pool->memory + pool->size >= (uint8_t*) keep
401  + copy_bytes);
402  if ( (NULL != keep) &&
403  (keep != pool->memory) )
404  {
405  if (0 != copy_bytes)
406  memmove (pool->memory,
407  keep,
408  copy_bytes);
409  }
410  /* technically not needed, but safer to zero out */
411  if (pool->size > copy_bytes)
412  {
413  size_t to_zero;
415  to_zero = pool->size - copy_bytes;
416 #ifdef _WIN32
417  if (pool->is_mmap)
418  {
419  size_t to_recommit;
420  uint8_t *recommit_addr;
421  /* Round down to page size */
422  to_recommit = to_zero - to_zero % MHD_sys_page_size_;
423  recommit_addr = pool->memory + pool->size - to_recommit;
424 
425  /* De-committing and re-committing again clear memory and make
426  * pages free / available for other needs until accessed. */
427  if (VirtualFree (recommit_addr,
428  to_recommit,
429  MEM_DECOMMIT))
430  {
431  to_zero -= to_recommit;
432 
433  if (recommit_addr != VirtualAlloc (recommit_addr,
434  to_recommit,
435  MEM_COMMIT,
436  PAGE_READWRITE))
437  abort (); /* Serious error, must never happen */
438  }
439  }
440 #endif /* _WIN32 */
441  memset (&pool->memory[copy_bytes],
442  0,
443  to_zero);
444  }
445  pool->pos = ROUND_TO_ALIGN (new_size);
446  pool->end = pool->size;
447  return pool->memory;
448 }
449 
450 
451 /* end of memorypool.c */
void MHD_pool_destroy(struct MemoryPool *pool)
Definition: memorypool.c:157
void * MHD_pool_reallocate(struct MemoryPool *pool, void *old, size_t old_size, size_t new_size)
Definition: memorypool.c:248
size_t MHD_pool_get_free(struct MemoryPool *pool)
Definition: memorypool.c:185
struct MemoryPool * MHD_pool_create(size_t max)
Definition: memorypool.c:102
void * MHD_pool_reset(struct MemoryPool *pool, void *keep, size_t copy_bytes, size_t new_size)
Definition: memorypool.c:314
void * MHD_pool_allocate(struct MemoryPool *pool, size_t size, int from_end)
Definition: memorypool.c:203
#define mhd_assert(CHK)
Definition: mhd_assert.h:39
#define NULL
Definition: reason_phrase.c:30
void MHD_init_mem_pools_(void)
Definition: memorypool.c:85
#define MHD_DEF_PAGE_SIZE_
Definition: memorypool.c:73
#define ALIGN_SIZE
Definition: memorypool.c:60
#define MAP_FAILED
Definition: memorypool.c:54
static size_t MHD_sys_page_size_
Definition: memorypool.c:79
#define ROUND_TO_ALIGN(n)
Definition: memorypool.c:65
memory pool; mostly used for efficient (de)allocation for each connection and bounding memory use for...
macros for mhd_assert()