root/maint/gnulib/lib/immutable.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. init_pagesize
  2. init_mmap_file
  3. do_init_mmap_file
  4. gl_once_define
  5. alloc_pages
  6. free_pages
  7. immmalloc
  8. immfreeze
  9. immfree
  10. immmalloc
  11. immfreeze
  12. immfree
  13. immstrdup

   1 /* Immutable data.
   2 
   3    Copyright (C) 2021 Free Software Foundation, Inc.
   4 
   5    This file is free software: you can redistribute it and/or modify
   6    it under the terms of the GNU Lesser General Public License as
   7    published by the Free Software Foundation; either version 2.1 of the
   8    License, or (at your option) any later version.
   9 
  10    This file is distributed in the hope that it will be useful,
  11    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13    GNU Lesser General Public License for more details.
  14 
  15    You should have received a copy of the GNU Lesser General Public License
  16    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  17 
  18 /* Written by Bruno Haible <bruno@clisp.org>, 2021.  */
  19 
  20 #include <config.h>
  21 
  22 /* Specification.  */
  23 #include "immutable.h"
  24 
  25 #include <errno.h>
  26 #include <stdlib.h>
  27 #include <string.h>
  28 
  29 #if IMMUTABLE_EFFECTIVE
  30 /* Real implementation.  */
  31 
  32 /* Get CHAR_BIT.  */
  33 # include <limits.h>
  34 
  35 /* Get intptr_t, uintptr_t.  */
  36 # include <stdint.h>
  37 
  38 # include <stdio.h>
  39 
  40 # if defined _WIN32 && !defined __CYGWIN__
  41 
  42 /* Declare VirtualAlloc(), GetSystemInfo.  */
  43 #  define WIN32_LEAN_AND_MEAN
  44 #  define WIN32_EXTRA_LEAN
  45 #  include <windows.h>
  46 
  47 /* Don't assume that UNICODE is not defined.  */
  48 #  undef CreateFileMapping
  49 #  define CreateFileMapping CreateFileMappingA
  50 
  51 # else
  52 
  53 /* Declare getpagesize().  */
  54 #  include <unistd.h>
  55 /* On HP-UX, getpagesize exists, but it is not declared in <unistd.h> even if
  56    the compiler options -D_HPUX_SOURCE -D_XOPEN_SOURCE=600 are used.  */
  57 #  ifdef __hpux
  58 extern
  59 #   ifdef __cplusplus
  60        "C"
  61 #   endif
  62        int getpagesize (void);
  63 #  endif
  64 
  65 /* Declare mmap(), mprotect().  */
  66 #  include <sys/types.h>
  67 #  include <sys/mman.h>
  68 
  69 /* Declare open().  */
  70 #  include <unistd.h>
  71 #  include <fcntl.h>
  72 
  73 #  include "glthread/lock.h"
  74 
  75 # endif
  76 
  77 
  78 /* ================= Back end of the malloc implementation ================= */
  79 
  80 /* The memory page size.
  81    Once it is initialized, a power of 2.  Typically 4096 or 8192.  */
  82 static uintptr_t pagesize;
  83 
  84 /* Initializes pagesize.  */
  85 static void
  86 init_pagesize (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  87 {
  88   /* Simultaneous execution of this initialization in multiple threads is OK. */
  89 # if defined _WIN32 && !defined __CYGWIN__
  90   /* GetSystemInfo
  91      <https://msdn.microsoft.com/en-us/library/ms724381.aspx>
  92      <https://msdn.microsoft.com/en-us/library/ms724958.aspx>  */
  93   SYSTEM_INFO info;
  94   GetSystemInfo (&info);
  95   pagesize = info.dwPageSize;
  96 # else
  97   pagesize = getpagesize ();
  98 # endif
  99 }
 100 
 101 
 102 # if defined _WIN32 && !defined __CYGWIN__
 103 
 104 static inline void
 105 init_mmap_file (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 106 {
 107 }
 108 
 109 # else
 110 
 111 /* Variables needed for obtaining memory pages via mmap().  */
 112 static int file_fd;
 113 static long file_length;
 114 
 115 /* Initialization of these variables. */
 116 static void
 117 do_init_mmap_file (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 118 {
 119   char filename[100];
 120   sprintf (filename, "%s/glimmdata-%d-%ld", "/tmp", getpid (), random ());
 121   file_fd = open (filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0700);
 122   if (file_fd < 0)
 123     {
 124       fprintf (stderr, "glimm: Cannot open %s!\n", filename);
 125       abort ();
 126     }
 127   /* Remove the file from the file system as soon as possible, to make
 128      sure there is no leftover after this process terminates or crashes.  */
 129   unlink (filename);
 130 
 131   file_length = 0;
 132 }
 133 
 134 /* Once-only initializer for these variables.  */
 135 gl_once_define (static, for_mmap_once)
     /* [previous][next][first][last][top][bottom][index][help] */
 136 
 137 static inline void
 138 init_mmap_file (void)
 139 {
 140   /* Use a once-only initializer here, since simultaneous execution of
 141      do_init_mmap_file() in multiple threads must be avoided.  */
 142   gl_once (for_mmap_once, do_init_mmap_file);
 143 }
 144 
 145 # endif
 146 
 147 
 148 /* Size of the (page-aligned) header that links the writable mapping
 149    and the read-only mapping together.  */
 150 # define SHARED_LINK_HEADER_SIZE \
 151     (INTPTR_WIDTH / CHAR_BIT)   /* = sizeof (void *) */
 152 
 153 /* Allocates a contiguous set of pages of memory.
 154    size > 0, must be a multiple of pagesize.
 155    Returns a multiple of PAGESIZE, or 0 upon failure.  */
 156 static uintptr_t
 157 alloc_pages (size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 158 {
 159 # if defined _WIN32 && !defined __CYGWIN__
 160   /* Allocate pages from the system paging file.
 161      CreateFileMapping
 162      <https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createfilemappinga>  */
 163   HANDLE h =
 164     CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT,
 165                        size >> 16 >> 16, size & 0xFFFFFFFFU, NULL);
 166   if (h == NULL)
 167     {
 168       fprintf (stderr,
 169                "glimm: Cannot allocate file mapping. GetLastError() = 0x%08X\n",
 170                (unsigned int) GetLastError ());
 171       return 0;
 172     }
 173   /* MapViewOfFile
 174      <https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffile>  */
 175   char *mem_w = (char *) MapViewOfFile (h, FILE_MAP_WRITE, 0, 0, size);
 176   char *mem_r = (char *) MapViewOfFile (h, FILE_MAP_READ,  0, 0, size);
 177   if (mem_w == NULL || mem_r == NULL)
 178     {
 179       /* UnmapViewOfFile
 180          <https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-unmapviewoffile>  */
 181       if (mem_w != NULL)
 182         UnmapViewOfFile (mem_w);
 183       if (mem_r != NULL)
 184         UnmapViewOfFile (mem_r);
 185       return 0;
 186     }
 187   /* It is OK to call CloseHandle before UnmapViewOfFile.  The file mapping
 188      object gets really closed only once all its views are unmapped.  */
 189   if (!CloseHandle (h))
 190     {
 191       UnmapViewOfFile (mem_w);
 192       UnmapViewOfFile (mem_r);
 193       CloseHandle (h);
 194       return 0;
 195     }
 196 # else
 197   /* Extend the file by size/pagesize pages.  */
 198   long new_file_length = file_length + size;
 199   if (ftruncate (file_fd, new_file_length) < 0)
 200     {
 201       fprintf (stderr, "glimm: Cannot extend backing file!\n");
 202       return 0;
 203     }
 204   /* Create separate writable mapping and read-only mapping.  */
 205   char *mem_w = (char *) mmap (NULL, size, PROT_READ | PROT_WRITE,
 206                                MAP_SHARED, file_fd, file_length);
 207   char *mem_r = (char *) mmap (NULL, size, PROT_READ,
 208                                MAP_SHARED, file_fd, file_length);
 209   if (mem_w == (char *)(-1) || mem_r == (char *)(-1))
 210     {
 211       if (mem_w != (char *)(-1))
 212         munmap (mem_w, size);
 213       if (mem_r != (char *)(-1))
 214         munmap (mem_r, size);
 215       return 0;
 216     }
 217   file_length = new_file_length;
 218 # endif
 219 
 220   /* Link the two memory areas together.  */
 221   ((intptr_t *) mem_w)[0] = mem_r - mem_w;
 222   return (uintptr_t) mem_w;
 223 }
 224 
 225 /* Frees a contiguous set of pages of memory, returned by alloc_pages.
 226    size > 0, must be a multiple of pagesize.  */
 227 static void
 228 free_pages (uintptr_t pages, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 229 {
 230   pages -= SHARED_LINK_HEADER_SIZE;
 231   if ((pages & (pagesize - 1)) != 0)
 232     abort ();
 233   char *mem_w = (char *) pages;
 234   char *mem_r = mem_w + ((intptr_t *) mem_w)[0];
 235 # if defined _WIN32 && !defined __CYGWIN__
 236   if (!UnmapViewOfFile (mem_w))
 237     abort ();
 238   if (!UnmapViewOfFile (mem_r))
 239     abort ();
 240 # else
 241   if (munmap (mem_w, size) < 0)
 242     abort ();
 243   if (munmap (mem_r, size) < 0)
 244     abort ();
 245 # endif
 246 }
 247 
 248 /* Cygwin defines PAGESIZE in <limits.h>.  */
 249 # undef PAGESIZE
 250 
 251 /* ======================= Instantiate the front end ======================= */
 252 
 253 # define PAGESIZE pagesize
 254 /* On Cygwin and Linux/PowerPC, PAGESIZE is 65536.  On macOS 11, it is 16384.
 255    On all other platforms, it is either 4096 or 8192.  */
 256 # if defined __CYGWIN__ || (defined __linux__ && defined __powerpc__)
 257 #  define PAGESIZE_MAX 65536
 258 # else
 259 #  define PAGESIZE_MAX 16384
 260 # endif
 261 
 262 # define ALLOC_PAGES alloc_pages
 263 # define FREE_PAGES free_pages
 264 # define ALIGNMENT sizeof (void *)
 265 # define PAGE_RESERVED_HEADER_SIZE SHARED_LINK_HEADER_SIZE
 266 
 267 # include "ssfmalloc.h"
 268 
 269 
 270 void *
 271 immmalloc (size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 272 {
 273   /* Initializations.  */
 274   if (!pagesize)
 275     {
 276       init_mmap_file ();
 277       init_pagesize ();
 278     }
 279 
 280   void *writable_pointer = (void *) allocate_block (size);
 281   if (writable_pointer == NULL)
 282     errno = ENOMEM;
 283   return writable_pointer;
 284 }
 285 
 286 const void *
 287 immfreeze (void *writable_pointer)
     /* [previous][next][first][last][top][bottom][index][help] */
 288 {
 289   uintptr_t mem_w = (uintptr_t) writable_pointer & -(intptr_t)pagesize;
 290   return (void *) ((uintptr_t) writable_pointer + ((intptr_t *) mem_w)[0]);
 291 }
 292 
 293 void
 294 immfree (const void *readonly_pointer)
     /* [previous][next][first][last][top][bottom][index][help] */
 295 {
 296   uintptr_t mem_r = (uintptr_t) readonly_pointer & -(intptr_t)pagesize;
 297   free_block ((uintptr_t) readonly_pointer - ((intptr_t *) mem_r)[0]);
 298 }
 299 
 300 #else
 301 /* Dummy implementation.  */
 302 
 303 void *
 304 immmalloc (size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 305 {
 306   void *p = malloc (size);
 307   if (p == NULL)
 308     errno = ENOMEM;
 309   return p;
 310 }
 311 
 312 const void *
 313 immfreeze (void *writable_pointer)
     /* [previous][next][first][last][top][bottom][index][help] */
 314 {
 315   return writable_pointer;
 316 }
 317 
 318 void
 319 immfree (const void *readonly_pointer)
     /* [previous][next][first][last][top][bottom][index][help] */
 320 {
 321   void *writable_pointer = (void *) readonly_pointer;
 322   free (writable_pointer);
 323 }
 324 
 325 #endif
 326 
 327 
 328 const char *
 329 immstrdup (const char *string)
     /* [previous][next][first][last][top][bottom][index][help] */
 330 {
 331   size_t size = strlen (string) + 1;
 332   void *wp = immmalloc (size);
 333   if (wp == NULL)
 334     return NULL;
 335   memcpy (wp, string, size);
 336   return (const char *) immfreeze (wp);
 337 }

/* [previous][next][first][last][top][bottom][index][help] */