Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
alloc.cpp
Go to the documentation of this file.
1
27// expand _TEST_EXPORT macros
28#define _RWSTD_TEST_SRC
29
30#include <stdlib.h> // for atoi(), getenv(), malloc(), free()
31#include <string.h> // for memset()
32
33#include <rw_alloc.h>
34#include <rw_driver.h> // for rw_error(), rw_fatal()
35
36
37#ifdef __CYGWIN__
38// use the Windows API on Cygwin
39# define _WIN32
40#endif
41
42#ifndef _WIN32
43# ifdef __SUNPRO_CC
44// working around SunOS bug #568
45# include <time.h>
46# endif
47# include <unistd.h> // for getpagesize(), sysconf()
48# include <sys/mman.h> // for mmap()
49# include <sys/types.h>
50
51# ifndef _SC_PAGE_SIZE
52// fall back on the alternative macro if it exists,
53// or use getpagesize() otherwise
54# ifndef _SC_PAGESIZE
55# define GETPAGESIZE() getpagesize ()
56# else
57# define GETPAGESIZE() sysconf (_SC_PAGESIZE)
58# endif
59# else
60# define GETPAGESIZE() sysconf (_SC_PAGE_SIZE)
61# endif // _SC_PAGE_SIZE
62
63// POSIX mprotect() and munmap() take void* but some legacy systems
64// still declare the functions to take char* (aliased as caddr_t)
65typedef _RWSTD_MUNMAP_ARG1_T CaddrT;
66
67#else // ifdef _WIN32
68
69# include <windows.h> // for everything (ugh)
70# include <sys/types.h> // for off_t
71# include <errno.h> // for errno
72
73# define GETPAGESIZE() getpagesize ()
74
75typedef void* CaddrT;
76
77static long
78getpagesize ()
79{
80 static long pagesize_ = 0;
81
82 if (0 == pagesize_) {
83 SYSTEM_INFO info;
84 GetSystemInfo (&info);
85 pagesize_ = long (info.dwPageSize);
86 }
87
88 return pagesize_;
89}
90
91enum {
92 PROT_NONE = 0,
93 PROT_READ = 1 << 0,
94 PROT_WRITE = 1 << 1,
95 PROT_RDWR = PROT_READ | PROT_WRITE,
96 PROT_EXEC = 1 << 2
97};
98
99#define MAP_PRIVATE 0
100#define MAP_ANONYMOUS 0
101
102#define MAP_FAILED ((CaddrT)-1)
103
104static const DWORD
105_rw_prots [] = {
106 PAGE_NOACCESS,
107 PAGE_READONLY,
108 PAGE_READWRITE,
109 PAGE_READWRITE,
110 PAGE_EXECUTE,
111 PAGE_EXECUTE_READ,
112 PAGE_EXECUTE_READWRITE,
113 PAGE_EXECUTE_READWRITE
114};
115
116
117static inline DWORD
118_rw_translate_prot (int prot)
119{
120 if (0 <= prot && prot < sizeof _rw_prots / sizeof *_rw_prots)
121 return _rw_prots[prot];
122
123 return PAGE_NOACCESS;
124}
125
126
127static inline CaddrT
128mmap (CaddrT addr, size_t len, int prot, int, int, off_t)
129{
130 addr = VirtualAlloc (addr, len, MEM_RESERVE | MEM_COMMIT,
131 _rw_translate_prot (prot));
132
133 if (addr)
134 return addr;
135
136 errno = EINVAL;
137 return MAP_FAILED;
138}
139
140
141static inline int
142munmap (CaddrT addr, size_t)
143{
144 if (VirtualFree (addr, 0, MEM_RELEASE))
145 return 0;
146
147 errno = EINVAL;
148 return -1;
149}
150
151
152static inline int
153mprotect (CaddrT addr, size_t len, int prot)
154{
155 DWORD flOldProt;
156 if (VirtualProtect (addr, len, _rw_translate_prot (prot), &flOldProt))
157 return 0;
158
159 errno = EINVAL;
160 return -1;
161}
162
163#endif // _WIN32
164
165#ifndef MAP_PRIVATE
166# define MAP_PRIVATE 0
167#endif // MAP_PRIVATE
168
169#ifndef MAP_ANONYMOUS
170 // no MAP_ANONYMOUS on IRIX64
171# define MAP_ANONYMOUS 0
172#endif // MAP_ANONYMOUS
173
174
177{
178 void* addr_; // address of the allocated block
179 // if addr_ == 0 block is unused
180 size_t size_; // size of the allocated block
181 void* data_; // address of the user data
182 size_t udsz_; // size of the user data
183 int flags_; // memory protection flags
184};
185
186struct Stats
187{
188 size_t blocks_; // number of the current allocated blocks
189 size_t maxblocks_; // max number of the allocated blocks
190};
191
192
193static Stats
194_rw_stats;
195
196
197struct Blocks
198{
200 size_t nblocks_;
202};
203
204
205static Blocks* _rw_head = 0; // pointer to the first Blocks in list
206static Blocks* _rw_tail = 0; // pointer to the last Blocks in list
207
208struct Pair
209{
210 void* addr_; // pointer to the user data in block
211 BlockInfo* info_; // pointer to the corresponding BlockInfo
212};
213
214static Pair* _rw_table = 0; // pointer to the table
215static size_t _rw_table_size = 0; // size of table in bytes
216static size_t _rw_table_max_size = 0; // max number of items in table
217
218
220static Pair*
221_rw_lower_bound (Pair* first, Pair* last, void* addr)
222{
223 for (size_t dist = last - first; dist > 0; ) {
224
225 // half = dist / 2
226 const size_t half = dist >> 1;
227 Pair* const middle = first + half;
228
229 if (middle->addr_ < addr) {
230 first = middle + 1;
231 dist -= half + 1;
232 }
233 else
234 dist = half;
235 }
236
237 return first;
238}
239
240
241static inline Pair*
242_rw_binary_search (Pair* first, Pair* last, void* addr)
243{
244 Pair* it = _rw_lower_bound (first, last, addr);
245 return (it != last && it->addr_ == addr) ? it : 0;
246}
247
248
250// constructor sets r/w access to the specified memory pages
251// destructor sets r/o access to the specified memory pages
253{
254 CaddrT caddr_;
255 size_t size_;
256
257public:
258 MemRWGuard (void* addr, size_t size) {
259 static const size_t pagemask = GETPAGESIZE () - 1;
260
261 // check that pagesize is power of 2
262 RW_ASSERT (0 == ((pagemask + 1) & pagemask));
263
264 // caddr_ should be aligned to memory page boundary
265 const size_t off = size_t (addr) & pagemask;
266
267 addr = _RWSTD_STATIC_CAST (char*, addr) - off;
268
269 caddr_ = _RWSTD_STATIC_CAST (CaddrT, addr);
270 size_ = size + off;
271
272 const int res = mprotect (caddr_, size, PROT_READ | PROT_WRITE);
273 rw_error (0 == res, __FILE__, __LINE__,
274 "mprotect failed: errno = %{#m} (%{m})");
275 }
276
278 const int res = mprotect (caddr_, size_, PROT_READ);
279 rw_error (0 == res, __FILE__, __LINE__,
280 "mprotect failed: errno = %{#m} (%{m})");
281 }
282
283private:
284 // not defined
285 MemRWGuard (const MemRWGuard&);
286 MemRWGuard& operator= (const MemRWGuard&);
287};
288
289
291static void
292_rw_table_free ()
293{
294 if (!_rw_table)
295 return;
296
297 const CaddrT caddr = _RWSTD_REINTERPRET_CAST (CaddrT, _rw_table);
298
299 const int res = munmap (caddr, _rw_table_size);
300 rw_error (0 == res, __FILE__, __LINE__,
301 "munmap(%#p, %zu) failed: errno = %{#m} (%{m})",
302 _rw_table, _rw_table_size);
303
304 _rw_table = 0;
305 _rw_table_size = 0;
306 _rw_table_max_size = 0;
307}
308
309
310static bool
311_rw_table_grow ()
312{
313 // _rw_table_max_size cannot be less than allocated blocks
314 RW_ASSERT (_rw_table_max_size >= _rw_stats.blocks_);
315
316 // check for free space in current table
317 if (_rw_table_max_size == _rw_stats.blocks_) {
318 // realloc more memory
319 static const size_t pagesize = GETPAGESIZE ();
320
321 const size_t new_size = _rw_table_size + pagesize;
322
323 const CaddrT caddr = mmap (0, new_size,
324 PROT_READ | PROT_WRITE,
325 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
326
327 if (MAP_FAILED == caddr)
328 // no memory available
329 return false;
330
331 // copy old table
332 memcpy (caddr, _rw_table, _rw_stats.blocks_ * sizeof (Pair));
333
334 // protect the new table
335 const int res = mprotect (caddr, new_size, PROT_READ);
336 rw_error (0 == res, __FILE__, __LINE__,
337 "mprotect(%#p, %zu, PROT_READ) failed: errno = %{#m} (%{m})",
338 caddr, new_size);
339
340 // free old table
341 _rw_table_free ();
342
343 _rw_table = _RWSTD_REINTERPRET_CAST (Pair*, caddr);
344 _rw_table_size = new_size;
345 _rw_table_max_size = new_size / sizeof (Pair);
346 }
347
348 return true;
349}
350
351
352// inserts info about newly created BlockInfo
353// increments the number of the allocated blocks
354static void
355_rw_table_insert (BlockInfo& info)
356{
357 Pair* const begin = _rw_table;
358 Pair* const end = begin + _rw_stats.blocks_;
359 Pair* const it = _rw_lower_bound (begin, end, info.data_);
360
361 {
362 MemRWGuard guard (_rw_table, _rw_table_size);
363
364 // move items [it, end) to the end of table
365 memmove (it + 1, it, (end - it) * sizeof (Pair));
366
367 it->addr_ = info.data_;
368 it->info_ = &info;
369 }
370
371 ++_rw_stats.blocks_;
372 if (_rw_stats.blocks_ > _rw_stats.maxblocks_)
373 _rw_stats.maxblocks_ = _rw_stats.blocks_;
374}
375
376
377// removes the specified item from table
378// decrements the number of the allocated blocks
379static void
380_rw_table_remove (Pair* it)
381{
382 const size_t index = size_t (it - _rw_table);
383
384 rw_fatal (index < _rw_stats.blocks_, __FILE__, __LINE__,
385 "invalid index in _rw_table_remove: %zu", index);
386
387 MemRWGuard guard (_rw_table, _rw_table_size);
388 memmove (it, it + 1, (--_rw_stats.blocks_ - index) * sizeof (Pair));
389}
390
391
393// allocate more blocks (allocates one memory page)
394static bool
395_rw_allocate_blocks ()
396{
397 // count of the blocks per memory page
398 static size_t blocks_per_page = 0;
399
400 static const size_t pagesize = GETPAGESIZE ();
401
402 if (0 == blocks_per_page)
403 blocks_per_page = (pagesize - sizeof (Blocks)) / sizeof (BlockInfo) + 1;
404
405 const CaddrT caddr = mmap (0, pagesize,
406 PROT_READ | PROT_WRITE,
407 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
408
409 if (MAP_FAILED != caddr) {
410
411 memset (caddr, 0, pagesize);
412
413 Blocks* const blocks = _RWSTD_REINTERPRET_CAST (Blocks*, caddr);
414
415 blocks->nblocks_ = blocks_per_page;
416
417 // set r/o access to the new page
418 const int res = mprotect (caddr, pagesize, PROT_READ);
419 rw_error (0 == res, __FILE__, __LINE__,
420 "mprotect(%#p, %zu, PROT_READ) failed: errno = %{#m} (%{m})",
421 caddr, pagesize);
422
423 if (0 == _rw_head)
424 _rw_head = blocks;
425 else {
426 MemRWGuard guard (&_rw_tail->next_, sizeof (_rw_tail->next_));
427 _rw_tail->next_ = blocks;
428 }
429
430 _rw_tail = blocks;
431
432 return true;
433 }
434
435 return false;
436}
437
438
439// free allocated blocks
440// should be called when all user memory allocated
441// by rw_alloc were freed by rw_free
442static void
443_rw_free_blocks ()
444{
445 rw_fatal (0 == _rw_stats.blocks_, __FILE__, __LINE__,
446 "_rw_free_blocks called when %zu blocks are not freed",
447 _rw_stats.blocks_);
448
449 static const size_t pagesize = GETPAGESIZE ();
450
451 while (_rw_head) {
452 const CaddrT caddr = _RWSTD_REINTERPRET_CAST (CaddrT, _rw_head);
453
454 _rw_head = _rw_head->next_;
455
456 const int res = munmap (caddr, pagesize);
457
458 rw_error (0 == res, __FILE__, __LINE__,
459 "munmap(%#p, %zu) failed: errno = %{#m} (%{m})",
460 caddr, pagesize);
461 }
462
463 _rw_tail = 0;
464}
465
466
467static BlockInfo*
468_rw_find_unused_from (Blocks* it)
469{
470 while (it) {
471 for (size_t i = 0; i < it->nblocks_; ++i) {
472 BlockInfo & info = it->blocks_ [i];
473 if (0 == info.addr_)
474 return &info;
475 }
476
477 it = it->next_;
478 }
479
480 return 0;
481}
482
483
484// returns pointer to the first unused BlockInfo
485// if none unused items tries allocate more blocks
486// returns 0 if no memory
487static BlockInfo*
488_rw_find_unused ()
489{
490 BlockInfo * res = _rw_find_unused_from (_rw_head);
491
492 if (!res && _rw_allocate_blocks ()) {
493 // find the unused block from newly allocated blocks
494 // res = _rw_find_unused_from (_rw_tail);
495 res = _rw_tail->blocks_;
496 // res should be != 0
497 rw_fatal (0 != res, __FILE__, __LINE__,
498 "logic error in _rw_find_unused: res == 0 after "
499 "_rw_allocate_blocks() succeeded ");
500 }
501
502 return res;
503}
504
505
506// returns pointer to the Pair which corresponds to addr
507// returns 0 if addr is not valid pointer, returned by rw_alloc
508static Pair*
509_rw_find_by_addr (void* addr)
510{
511 Pair* const end = _rw_table + _rw_stats.blocks_;
512 return _rw_binary_search (_rw_table, end, addr);
513}
514
515
516static inline int
517_rw_get_prot (int flags)
518{
519 return (flags & RW_PROT_READ ? PROT_READ : 0)
520 | (flags & RW_PROT_WRITE ? PROT_WRITE : 0);
521}
522
523
524static inline int
525_rw_getenvflags ()
526{
527 const char* const envvar = getenv ("RWSTD_ALLOC_FLAGS");
528 if (envvar)
529 return atoi (envvar);
530
531 return 0;
532}
533
534
536_TEST_EXPORT void*
537rw_alloc (size_t nbytes, int flags /* = -1 */)
538{
539 static const int RWSTD_ALLOC_FLAGS = _rw_getenvflags ();
540
541 // redefine flags if environment variable was set
542 if (-1 == flags && 0 != RWSTD_ALLOC_FLAGS)
543 flags = RWSTD_ALLOC_FLAGS;
544
545 // make sure that table has free space
546 if (!_rw_table_grow ())
547 return 0;
548
549 if (BlockInfo *info = _rw_find_unused ()) {
550
551 BlockInfo newinfo = BlockInfo ();
552
553 newinfo.udsz_ = nbytes;
554 newinfo.flags_ = flags;
555
556 if (-1 == flags) {
557
558 newinfo.addr_ = malloc (nbytes);
559
560 if (newinfo.addr_) {
561 newinfo.size_ = nbytes;
562 newinfo.data_ = newinfo.addr_;
563 }
564 }
565 else {
566 static const size_t pagesize = GETPAGESIZE ();
567
568 size_t size = nbytes + pagesize;
569
570 // check that pagesize is power of 2
571 RW_ASSERT (0 == (pagesize & (pagesize - 1)));
572 size_t offset = size & (pagesize - 1);
573
574 if (offset) {
575 offset = pagesize - offset;
576 size += offset;
577 }
578
579 newinfo.addr_ = mmap (0, size, _rw_get_prot(flags),
580 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
581
582 if (MAP_FAILED != newinfo.addr_) {
583
584 char* data = _RWSTD_STATIC_CAST (char*, newinfo.addr_);
585 char* guard = data;
586
587 if (RW_PROT_BELOW & flags)
588 offset = pagesize;
589 else
590 guard += size - pagesize;
591
592 // deny access to the guard page
593 int res = mprotect (guard, pagesize, PROT_NONE);
594 rw_error (0 == res, __FILE__, __LINE__,
595 "mprotect failed: errno = %{#m} (%{m})");
596
597 newinfo.size_ = size;
598 newinfo.data_ = data + offset;
599 }
600 }
601
602 if (newinfo.data_) {
603
604 {
605 MemRWGuard guard (info, sizeof (*info));
606 *info = newinfo;
607 }
608
609 _rw_table_insert (*info);
610
611 return info->data_;
612 }
613 }
614
615 return 0;
616}
617
618
620_TEST_EXPORT void
622{
623 if (Pair *it = _rw_find_by_addr (addr)) {
624
625 BlockInfo &info = *it->info_;
626
627 if (-1 == info.flags_)
628 free (addr);
629 else {
630 const CaddrT caddr = _RWSTD_REINTERPRET_CAST (CaddrT, info.addr_);
631
632 const int res = munmap (caddr, info.size_);
633 rw_error (0 == res, __FILE__, __LINE__,
634 "munmap(%#p, %zu) failed: errno = %{#m} (%{m})",
635 caddr, info.size_);
636 }
637
638 {
639 MemRWGuard guard (&info, sizeof (info));
640 info = BlockInfo ();
641 }
642
643 _rw_table_remove (it);
644
645 if (0 == _rw_stats.blocks_) {
646 _rw_free_blocks ();
647 _rw_table_free ();
648 }
649 }
650 else
651 rw_error (0 == addr, __FILE__, __LINE__,
652 "rw_free(%#p): the address is not a valid address, "
653 "returned by rw_alloc()",
654 addr);
655}
#define i
Definition 17.names.cpp:126
#define middle
Definition 17.names.cpp:496
#define res
Definition 17.names.cpp:531
#define last
Definition 17.names.cpp:487
#define dist
Definition 17.names.cpp:464
#define index
Definition 17.names.cpp:482
#define off
Definition 17.names.cpp:507
#define offset
Definition 17.names.cpp:508
#define addr
Definition 17.names.cpp:446
#define len
Definition 17.names.cpp:488
const char *const data[]
#define flags
const int end
int atoi(const char *)
#define _RWSTD_REINTERPRET_CAST(T, x)
Definition _defs.h:535
#define _RWSTD_STATIC_CAST(T, x)
Definition _defs.h:534
#define MAP_PRIVATE
Definition alloc.cpp:166
_TEST_EXPORT void rw_free(void *addr)
Definition alloc.cpp:621
#define GETPAGESIZE()
Definition alloc.cpp:55
_TEST_EXPORT void * rw_alloc(size_t nbytes, int flags)
Definition alloc.cpp:537
#define MAP_ANONYMOUS
Definition alloc.cpp:171
_RWSTD_MUNMAP_ARG1_T CaddrT
Definition alloc.cpp:65
MemRWGuard(void *addr, size_t size)
Definition alloc.cpp:258
~MemRWGuard()
Definition alloc.cpp:277
#define MAP_FAILED
std::ostream & info() noexcept
replacement for std::cout for NeBuild logging.
Definition Config.h:69
@ RW_PROT_READ
Definition rw_alloc.h:35
@ RW_PROT_WRITE
Definition rw_alloc.h:36
@ RW_PROT_BELOW
Definition rw_alloc.h:38
_TEST_EXPORT int rw_fatal(int expr, const char *filename, int line, const char *fmtspec,...)
Definition driver.cpp:1596
_TEST_EXPORT int rw_error(int, const char *, int, const char *,...)
Definition driver.cpp:1613
#define _TEST_EXPORT
Definition rw_testdefs.h:55
#define RW_ASSERT(expr)
int flags_
Definition alloc.cpp:183
size_t udsz_
Definition alloc.cpp:182
void * addr_
Definition alloc.cpp:178
void * data_
Definition alloc.cpp:181
size_t size_
Definition alloc.cpp:180
Blocks * next_
Definition alloc.cpp:199
BlockInfo blocks_[1]
Definition alloc.cpp:201
size_t nblocks_
Definition alloc.cpp:200
void * addr_
Definition alloc.cpp:210
BlockInfo * info_
Definition alloc.cpp:211
size_t blocks_
Definition alloc.cpp:188
size_t maxblocks_
Definition alloc.cpp:189
#define EINVAL
Definition cmdopt.cpp:40