comparison druntime/src/gc/basic/gcx.d @ 759:d3eb054172f9

Added copy of druntime from DMD 2.020 modified for LDC.
author Tomas Lindquist Olsen <tomas.l.olsen@gmail.com>
date Tue, 11 Nov 2008 01:52:37 +0100
parents
children
comparison
equal deleted inserted replaced
758:f04dde6e882c 759:d3eb054172f9
1 /**
2 * This module contains the garbage collector implementation.
3 *
4 * Copyright: Copyright (C) 2001-2007 Digital Mars, www.digitalmars.com.
5 * All rights reserved.
6 * License:
7 * This software is provided 'as-is', without any express or implied
8 * warranty. In no event will the authors be held liable for any damages
9 * arising from the use of this software.
10 *
11 * Permission is granted to anyone to use this software for any purpose,
12 * including commercial applications, and to alter it and redistribute it
13 * freely, in both source and binary form, subject to the following
14 * restrictions:
15 *
16 * o The origin of this software must not be misrepresented; you must not
17 * claim that you wrote the original software. If you use this software
18 * in a product, an acknowledgment in the product documentation would be
19 * appreciated but is not required.
20 * o Altered source versions must be plainly marked as such, and must not
21 * be misrepresented as being the original software.
22 * o This notice may not be removed or altered from any source
23 * distribution.
24 * Authors: Walter Bright, David Friedman, Sean Kelly
25 */
26
27 module gc.gcx;
28
29 // D Programming Language Garbage Collector implementation
30
31 /************** Debugging ***************************/
32
33 //debug = PRINTF; // turn on printf's
34 //debug = COLLECT_PRINTF; // turn on printf's
35 //debug = THREADINVARIANT; // check thread integrity
36 //debug = LOGGING; // log allocations / frees
37 //debug = MEMSTOMP; // stomp on memory
38 //debug = SENTINEL; // add underrun/overrrun protection
39 //debug = PTRCHECK; // more pointer checking
40 //debug = PTRCHECK2; // thorough but slow pointer checking
41
42 /*************** Configuration *********************/
43
44 version = STACKGROWSDOWN; // growing the stack means subtracting from the stack pointer
45 // (use for Intel X86 CPUs)
46 // else growing the stack means adding to the stack pointer
47 version = MULTI_THREADED; // produce multithreaded version
48
49 /***************************************************/
50
51 private import gcbits;
52 private import gcstats;
53 private import gcalloc;
54
55 private import cstdlib = stdc.stdlib : calloc, free, malloc, realloc;
56 private import stdc.string;
57
58 debug private import stdc.stdio;
59
60 version (GNU)
61 {
62 // BUG: The following import will likely not work, since the gcc
63 // subdirectory is elsewhere. Instead, perhaps the functions
64 // could be declared directly or some other resolution could
65 // be found.
66 private import gcc.builtins; // for __builtin_unwind_init
67 }
68
69 struct BlkInfo
70 {
71 void* base;
72 size_t size;
73 uint attr;
74 }
75
76 private
77 {
78 enum BlkAttr : uint
79 {
80 FINALIZE = 0b0000_0001,
81 NO_SCAN = 0b0000_0010,
82 NO_MOVE = 0b0000_0100,
83 ALL_BITS = 0b1111_1111
84 }
85
86 extern (C) void* rt_stackBottom();
87 extern (C) void* rt_stackTop();
88 extern (C) void* rt_staticDataBottom();
89 extern (C) void* rt_staticDataTop();
90
91 extern (C) void rt_finalize( void* p, bool det = true );
92
93 alias void delegate( void*, void* ) scanFn;
94
95 extern (C) void rt_scanStaticData( scanFn scan );
96
97 version (MULTI_THREADED)
98 {
99 extern (C) bool thread_needLock();
100 extern (C) void thread_suspendAll();
101 extern (C) void thread_resumeAll();
102
103 extern (C) void thread_scanAll( scanFn fn, void* curStackTop = null );
104 }
105
106 extern (C) void onOutOfMemoryError();
107
108 enum
109 {
110 OPFAIL = ~cast(size_t)0
111 }
112 }
113
114
115 alias GC gc_t;
116
117
118 /* ======================= Leak Detector =========================== */
119
120
121 debug (LOGGING)
122 {
123 struct Log
124 {
125 void* p;
126 size_t size;
127 size_t line;
128 char* file;
129 void* parent;
130
131 void print()
132 {
133 printf(" p = %x, size = %d, parent = %x ", p, size, parent);
134 if (file)
135 {
136 printf("%s(%u)", file, line);
137 }
138 printf("\n");
139 }
140 }
141
142
143 struct LogArray
144 {
145 size_t dim;
146 size_t allocdim;
147 Log *data;
148
149 void Dtor()
150 {
151 if (data)
152 cstdlib.free(data);
153 data = null;
154 }
155
156 void reserve(size_t nentries)
157 {
158 assert(dim <= allocdim);
159 if (allocdim - dim < nentries)
160 {
161 allocdim = (dim + nentries) * 2;
162 assert(dim + nentries <= allocdim);
163 if (!data)
164 {
165 data = cast(Log*)cstdlib.malloc(allocdim * Log.sizeof);
166 if (!data && allocdim)
167 onOutOfMemoryError();
168 }
169 else
170 { Log *newdata;
171
172 newdata = cast(Log*)cstdlib.malloc(allocdim * Log.sizeof);
173 if (!newdata && allocdim)
174 onOutOfMemoryError();
175 memcpy(newdata, data, dim * Log.sizeof);
176 cstdlib.free(data);
177 data = newdata;
178 }
179 }
180 }
181
182
183 void push(Log log)
184 {
185 reserve(1);
186 data[dim++] = log;
187 }
188
189 void remove(size_t i)
190 {
191 memmove(data + i, data + i + 1, (dim - i) * Log.sizeof);
192 dim--;
193 }
194
195
196 size_t find(void *p)
197 {
198 for (size_t i = 0; i < dim; i++)
199 {
200 if (data[i].p == p)
201 return i;
202 }
203 return OPFAIL; // not found
204 }
205
206
207 void copy(LogArray *from)
208 {
209 reserve(from.dim - dim);
210 assert(from.dim <= allocdim);
211 memcpy(data, from.data, from.dim * Log.sizeof);
212 dim = from.dim;
213 }
214 }
215 }
216
217
218 /* ============================ GC =============================== */
219
220
221 class GCLock { } // just a dummy so we can get a global lock
222
223
224 const uint GCVERSION = 1; // increment every time we change interface
225 // to GC.
226
227 class GC
228 {
229 // For passing to debug code
230 static size_t line;
231 static char* file;
232
233 uint gcversion = GCVERSION;
234
235 Gcx *gcx; // implementation
236 static ClassInfo gcLock; // global lock
237
238
239 void initialize()
240 {
241 gcLock = GCLock.classinfo;
242 gcx = cast(Gcx*)cstdlib.calloc(1, Gcx.sizeof);
243 if (!gcx)
244 onOutOfMemoryError();
245 gcx.initialize();
246 setStackBottom(rt_stackBottom());
247 }
248
249
250 void Dtor()
251 {
252 version (linux)
253 {
254 //debug(PRINTF) printf("Thread %x ", pthread_self());
255 //debug(PRINTF) printf("GC.Dtor()\n");
256 }
257
258 if (gcx)
259 {
260 gcx.Dtor();
261 cstdlib.free(gcx);
262 gcx = null;
263 }
264 }
265
266
267 invariant()
268 {
269 if (gcx)
270 {
271 gcx.thread_Invariant();
272 }
273 }
274
275
276 /**
277 *
278 */
279 void enable()
280 {
281 if (!thread_needLock())
282 {
283 assert(gcx.disabled > 0);
284 gcx.disabled--;
285 }
286 else synchronized (gcLock)
287 {
288 assert(gcx.disabled > 0);
289 gcx.disabled--;
290 }
291 }
292
293
294 /**
295 *
296 */
297 void disable()
298 {
299 if (!thread_needLock())
300 {
301 gcx.disabled++;
302 }
303 else synchronized (gcLock)
304 {
305 gcx.disabled++;
306 }
307 }
308
309
310 /**
311 *
312 */
313 uint getAttr(void* p)
314 {
315 if (!p)
316 {
317 return 0;
318 }
319
320 uint go()
321 {
322 Pool* pool = gcx.findPool(p);
323 uint oldb = 0;
324
325 if (pool)
326 {
327 auto biti = cast(size_t)(p - pool.baseAddr) / 16;
328
329 oldb = gcx.getBits(pool, biti);
330 }
331 return oldb;
332 }
333
334 if (!thread_needLock())
335 {
336 return go();
337 }
338 else synchronized (gcLock)
339 {
340 return go();
341 }
342 }
343
344
345 /**
346 *
347 */
348 uint setAttr(void* p, uint mask)
349 {
350 if (!p)
351 {
352 return 0;
353 }
354
355 uint go()
356 {
357 Pool* pool = gcx.findPool(p);
358 uint oldb = 0;
359
360 if (pool)
361 {
362 auto biti = cast(size_t)(p - pool.baseAddr) / 16;
363
364 oldb = gcx.getBits(pool, biti);
365 gcx.setBits(pool, biti, mask);
366 }
367 return oldb;
368 }
369
370 if (!thread_needLock())
371 {
372 return go();
373 }
374 else synchronized (gcLock)
375 {
376 return go();
377 }
378 }
379
380
381 /**
382 *
383 */
384 uint clrAttr(void* p, uint mask)
385 {
386 if (!p)
387 {
388 return 0;
389 }
390
391 uint go()
392 {
393 Pool* pool = gcx.findPool(p);
394 uint oldb = 0;
395
396 if (pool)
397 {
398 auto biti = cast(size_t)(p - pool.baseAddr) / 16;
399
400 oldb = gcx.getBits(pool, biti);
401 gcx.clrBits(pool, biti, mask);
402 }
403 return oldb;
404 }
405
406 if (!thread_needLock())
407 {
408 return go();
409 }
410 else synchronized (gcLock)
411 {
412 return go();
413 }
414 }
415
416
417 /**
418 *
419 */
420 void *malloc(size_t size, uint bits = 0)
421 {
422 if (!size)
423 {
424 return null;
425 }
426
427 if (!thread_needLock())
428 {
429 return mallocNoSync(size, bits);
430 }
431 else synchronized (gcLock)
432 {
433 return mallocNoSync(size, bits);
434 }
435 }
436
437
438 //
439 //
440 //
441 private void *mallocNoSync(size_t size, uint bits = 0)
442 {
443 assert(size != 0);
444
445 void *p = null;
446 Bins bin;
447
448 //debug(PRINTF) printf("GC::malloc(size = %d, gcx = %p)\n", size, gcx);
449 assert(gcx);
450 //debug(PRINTF) printf("gcx.self = %x, pthread_self() = %x\n", gcx.self, pthread_self());
451
452 size += SENTINEL_EXTRA;
453
454 // Compute size bin
455 // Cache previous binsize lookup - Dave Fladebo.
456 static size_t lastsize = -1;
457 static Bins lastbin;
458 if (size == lastsize)
459 bin = lastbin;
460 else
461 {
462 bin = gcx.findBin(size);
463 lastsize = size;
464 lastbin = bin;
465 }
466
467 if (bin < B_PAGE)
468 {
469 p = gcx.bucket[bin];
470 if (p is null)
471 {
472 if (!gcx.allocPage(bin) && !gcx.disabled) // try to find a new page
473 {
474 if (!thread_needLock())
475 {
476 /* Then we haven't locked it yet. Be sure
477 * and lock for a collection, since a finalizer
478 * may start a new thread.
479 */
480 synchronized (gcLock)
481 {
482 gcx.fullcollectshell();
483 }
484 }
485 else if (!gcx.fullcollectshell()) // collect to find a new page
486 {
487 //gcx.newPool(1);
488 }
489 }
490 if (!gcx.bucket[bin] && !gcx.allocPage(bin))
491 { int result;
492
493 gcx.newPool(1); // allocate new pool to find a new page
494 result = gcx.allocPage(bin);
495 if (!result)
496 onOutOfMemoryError();
497 }
498 p = gcx.bucket[bin];
499 }
500
501 // Return next item from free list
502 gcx.bucket[bin] = (cast(List*)p).next;
503 if( !(bits & BlkAttr.NO_SCAN) )
504 memset(p + size, 0, binsize[bin] - size);
505 //debug(PRINTF) printf("\tmalloc => %x\n", p);
506 debug (MEMSTOMP) memset(p, 0xF0, size);
507 }
508 else
509 {
510 p = gcx.bigAlloc(size);
511 if (!p)
512 onOutOfMemoryError();
513 }
514 size -= SENTINEL_EXTRA;
515 p = sentinel_add(p);
516 sentinel_init(p, size);
517 gcx.log_malloc(p, size);
518
519 if (bits)
520 {
521 Pool *pool = gcx.findPool(p);
522 assert(pool);
523
524 gcx.setBits(pool, cast(size_t)(p - pool.baseAddr) / 16, bits);
525 }
526 return p;
527 }
528
529
530 /**
531 *
532 */
533 void *calloc(size_t size, uint bits = 0)
534 {
535 if (!size)
536 {
537 return null;
538 }
539
540 if (!thread_needLock())
541 {
542 return callocNoSync(size, bits);
543 }
544 else synchronized (gcLock)
545 {
546 return callocNoSync(size, bits);
547 }
548 }
549
550
551 //
552 //
553 //
554 private void *callocNoSync(size_t size, uint bits = 0)
555 {
556 assert(size != 0);
557
558 //debug(PRINTF) printf("calloc: %x len %d\n", p, len);
559 void *p = mallocNoSync(size, bits);
560 memset(p, 0, size);
561 return p;
562 }
563
564
565 /**
566 *
567 */
568 void *realloc(void *p, size_t size, uint bits = 0)
569 {
570 if (!thread_needLock())
571 {
572 return reallocNoSync(p, size, bits);
573 }
574 else synchronized (gcLock)
575 {
576 return reallocNoSync(p, size, bits);
577 }
578 }
579
580
581 //
582 //
583 //
584 private void *reallocNoSync(void *p, size_t size, uint bits = 0)
585 {
586 if (!size)
587 { if (p)
588 { freeNoSync(p);
589 p = null;
590 }
591 }
592 else if (!p)
593 {
594 p = mallocNoSync(size, bits);
595 }
596 else
597 { void *p2;
598 size_t psize;
599
600 //debug(PRINTF) printf("GC::realloc(p = %x, size = %u)\n", p, size);
601 version (SENTINEL)
602 {
603 sentinel_Invariant(p);
604 psize = *sentinel_size(p);
605 if (psize != size)
606 {
607 if (psize)
608 {
609 Pool *pool = gcx.findPool(p);
610
611 if (pool)
612 {
613 auto biti = cast(size_t)(p - pool.baseAddr) / 16;
614
615 if (bits)
616 {
617 gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
618 gcx.setBits(pool, biti, bits);
619 }
620 else
621 {
622 bits = gcx.getBits(pool, biti);
623 }
624 }
625 }
626 p2 = mallocNoSync(size, bits);
627 if (psize < size)
628 size = psize;
629 //debug(PRINTF) printf("\tcopying %d bytes\n",size);
630 memcpy(p2, p, size);
631 p = p2;
632 }
633 }
634 else
635 {
636 psize = gcx.findSize(p); // find allocated size
637 if (psize >= PAGESIZE && size >= PAGESIZE)
638 {
639 auto psz = psize / PAGESIZE;
640 auto newsz = (size + PAGESIZE - 1) / PAGESIZE;
641 if (newsz == psz)
642 return p;
643
644 auto pool = gcx.findPool(p);
645 auto pagenum = (p - pool.baseAddr) / PAGESIZE;
646
647 if (newsz < psz)
648 { // Shrink in place
649 synchronized (gcLock)
650 {
651 debug (MEMSTOMP) memset(p + size, 0xF2, psize - size);
652 pool.freePages(pagenum + newsz, psz - newsz);
653 }
654 return p;
655 }
656 else if (pagenum + newsz <= pool.npages)
657 {
658 // Attempt to expand in place
659 synchronized (gcLock)
660 {
661 for (size_t i = pagenum + psz; 1;)
662 {
663 if (i == pagenum + newsz)
664 {
665 debug (MEMSTOMP) memset(p + psize, 0xF0, size - psize);
666 memset(&pool.pagetable[pagenum + psz], B_PAGEPLUS, newsz - psz);
667 return p;
668 }
669 if (i == pool.ncommitted)
670 {
671 auto u = pool.extendPages(pagenum + newsz - pool.ncommitted);
672 if (u == OPFAIL)
673 break;
674 i = pagenum + newsz;
675 continue;
676 }
677 if (pool.pagetable[i] != B_FREE)
678 break;
679 i++;
680 }
681 }
682 }
683 }
684 if (psize < size || // if new size is bigger
685 psize > size * 2) // or less than half
686 {
687 if (psize)
688 {
689 Pool *pool = gcx.findPool(p);
690
691 if (pool)
692 {
693 auto biti = cast(size_t)(p - pool.baseAddr) / 16;
694
695 if (bits)
696 {
697 gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
698 gcx.setBits(pool, biti, bits);
699 }
700 else
701 {
702 bits = gcx.getBits(pool, biti);
703 }
704 }
705 }
706 p2 = mallocNoSync(size, bits);
707 if (psize < size)
708 size = psize;
709 //debug(PRINTF) printf("\tcopying %d bytes\n",size);
710 memcpy(p2, p, size);
711 p = p2;
712 }
713 }
714 }
715 return p;
716 }
717
718
719 /**
720 * Attempt to in-place enlarge the memory block pointed to by p by at least
721 * minbytes beyond its current capacity, up to a maximum of maxsize. This
722 * does not attempt to move the memory block (like realloc() does).
723 *
724 * Returns:
725 * 0 if could not extend p,
726 * total size of entire memory block if successful.
727 */
728 size_t extend(void* p, size_t minsize, size_t maxsize)
729 {
730 if (!thread_needLock())
731 {
732 return extendNoSync(p, minsize, maxsize);
733 }
734 else synchronized (gcLock)
735 {
736 return extendNoSync(p, minsize, maxsize);
737 }
738 }
739
740
741 //
742 //
743 //
744 private size_t extendNoSync(void* p, size_t minsize, size_t maxsize)
745 in
746 {
747 assert( minsize <= maxsize );
748 }
749 body
750 {
751 //debug(PRINTF) printf("GC::extend(p = %x, minsize = %u, maxsize = %u)\n", p, minsize, maxsize);
752 version (SENTINEL)
753 {
754 return 0;
755 }
756 auto psize = gcx.findSize(p); // find allocated size
757 if (psize < PAGESIZE)
758 return 0; // cannot extend buckets
759
760 auto psz = psize / PAGESIZE;
761 auto minsz = (minsize + PAGESIZE - 1) / PAGESIZE;
762 auto maxsz = (maxsize + PAGESIZE - 1) / PAGESIZE;
763
764 auto pool = gcx.findPool(p);
765 auto pagenum = (p - pool.baseAddr) / PAGESIZE;
766
767 size_t sz;
768 for (sz = 0; sz < maxsz; sz++)
769 {
770 auto i = pagenum + psz + sz;
771 if (i == pool.ncommitted)
772 break;
773 if (pool.pagetable[i] != B_FREE)
774 { if (sz < minsz)
775 return 0;
776 break;
777 }
778 }
779 if (sz >= minsz)
780 {
781 }
782 else if (pagenum + psz + sz == pool.ncommitted)
783 {
784 auto u = pool.extendPages(minsz - sz);
785 if (u == OPFAIL)
786 return 0;
787 sz = minsz;
788 }
789 else
790 return 0;
791 debug (MEMSTOMP) memset(p + psize, 0xF0, (psz + sz) * PAGESIZE - psize);
792 memset(pool.pagetable + pagenum + psz, B_PAGEPLUS, sz);
793 gcx.p_cache = null;
794 gcx.size_cache = 0;
795 return (psz + sz) * PAGESIZE;
796 }
797
798
799 /**
800 *
801 */
802 size_t reserve(size_t size)
803 {
804 if (!size)
805 {
806 return 0;
807 }
808
809 if (!thread_needLock())
810 {
811 return reserveNoSync(size);
812 }
813 else synchronized (gcLock)
814 {
815 return reserveNoSync(size);
816 }
817 }
818
819
820 //
821 //
822 //
823 private size_t reserveNoSync(size_t size)
824 {
825 assert(size != 0);
826 assert(gcx);
827
828 return gcx.reserve(size);
829 }
830
831
832 /**
833 *
834 */
835 void free(void *p)
836 {
837 if (!p)
838 {
839 return;
840 }
841
842 if (!thread_needLock())
843 {
844 return freeNoSync(p);
845 }
846 else synchronized (gcLock)
847 {
848 return freeNoSync(p);
849 }
850 }
851
852
853 //
854 //
855 //
856 private void freeNoSync(void *p)
857 {
858 assert (p);
859
860 Pool* pool;
861 size_t pagenum;
862 Bins bin;
863 size_t biti;
864
865 // Find which page it is in
866 pool = gcx.findPool(p);
867 if (!pool) // if not one of ours
868 return; // ignore
869 sentinel_Invariant(p);
870 p = sentinel_sub(p);
871 pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE;
872 biti = cast(size_t)(p - pool.baseAddr) / 16;
873 gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
874
875 bin = cast(Bins)pool.pagetable[pagenum];
876 if (bin == B_PAGE) // if large alloc
877 { size_t npages;
878 size_t n;
879
880 // Free pages
881 npages = 1;
882 n = pagenum;
883 while (++n < pool.ncommitted && pool.pagetable[n] == B_PAGEPLUS)
884 npages++;
885 debug (MEMSTOMP) memset(p, 0xF2, npages * PAGESIZE);
886 pool.freePages(pagenum, npages);
887 }
888 else
889 { // Add to free list
890 List *list = cast(List*)p;
891
892 debug (MEMSTOMP) memset(p, 0xF2, binsize[bin]);
893
894 list.next = gcx.bucket[bin];
895 gcx.bucket[bin] = list;
896 }
897 gcx.log_free(sentinel_add(p));
898 }
899
900
901 /**
902 * Determine the base address of the block containing p. If p is not a gc
903 * allocated pointer, return null.
904 */
905 void* addrOf(void *p)
906 {
907 if (!p)
908 {
909 return null;
910 }
911
912 if (!thread_needLock())
913 {
914 return addrOfNoSync(p);
915 }
916 else synchronized (gcLock)
917 {
918 return addrOfNoSync(p);
919 }
920 }
921
922
923 //
924 //
925 //
926 void* addrOfNoSync(void *p)
927 {
928 if (!p)
929 {
930 return null;
931 }
932
933 return gcx.findBase(p);
934 }
935
936
937 /**
938 * Determine the allocated size of pointer p. If p is an interior pointer
939 * or not a gc allocated pointer, return 0.
940 */
941 size_t sizeOf(void *p)
942 {
943 if (!p)
944 {
945 return 0;
946 }
947
948 if (!thread_needLock())
949 {
950 return sizeOfNoSync(p);
951 }
952 else synchronized (gcLock)
953 {
954 return sizeOfNoSync(p);
955 }
956 }
957
958
959 //
960 //
961 //
962 private size_t sizeOfNoSync(void *p)
963 {
964 assert (p);
965
966 version (SENTINEL)
967 {
968 p = sentinel_sub(p);
969 size_t size = gcx.findSize(p);
970
971 // Check for interior pointer
972 // This depends on:
973 // 1) size is a power of 2 for less than PAGESIZE values
974 // 2) base of memory pool is aligned on PAGESIZE boundary
975 if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
976 size = 0;
977 return size ? size - SENTINEL_EXTRA : 0;
978 }
979 else
980 {
981 if (p == gcx.p_cache)
982 return gcx.size_cache;
983
984 size_t size = gcx.findSize(p);
985
986 // Check for interior pointer
987 // This depends on:
988 // 1) size is a power of 2 for less than PAGESIZE values
989 // 2) base of memory pool is aligned on PAGESIZE boundary
990 if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
991 size = 0;
992 else
993 {
994 gcx.p_cache = p;
995 gcx.size_cache = size;
996 }
997
998 return size;
999 }
1000 }
1001
1002
1003 /**
1004 * Determine the base address of the block containing p. If p is not a gc
1005 * allocated pointer, return null.
1006 */
1007 BlkInfo query(void *p)
1008 {
1009 if (!p)
1010 {
1011 BlkInfo i;
1012 return i;
1013 }
1014
1015 if (!thread_needLock())
1016 {
1017 return queryNoSync(p);
1018 }
1019 else synchronized (gcLock)
1020 {
1021 return queryNoSync(p);
1022 }
1023 }
1024
1025
1026 //
1027 //
1028 //
1029 BlkInfo queryNoSync(void *p)
1030 {
1031 assert(p);
1032
1033 return gcx.getInfo(p);
1034 }
1035
1036
1037 /**
1038 * Verify that pointer p:
1039 * 1) belongs to this memory pool
1040 * 2) points to the start of an allocated piece of memory
1041 * 3) is not on a free list
1042 */
1043 void check(void *p)
1044 {
1045 if (!p)
1046 {
1047 return;
1048 }
1049
1050 if (!thread_needLock())
1051 {
1052 checkNoSync(p);
1053 }
1054 else synchronized (gcLock)
1055 {
1056 checkNoSync(p);
1057 }
1058 }
1059
1060
1061 //
1062 //
1063 //
1064 private void checkNoSync(void *p)
1065 {
1066 assert(p);
1067
1068 sentinel_Invariant(p);
1069 debug (PTRCHECK)
1070 {
1071 Pool* pool;
1072 size_t pagenum;
1073 Bins bin;
1074 size_t size;
1075
1076 p = sentinel_sub(p);
1077 pool = gcx.findPool(p);
1078 assert(pool);
1079 pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE;
1080 bin = cast(Bins)pool.pagetable[pagenum];
1081 assert(bin <= B_PAGE);
1082 size = binsize[bin];
1083 assert((cast(size_t)p & (size - 1)) == 0);
1084
1085 debug (PTRCHECK2)
1086 {
1087 if (bin < B_PAGE)
1088 {
1089 // Check that p is not on a free list
1090 List *list;
1091
1092 for (list = gcx.bucket[bin]; list; list = list.next)
1093 {
1094 assert(cast(void*)list != p);
1095 }
1096 }
1097 }
1098 }
1099 }
1100
1101
1102 //
1103 //
1104 //
1105 private void setStackBottom(void *p)
1106 {
1107 version (STACKGROWSDOWN)
1108 {
1109 //p = (void *)((uint *)p + 4);
1110 if (p > gcx.stackBottom)
1111 {
1112 //debug(PRINTF) printf("setStackBottom(%x)\n", p);
1113 gcx.stackBottom = p;
1114 }
1115 }
1116 else
1117 {
1118 //p = (void *)((uint *)p - 4);
1119 if (p < gcx.stackBottom)
1120 {
1121 //debug(PRINTF) printf("setStackBottom(%x)\n", p);
1122 gcx.stackBottom = cast(char*)p;
1123 }
1124 }
1125 }
1126
1127
1128 static void scanStaticData(gc_t g)
1129 {
1130 //debug(PRINTF) printf("+GC.scanStaticData()\n");
1131 auto pbot = rt_staticDataBottom();
1132 auto ptop = rt_staticDataTop();
1133 g.addRange(pbot, ptop - pbot);
1134 //debug(PRINTF) printf("-GC.scanStaticData()\n");
1135 }
1136
1137 static void unscanStaticData(gc_t g)
1138 {
1139 auto pbot = rt_staticDataBottom();
1140 g.removeRange(pbot);
1141 }
1142
1143 /**
1144 * add p to list of roots
1145 */
1146 void addRoot(void *p)
1147 {
1148 if (!p)
1149 {
1150 return;
1151 }
1152
1153 if (!thread_needLock())
1154 {
1155 gcx.addRoot(p);
1156 }
1157 else synchronized (gcLock)
1158 {
1159 gcx.addRoot(p);
1160 }
1161 }
1162
1163
1164 /**
1165 * remove p from list of roots
1166 */
1167 void removeRoot(void *p)
1168 {
1169 if (!p)
1170 {
1171 return;
1172 }
1173
1174 if (!thread_needLock())
1175 {
1176 gcx.removeRoot(p);
1177 }
1178 else synchronized (gcLock)
1179 {
1180 gcx.removeRoot(p);
1181 }
1182 }
1183
1184
1185 /**
1186 * add range to scan for roots
1187 */
1188 void addRange(void *p, size_t sz)
1189 {
1190 if (!p || !sz)
1191 {
1192 return;
1193 }
1194
1195 //debug(PRINTF) printf("+GC.addRange(pbot = x%x, ptop = x%x)\n", pbot, ptop);
1196 if (!thread_needLock())
1197 {
1198 gcx.addRange(p, p + sz);
1199 }
1200 else synchronized (gcLock)
1201 {
1202 gcx.addRange(p, p + sz);
1203 }
1204 //debug(PRINTF) printf("-GC.addRange()\n");
1205 }
1206
1207
1208 /**
1209 * remove range
1210 */
1211 void removeRange(void *p)
1212 {
1213 if (!p)
1214 {
1215 return;
1216 }
1217
1218 if (!thread_needLock())
1219 {
1220 gcx.removeRange(p);
1221 }
1222 else synchronized (gcLock)
1223 {
1224 gcx.removeRange(p);
1225 }
1226 }
1227
1228
1229 /**
1230 * do full garbage collection
1231 */
1232 void fullCollect()
1233 {
1234 debug(PRINTF) printf("GC.fullCollect()\n");
1235
1236 if (!thread_needLock())
1237 {
1238 gcx.fullcollectshell();
1239 }
1240 else synchronized (gcLock)
1241 {
1242 gcx.fullcollectshell();
1243 }
1244
1245 version (none)
1246 {
1247 GCStats stats;
1248
1249 getStats(stats);
1250 debug(PRINTF) printf("poolsize = %x, usedsize = %x, freelistsize = %x\n",
1251 stats.poolsize, stats.usedsize, stats.freelistsize);
1252 }
1253
1254 gcx.log_collect();
1255 }
1256
1257
1258 /**
1259 * do full garbage collection ignoring roots
1260 */
1261 void fullCollectNoStack()
1262 {
1263 if (!thread_needLock())
1264 {
1265 gcx.noStack++;
1266 gcx.fullcollectshell();
1267 gcx.noStack--;
1268 }
1269 else synchronized (gcLock)
1270 {
1271 gcx.noStack++;
1272 gcx.fullcollectshell();
1273 gcx.noStack--;
1274 }
1275 }
1276
1277
1278 /**
1279 * minimize free space usage
1280 */
1281 void minimize()
1282 {
1283 if (!thread_needLock())
1284 {
1285 gcx.minimize();
1286 }
1287 else synchronized (gcLock)
1288 {
1289 gcx.minimize();
1290 }
1291 }
1292
1293
1294 /**
1295 * Retrieve statistics about garbage collection.
1296 * Useful for debugging and tuning.
1297 */
1298 void getStats(out GCStats stats)
1299 {
1300 if (!thread_needLock())
1301 {
1302 getStatsNoSync(stats);
1303 }
1304 else synchronized (gcLock)
1305 {
1306 getStatsNoSync(stats);
1307 }
1308 }
1309
1310
1311 //
1312 //
1313 //
1314 private void getStatsNoSync(out GCStats stats)
1315 {
1316 size_t psize = 0;
1317 size_t usize = 0;
1318 size_t flsize = 0;
1319
1320 size_t n;
1321 size_t bsize = 0;
1322
1323 //debug(PRINTF) printf("getStats()\n");
1324 memset(&stats, 0, GCStats.sizeof);
1325
1326 for (n = 0; n < gcx.npools; n++)
1327 { Pool *pool = gcx.pooltable[n];
1328
1329 psize += pool.ncommitted * PAGESIZE;
1330 for (size_t j = 0; j < pool.ncommitted; j++)
1331 {
1332 Bins bin = cast(Bins)pool.pagetable[j];
1333 if (bin == B_FREE)
1334 stats.freeblocks++;
1335 else if (bin == B_PAGE)
1336 stats.pageblocks++;
1337 else if (bin < B_PAGE)
1338 bsize += PAGESIZE;
1339 }
1340 }
1341
1342 for (n = 0; n < B_PAGE; n++)
1343 {
1344 //debug(PRINTF) printf("bin %d\n", n);
1345 for (List *list = gcx.bucket[n]; list; list = list.next)
1346 {
1347 //debug(PRINTF) printf("\tlist %x\n", list);
1348 flsize += binsize[n];
1349 }
1350 }
1351
1352 usize = bsize - flsize;
1353
1354 stats.poolsize = psize;
1355 stats.usedsize = bsize - flsize;
1356 stats.freelistsize = flsize;
1357 }
1358 }
1359
1360
1361 /* ============================ Gcx =============================== */
1362
1363 enum
1364 { PAGESIZE = 4096,
1365 COMMITSIZE = (4096*16),
1366 POOLSIZE = (4096*256),
1367 }
1368
1369
1370 enum
1371 {
1372 B_16,
1373 B_32,
1374 B_64,
1375 B_128,
1376 B_256,
1377 B_512,
1378 B_1024,
1379 B_2048,
1380 B_PAGE, // start of large alloc
1381 B_PAGEPLUS, // continuation of large alloc
1382 B_FREE, // free page
1383 B_UNCOMMITTED, // memory not committed for this page
1384 B_MAX
1385 }
1386
1387
1388 alias ubyte Bins;
1389
1390
1391 struct List
1392 {
1393 List *next;
1394 }
1395
1396
1397 struct Range
1398 {
1399 void *pbot;
1400 void *ptop;
1401 }
1402
1403
1404 const uint binsize[B_MAX] = [ 16,32,64,128,256,512,1024,2048,4096 ];
1405 const uint notbinsize[B_MAX] = [ ~(16u-1),~(32u-1),~(64u-1),~(128u-1),~(256u-1),
1406 ~(512u-1),~(1024u-1),~(2048u-1),~(4096u-1) ];
1407
1408 /* ============================ Gcx =============================== */
1409
1410
1411 struct Gcx
1412 {
1413 debug (THREADINVARIANT)
1414 {
1415 pthread_t self;
1416 void thread_Invariant()
1417 {
1418 if (self != pthread_self())
1419 printf("thread_Invariant(): gcx = %x, self = %x, pthread_self() = %x\n", this, self, pthread_self());
1420 assert(self == pthread_self());
1421 }
1422 }
1423 else
1424 {
1425 void thread_Invariant() { }
1426 }
1427
1428 void *p_cache;
1429 size_t size_cache;
1430
1431 size_t nroots;
1432 size_t rootdim;
1433 void **roots;
1434
1435 size_t nranges;
1436 size_t rangedim;
1437 Range *ranges;
1438
1439 uint noStack; // !=0 means don't scan stack
1440 uint log; // turn on logging
1441 uint anychanges;
1442 void *stackBottom;
1443 uint inited;
1444 int disabled; // turn off collections if >0
1445
1446 byte *minAddr; // min(baseAddr)
1447 byte *maxAddr; // max(topAddr)
1448
1449 size_t npools;
1450 Pool **pooltable;
1451
1452 List *bucket[B_MAX]; // free list for each size
1453
1454
1455 void initialize()
1456 { int dummy;
1457
1458 (cast(byte*)this)[0 .. Gcx.sizeof] = 0;
1459 stackBottom = cast(char*)&dummy;
1460 log_init();
1461 debug (THREADINVARIANT)
1462 self = pthread_self();
1463 //printf("gcx = %p, self = %x\n", this, self);
1464 inited = 1;
1465 }
1466
1467
1468 void Dtor()
1469 {
1470 inited = 0;
1471
1472 for (size_t i = 0; i < npools; i++)
1473 { Pool *pool = pooltable[i];
1474
1475 pool.Dtor();
1476 cstdlib.free(pool);
1477 }
1478 if (pooltable)
1479 cstdlib.free(pooltable);
1480
1481 if (roots)
1482 cstdlib.free(roots);
1483
1484 if (ranges)
1485 cstdlib.free(ranges);
1486 }
1487
1488
1489 void Invariant() { }
1490
1491
1492 invariant()
1493 {
1494 if (inited)
1495 {
1496 //printf("Gcx.invariant(): this = %p\n", this);
1497 size_t i;
1498
1499 // Assure we're called on the right thread
1500 debug (THREADINVARIANT) assert(self == pthread_self());
1501
1502 for (i = 0; i < npools; i++)
1503 { Pool *pool = pooltable[i];
1504
1505 pool.Invariant();
1506 if (i == 0)
1507 {
1508 assert(minAddr == pool.baseAddr);
1509 }
1510 if (i + 1 < npools)
1511 {
1512 assert(pool.opCmp(pooltable[i + 1]) < 0);
1513 }
1514 else if (i + 1 == npools)
1515 {
1516 assert(maxAddr == pool.topAddr);
1517 }
1518 }
1519
1520 if (roots)
1521 {
1522 assert(rootdim != 0);
1523 assert(nroots <= rootdim);
1524 }
1525
1526 if (ranges)
1527 {
1528 assert(rangedim != 0);
1529 assert(nranges <= rangedim);
1530
1531 for (i = 0; i < nranges; i++)
1532 {
1533 assert(ranges[i].pbot);
1534 assert(ranges[i].ptop);
1535 assert(ranges[i].pbot <= ranges[i].ptop);
1536 }
1537 }
1538
1539 for (i = 0; i < B_PAGE; i++)
1540 {
1541 for (List *list = bucket[i]; list; list = list.next)
1542 {
1543 }
1544 }
1545 }
1546 }
1547
1548
1549 /**
1550 *
1551 */
1552 void addRoot(void *p)
1553 {
1554 if (nroots == rootdim)
1555 {
1556 size_t newdim = rootdim * 2 + 16;
1557 void** newroots;
1558
1559 newroots = cast(void**)cstdlib.malloc(newdim * newroots[0].sizeof);
1560 if (!newroots)
1561 onOutOfMemoryError();
1562 if (roots)
1563 { memcpy(newroots, roots, nroots * newroots[0].sizeof);
1564 cstdlib.free(roots);
1565 }
1566 roots = newroots;
1567 rootdim = newdim;
1568 }
1569 roots[nroots] = p;
1570 nroots++;
1571 }
1572
1573
1574 /**
1575 *
1576 */
1577 void removeRoot(void *p)
1578 {
1579 for (size_t i = nroots; i--;)
1580 {
1581 if (roots[i] == p)
1582 {
1583 nroots--;
1584 memmove(roots + i, roots + i + 1, (nroots - i) * roots[0].sizeof);
1585 return;
1586 }
1587 }
1588 assert(0);
1589 }
1590
1591
1592 /**
1593 *
1594 */
1595 void addRange(void *pbot, void *ptop)
1596 {
1597 debug(PRINTF) printf("Thread %x ", pthread_self());
1598 debug(PRINTF) printf("%x.Gcx::addRange(%x, %x), nranges = %d\n", this, pbot, ptop, nranges);
1599 if (nranges == rangedim)
1600 {
1601 size_t newdim = rangedim * 2 + 16;
1602 Range *newranges;
1603
1604 newranges = cast(Range*)cstdlib.malloc(newdim * newranges[0].sizeof);
1605 if (!newranges)
1606 onOutOfMemoryError();
1607 if (ranges)
1608 { memcpy(newranges, ranges, nranges * newranges[0].sizeof);
1609 cstdlib.free(ranges);
1610 }
1611 ranges = newranges;
1612 rangedim = newdim;
1613 }
1614 ranges[nranges].pbot = pbot;
1615 ranges[nranges].ptop = ptop;
1616 nranges++;
1617 }
1618
1619
1620 /**
1621 *
1622 */
1623 void removeRange(void *pbot)
1624 {
1625 debug(PRINTF) printf("Thread %x ", pthread_self());
1626 debug(PRINTF) printf("%x.Gcx.removeRange(%x), nranges = %d\n", this, pbot, nranges);
1627 for (size_t i = nranges; i--;)
1628 {
1629 if (ranges[i].pbot == pbot)
1630 {
1631 nranges--;
1632 memmove(ranges + i, ranges + i + 1, (nranges - i) * ranges[0].sizeof);
1633 return;
1634 }
1635 }
1636 debug(PRINTF) printf("Wrong thread\n");
1637
1638 // This is a fatal error, but ignore it.
1639 // The problem is that we can get a Close() call on a thread
1640 // other than the one the range was allocated on.
1641 //assert(zero);
1642 }
1643
1644
1645 /**
1646 * Find Pool that pointer is in.
1647 * Return null if not in a Pool.
1648 * Assume pooltable[] is sorted.
1649 */
1650 Pool *findPool(void *p)
1651 {
1652 if (p >= minAddr && p < maxAddr)
1653 {
1654 if (npools == 1)
1655 {
1656 return pooltable[0];
1657 }
1658
1659 for (size_t i = 0; i < npools; i++)
1660 { Pool *pool;
1661
1662 pool = pooltable[i];
1663 if (p < pool.topAddr)
1664 { if (pool.baseAddr <= p)
1665 return pool;
1666 break;
1667 }
1668 }
1669 }
1670 return null;
1671 }
1672
1673
1674 /**
1675 * Find base address of block containing pointer p.
1676 * Returns null if not a gc'd pointer
1677 */
1678 void* findBase(void *p)
1679 {
1680 Pool *pool;
1681
1682 pool = findPool(p);
1683 if (pool)
1684 {
1685 size_t offset = cast(size_t)(p - pool.baseAddr);
1686 size_t pn = offset / PAGESIZE;
1687 Bins bin = cast(Bins)pool.pagetable[pn];
1688
1689 // Adjust bit to be at start of allocated memory block
1690 if (bin <= B_PAGE)
1691 {
1692 return pool.baseAddr + (offset & notbinsize[bin]);
1693 }
1694 else if (bin == B_PAGEPLUS)
1695 {
1696 do
1697 { --pn, offset -= PAGESIZE;
1698 } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
1699
1700 return pool.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
1701 }
1702 else
1703 {
1704 // we are in a B_FREE or B_UNCOMMITTED page
1705 return null;
1706 }
1707 }
1708 return null;
1709 }
1710
1711
1712 /**
1713 * Find size of pointer p.
1714 * Returns 0 if not a gc'd pointer
1715 */
1716 size_t findSize(void *p)
1717 {
1718 Pool* pool;
1719 size_t size = 0;
1720
1721 pool = findPool(p);
1722 if (pool)
1723 {
1724 size_t pagenum;
1725 Bins bin;
1726
1727 pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE;
1728 bin = cast(Bins)pool.pagetable[pagenum];
1729 size = binsize[bin];
1730 if (bin == B_PAGE)
1731 { size_t npages = pool.ncommitted;
1732 ubyte* pt;
1733 size_t i;
1734
1735 pt = &pool.pagetable[0];
1736 for (i = pagenum + 1; i < npages; i++)
1737 {
1738 if (pt[i] != B_PAGEPLUS)
1739 break;
1740 }
1741 size = (i - pagenum) * PAGESIZE;
1742 }
1743 }
1744 return size;
1745 }
1746
1747
1748 /**
1749 *
1750 */
1751 BlkInfo getInfo(void* p)
1752 {
1753 Pool* pool;
1754 BlkInfo info;
1755
1756 pool = findPool(p);
1757 if (pool)
1758 {
1759 size_t offset = cast(size_t)(p - pool.baseAddr);
1760 size_t pn = offset / PAGESIZE;
1761 Bins bin = cast(Bins)pool.pagetable[pn];
1762
1763 ////////////////////////////////////////////////////////////////////
1764 // findAddr
1765 ////////////////////////////////////////////////////////////////////
1766
1767 if (bin <= B_PAGE)
1768 {
1769 info.base = pool.baseAddr + (offset & notbinsize[bin]);
1770 }
1771 else if (bin == B_PAGEPLUS)
1772 {
1773 do
1774 { --pn, offset -= PAGESIZE;
1775 } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
1776
1777 info.base = pool.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
1778
1779 // fix bin for use by size calc below
1780 bin = cast(Bins)pool.pagetable[pn];
1781 }
1782
1783 ////////////////////////////////////////////////////////////////////
1784 // findSize
1785 ////////////////////////////////////////////////////////////////////
1786
1787 info.size = binsize[bin];
1788 if (bin == B_PAGE)
1789 { size_t npages = pool.ncommitted;
1790 ubyte* pt;
1791 size_t i;
1792
1793 pt = &pool.pagetable[0];
1794 for (i = pn + 1; i < npages; i++)
1795 {
1796 if (pt[i] != B_PAGEPLUS)
1797 break;
1798 }
1799 info.size = (i - pn) * PAGESIZE;
1800 }
1801
1802 ////////////////////////////////////////////////////////////////////
1803 // getBits
1804 ////////////////////////////////////////////////////////////////////
1805
1806 info.attr = getBits(pool, cast(size_t)(offset / 16));
1807 }
1808 return info;
1809 }
1810
1811
1812 /**
1813 * Compute bin for size.
1814 */
1815 static Bins findBin(size_t size)
1816 { Bins bin;
1817
1818 if (size <= 256)
1819 {
1820 if (size <= 64)
1821 {
1822 if (size <= 16)
1823 bin = B_16;
1824 else if (size <= 32)
1825 bin = B_32;
1826 else
1827 bin = B_64;
1828 }
1829 else
1830 {
1831 if (size <= 128)
1832 bin = B_128;
1833 else
1834 bin = B_256;
1835 }
1836 }
1837 else
1838 {
1839 if (size <= 1024)
1840 {
1841 if (size <= 512)
1842 bin = B_512;
1843 else
1844 bin = B_1024;
1845 }
1846 else
1847 {
1848 if (size <= 2048)
1849 bin = B_2048;
1850 else
1851 bin = B_PAGE;
1852 }
1853 }
1854 return bin;
1855 }
1856
1857
1858 /**
1859 * Allocate a new pool of at least size bytes.
1860 * Sort it into pooltable[].
1861 * Mark all memory in the pool as B_FREE.
1862 * Return the actual number of bytes reserved or 0 on error.
1863 */
1864 size_t reserve(size_t size)
1865 {
1866 size_t npages = (size + PAGESIZE - 1) / PAGESIZE;
1867 Pool* pool = newPool(npages);
1868
1869 if (!pool || pool.extendPages(npages) == OPFAIL)
1870 return 0;
1871 return pool.ncommitted * PAGESIZE;
1872 }
1873
1874
1875 /**
1876 * Minimizes physical memory usage by returning free pools to the OS.
1877 */
1878 void minimize()
1879 {
1880 size_t n;
1881 size_t pn;
1882 Pool* pool;
1883 size_t ncommitted;
1884
1885 for (n = 0; n < npools; n++)
1886 {
1887 pool = pooltable[n];
1888 ncommitted = pool.ncommitted;
1889 for (pn = 0; pn < ncommitted; pn++)
1890 {
1891 if (cast(Bins)pool.pagetable[pn] != B_FREE)
1892 break;
1893 }
1894 if (pn < ncommitted)
1895 {
1896 n++;
1897 continue;
1898 }
1899 pool.Dtor();
1900 cstdlib.free(pool);
1901 memmove(pooltable + n,
1902 pooltable + n + 1,
1903 (--npools - n) * (Pool*).sizeof);
1904 minAddr = pooltable[0].baseAddr;
1905 maxAddr = pooltable[npools - 1].topAddr;
1906 }
1907 }
1908
1909
1910 /**
1911 * Allocate a chunk of memory that is larger than a page.
1912 * Return null if out of memory.
1913 */
1914 void *bigAlloc(size_t size)
1915 {
1916 Pool* pool;
1917 size_t npages;
1918 size_t n;
1919 size_t pn;
1920 size_t freedpages;
1921 void* p;
1922 int state;
1923
1924 npages = (size + PAGESIZE - 1) / PAGESIZE;
1925
1926 for (state = 0; ; )
1927 {
1928 // This code could use some refinement when repeatedly
1929 // allocating very large arrays.
1930
1931 for (n = 0; n < npools; n++)
1932 {
1933 pool = pooltable[n];
1934 pn = pool.allocPages(npages);
1935 if (pn != OPFAIL)
1936 goto L1;
1937 }
1938
1939 // Failed
1940 switch (state)
1941 {
1942 case 0:
1943 if (disabled)
1944 { state = 1;
1945 continue;
1946 }
1947 // Try collecting
1948 freedpages = fullcollectshell();
1949 if (freedpages >= npools * ((POOLSIZE / PAGESIZE) / 4))
1950 { state = 1;
1951 continue;
1952 }
1953 // Release empty pools to prevent bloat
1954 minimize();
1955 // Allocate new pool
1956 pool = newPool(npages);
1957 if (!pool)
1958 { state = 2;
1959 continue;
1960 }
1961 pn = pool.allocPages(npages);
1962 assert(pn != OPFAIL);
1963 goto L1;
1964 case 1:
1965 // Release empty pools to prevent bloat
1966 minimize();
1967 // Allocate new pool
1968 pool = newPool(npages);
1969 if (!pool)
1970 goto Lnomemory;
1971 pn = pool.allocPages(npages);
1972 assert(pn != OPFAIL);
1973 goto L1;
1974 case 2:
1975 goto Lnomemory;
1976 default:
1977 assert(false);
1978 }
1979 }
1980
1981 L1:
1982 pool.pagetable[pn] = B_PAGE;
1983 if (npages > 1)
1984 memset(&pool.pagetable[pn + 1], B_PAGEPLUS, npages - 1);
1985 p = pool.baseAddr + pn * PAGESIZE;
1986 memset(cast(char *)p + size, 0, npages * PAGESIZE - size);
1987 debug (MEMSTOMP) memset(p, 0xF1, size);
1988 //debug(PRINTF) printf("\tp = %x\n", p);
1989 return p;
1990
1991 Lnomemory:
1992 return null; // let mallocNoSync handle the error
1993 }
1994
1995
1996 /**
1997 * Allocate a new pool with at least npages in it.
1998 * Sort it into pooltable[].
1999 * Return null if failed.
2000 */
2001 Pool *newPool(size_t npages)
2002 {
2003 Pool* pool;
2004 Pool** newpooltable;
2005 size_t newnpools;
2006 size_t i;
2007
2008 //debug(PRINTF) printf("************Gcx::newPool(npages = %d)****************\n", npages);
2009
2010 // Round up to COMMITSIZE pages
2011 npages = (npages + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1);
2012
2013 // Minimum of POOLSIZE
2014 if (npages < POOLSIZE/PAGESIZE)
2015 npages = POOLSIZE/PAGESIZE;
2016 else if (npages > POOLSIZE/PAGESIZE)
2017 { // Give us 150% of requested size, so there's room to extend
2018 auto n = npages + (npages >> 1);
2019 if (n < size_t.max/PAGESIZE)
2020 npages = n;
2021 }
2022
2023 // Allocate successively larger pools up to 8 megs
2024 if (npools)
2025 { size_t n;
2026
2027 n = npools;
2028 if (n > 8)
2029 n = 8; // cap pool size at 8 megs
2030 n *= (POOLSIZE / PAGESIZE);
2031 if (npages < n)
2032 npages = n;
2033 }
2034
2035 pool = cast(Pool *)cstdlib.calloc(1, Pool.sizeof);
2036 if (pool)
2037 {
2038 pool.initialize(npages);
2039 if (!pool.baseAddr)
2040 goto Lerr;
2041
2042 newnpools = npools + 1;
2043 newpooltable = cast(Pool **)cstdlib.realloc(pooltable, newnpools * (Pool *).sizeof);
2044 if (!newpooltable)
2045 goto Lerr;
2046
2047 // Sort pool into newpooltable[]
2048 for (i = 0; i < npools; i++)
2049 {
2050 if (pool.opCmp(newpooltable[i]) < 0)
2051 break;
2052 }
2053 memmove(newpooltable + i + 1, newpooltable + i, (npools - i) * (Pool *).sizeof);
2054 newpooltable[i] = pool;
2055
2056 pooltable = newpooltable;
2057 npools = newnpools;
2058
2059 minAddr = pooltable[0].baseAddr;
2060 maxAddr = pooltable[npools - 1].topAddr;
2061 }
2062 return pool;
2063
2064 Lerr:
2065 pool.Dtor();
2066 cstdlib.free(pool);
2067 return null;
2068 }
2069
2070
2071 /**
2072 * Allocate a page of bin's.
2073 * Returns:
2074 * 0 failed
2075 */
2076 int allocPage(Bins bin)
2077 {
2078 Pool* pool;
2079 size_t n;
2080 size_t pn;
2081 byte* p;
2082 byte* ptop;
2083
2084 //debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin);
2085 for (n = 0; n < npools; n++)
2086 {
2087 pool = pooltable[n];
2088 pn = pool.allocPages(1);
2089 if (pn != OPFAIL)
2090 goto L1;
2091 }
2092 return 0; // failed
2093
2094 L1:
2095 pool.pagetable[pn] = cast(ubyte)bin;
2096
2097 // Convert page to free list
2098 size_t size = binsize[bin];
2099 List **b = &bucket[bin];
2100
2101 p = pool.baseAddr + pn * PAGESIZE;
2102 ptop = p + PAGESIZE;
2103 for (; p < ptop; p += size)
2104 {
2105 (cast(List *)p).next = *b;
2106 *b = cast(List *)p;
2107 }
2108 return 1;
2109 }
2110
2111
2112 /**
2113 * Search a range of memory values and mark any pointers into the GC pool.
2114 */
2115 void mark(void *pbot, void *ptop)
2116 {
2117 void **p1 = cast(void **)pbot;
2118 void **p2 = cast(void **)ptop;
2119 size_t pcache = 0;
2120 uint changes = 0;
2121
2122 //printf("marking range: %p -> %p\n", pbot, ptop);
2123 for (; p1 < p2; p1++)
2124 {
2125 Pool *pool;
2126 byte *p = cast(byte *)(*p1);
2127
2128 //if (log) debug(PRINTF) printf("\tmark %x\n", p);
2129 if (p >= minAddr && p < maxAddr)
2130 {
2131 if ((cast(size_t)p & ~(PAGESIZE-1)) == pcache)
2132 continue;
2133
2134 pool = findPool(p);
2135 if (pool)
2136 {
2137 size_t offset = cast(size_t)(p - pool.baseAddr);
2138 size_t biti;
2139 size_t pn = offset / PAGESIZE;
2140 Bins bin = cast(Bins)pool.pagetable[pn];
2141
2142 //debug(PRINTF) printf("\t\tfound pool %x, base=%x, pn = %d, bin = %d, biti = x%x\n", pool, pool.baseAddr, pn, bin, biti);
2143
2144 // Adjust bit to be at start of allocated memory block
2145 if (bin <= B_PAGE)
2146 {
2147 biti = (offset & notbinsize[bin]) >> 4;
2148 //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
2149 }
2150 else if (bin == B_PAGEPLUS)
2151 {
2152 do
2153 { --pn;
2154 } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
2155 biti = pn * (PAGESIZE / 16);
2156 }
2157 else
2158 {
2159 // Don't mark bits in B_FREE or B_UNCOMMITTED pages
2160 continue;
2161 }
2162
2163 if (bin >= B_PAGE) // Cache B_PAGE and B_PAGEPLUS lookups
2164 pcache = cast(size_t)p & ~(PAGESIZE-1);
2165
2166 //debug(PRINTF) printf("\t\tmark(x%x) = %d\n", biti, pool.mark.test(biti));
2167 if (!pool.mark.test(biti))
2168 {
2169 //if (log) debug(PRINTF) printf("\t\tmarking %x\n", p);
2170 pool.mark.set(biti);
2171 if (!pool.noscan.test(biti))
2172 {
2173 pool.scan.set(biti);
2174 changes = 1;
2175 }
2176 log_parent(sentinel_add(pool.baseAddr + biti * 16), sentinel_add(pbot));
2177 }
2178 }
2179 }
2180 }
2181 anychanges |= changes;
2182 }
2183
2184
2185 /**
2186 * Return number of full pages free'd.
2187 */
2188 size_t fullcollectshell()
2189 {
2190 // The purpose of the 'shell' is to ensure all the registers
2191 // get put on the stack so they'll be scanned
2192 void *sp;
2193 size_t result;
2194 version (GNU)
2195 {
2196 __builtin_unwind_init();
2197 sp = & sp;
2198 }
2199 else
2200 {
2201 asm
2202 {
2203 pushad ;
2204 mov sp[EBP],ESP ;
2205 }
2206 }
2207 result = fullcollect(sp);
2208 version (GNU)
2209 {
2210 // nothing to do
2211 }
2212 else
2213 {
2214 asm
2215 {
2216 popad ;
2217 }
2218 }
2219 return result;
2220 }
2221
2222
2223 /**
2224 *
2225 */
2226 size_t fullcollect(void *stackTop)
2227 {
2228 size_t n;
2229 Pool* pool;
2230
2231 debug(COLLECT_PRINTF) printf("Gcx.fullcollect()\n");
2232
2233 thread_suspendAll();
2234
2235 p_cache = null;
2236 size_cache = 0;
2237
2238 anychanges = 0;
2239 for (n = 0; n < npools; n++)
2240 {
2241 pool = pooltable[n];
2242 pool.mark.zero();
2243 pool.scan.zero();
2244 pool.freebits.zero();
2245 }
2246
2247 // Mark each free entry, so it doesn't get scanned
2248 for (n = 0; n < B_PAGE; n++)
2249 {
2250 for (List *list = bucket[n]; list; list = list.next)
2251 {
2252 pool = findPool(list);
2253 assert(pool);
2254 pool.freebits.set(cast(size_t)(cast(byte*)list - pool.baseAddr) / 16);
2255 }
2256 }
2257
2258 for (n = 0; n < npools; n++)
2259 {
2260 pool = pooltable[n];
2261 pool.mark.copy(&pool.freebits);
2262 }
2263
2264 rt_scanStaticData( &mark );
2265
2266 version (MULTI_THREADED)
2267 {
2268 if (!noStack)
2269 {
2270 // Scan stacks and registers for each paused thread
2271 thread_scanAll( &mark, stackTop );
2272 }
2273 }
2274 else
2275 {
2276 if (!noStack)
2277 {
2278 // Scan stack for main thread
2279 debug(PRINTF) printf(" scan stack bot = %x, top = %x\n", stackTop, stackBottom);
2280 version (STACKGROWSDOWN)
2281 mark(stackTop, stackBottom);
2282 else
2283 mark(stackBottom, stackTop);
2284 }
2285 }
2286
2287 // Scan roots[]
2288 debug(COLLECT_PRINTF) printf("scan roots[]\n");
2289 mark(roots, roots + nroots);
2290
2291 // Scan ranges[]
2292 debug(COLLECT_PRINTF) printf("scan ranges[]\n");
2293 //log++;
2294 for (n = 0; n < nranges; n++)
2295 {
2296 debug(COLLECT_PRINTF) printf("\t%x .. %x\n", ranges[n].pbot, ranges[n].ptop);
2297 mark(ranges[n].pbot, ranges[n].ptop);
2298 }
2299 //log--;
2300
2301 debug(COLLECT_PRINTF) printf("\tscan heap\n");
2302 while (anychanges)
2303 {
2304 anychanges = 0;
2305 for (n = 0; n < npools; n++)
2306 {
2307 uint *bbase;
2308 uint *b;
2309 uint *btop;
2310
2311 pool = pooltable[n];
2312
2313 bbase = pool.scan.base();
2314 btop = bbase + pool.scan.nwords;
2315 for (b = bbase; b < btop;)
2316 { Bins bin;
2317 size_t pn;
2318 size_t u;
2319 size_t bitm;
2320 byte* o;
2321
2322 bitm = *b;
2323 if (!bitm)
2324 { b++;
2325 continue;
2326 }
2327 *b = 0;
2328
2329 o = pool.baseAddr + (b - bbase) * 32 * 16;
2330 if (!(bitm & 0xFFFF))
2331 {
2332 bitm >>= 16;
2333 o += 16 * 16;
2334 }
2335 for (; bitm; o += 16, bitm >>= 1)
2336 {
2337 if (!(bitm & 1))
2338 continue;
2339
2340 pn = cast(size_t)(o - pool.baseAddr) / PAGESIZE;
2341 bin = cast(Bins)pool.pagetable[pn];
2342 if (bin < B_PAGE)
2343 {
2344 mark(o, o + binsize[bin]);
2345 }
2346 else if (bin == B_PAGE || bin == B_PAGEPLUS)
2347 {
2348 if (bin == B_PAGEPLUS)
2349 {
2350 while (pool.pagetable[pn - 1] != B_PAGE)
2351 pn--;
2352 }
2353 u = 1;
2354 while (pn + u < pool.ncommitted && pool.pagetable[pn + u] == B_PAGEPLUS)
2355 u++;
2356 mark(o, o + u * PAGESIZE);
2357 }
2358 }
2359 }
2360 }
2361 }
2362
2363 thread_resumeAll();
2364
2365 // Free up everything not marked
2366 debug(COLLECT_PRINTF) printf("\tfree'ing\n");
2367 size_t freedpages = 0;
2368 size_t freed = 0;
2369 for (n = 0; n < npools; n++)
2370 { size_t pn;
2371 size_t ncommitted;
2372 uint* bbase;
2373
2374 pool = pooltable[n];
2375 bbase = pool.mark.base();
2376 ncommitted = pool.ncommitted;
2377 for (pn = 0; pn < ncommitted; pn++, bbase += PAGESIZE / (32 * 16))
2378 {
2379 Bins bin = cast(Bins)pool.pagetable[pn];
2380
2381 if (bin < B_PAGE)
2382 { byte* p;
2383 byte* ptop;
2384 size_t biti;
2385 size_t bitstride;
2386 auto size = binsize[bin];
2387
2388 p = pool.baseAddr + pn * PAGESIZE;
2389 ptop = p + PAGESIZE;
2390 biti = pn * (PAGESIZE/16);
2391 bitstride = size / 16;
2392
2393 version(none) // BUG: doesn't work because freebits() must also be cleared
2394 {
2395 // If free'd entire page
2396 if (bbase[0] == 0 && bbase[1] == 0 && bbase[2] == 0 && bbase[3] == 0 &&
2397 bbase[4] == 0 && bbase[5] == 0 && bbase[6] == 0 && bbase[7] == 0)
2398 {
2399 for (; p < ptop; p += size, biti += bitstride)
2400 {
2401 if (pool.finals.nbits && pool.finals.testClear(biti))
2402 rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/);
2403 gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
2404
2405 List *list = cast(List *)p;
2406 //debug(PRINTF) printf("\tcollecting %x\n", list);
2407 log_free(sentinel_add(list));
2408
2409 debug (MEMSTOMP) memset(p, 0xF3, size);
2410 }
2411 pool.pagetable[pn] = B_FREE;
2412 freed += PAGESIZE;
2413 //debug(PRINTF) printf("freeing entire page %d\n", pn);
2414 continue;
2415 }
2416 }
2417 for (; p < ptop; p += size, biti += bitstride)
2418 {
2419 if (!pool.mark.test(biti))
2420 {
2421 sentinel_Invariant(sentinel_add(p));
2422
2423 pool.freebits.set(biti);
2424 if (pool.finals.nbits && pool.finals.testClear(biti))
2425 rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/);
2426 clrBits(pool, biti, BlkAttr.ALL_BITS);
2427
2428 List *list = cast(List *)p;
2429 debug(PRINTF) printf("\tcollecting %x\n", list);
2430 log_free(sentinel_add(list));
2431
2432 debug (MEMSTOMP) memset(p, 0xF3, size);
2433
2434 freed += size;
2435 }
2436 }
2437 }
2438 else if (bin == B_PAGE)
2439 { size_t biti = pn * (PAGESIZE / 16);
2440
2441 if (!pool.mark.test(biti))
2442 { byte *p = pool.baseAddr + pn * PAGESIZE;
2443
2444 sentinel_Invariant(sentinel_add(p));
2445 if (pool.finals.nbits && pool.finals.testClear(biti))
2446 rt_finalize(sentinel_add(p), false/*noStack > 0*/);
2447 clrBits(pool, biti, BlkAttr.ALL_BITS);
2448
2449 debug(COLLECT_PRINTF) printf("\tcollecting big %x\n", p);
2450 log_free(sentinel_add(p));
2451 pool.pagetable[pn] = B_FREE;
2452 freedpages++;
2453 debug (MEMSTOMP) memset(p, 0xF3, PAGESIZE);
2454 while (pn + 1 < ncommitted && pool.pagetable[pn + 1] == B_PAGEPLUS)
2455 {
2456 pn++;
2457 pool.pagetable[pn] = B_FREE;
2458 freedpages++;
2459
2460 debug (MEMSTOMP)
2461 { p += PAGESIZE;
2462 memset(p, 0xF3, PAGESIZE);
2463 }
2464 }
2465 }
2466 }
2467 }
2468 }
2469
2470 // Zero buckets
2471 bucket[] = null;
2472
2473 // Free complete pages, rebuild free list
2474 debug(COLLECT_PRINTF) printf("\tfree complete pages\n");
2475 size_t recoveredpages = 0;
2476 for (n = 0; n < npools; n++)
2477 { size_t pn;
2478 size_t ncommitted;
2479
2480 pool = pooltable[n];
2481 ncommitted = pool.ncommitted;
2482 for (pn = 0; pn < ncommitted; pn++)
2483 {
2484 Bins bin = cast(Bins)pool.pagetable[pn];
2485 size_t biti;
2486 size_t u;
2487
2488 if (bin < B_PAGE)
2489 {
2490 size_t size = binsize[bin];
2491 size_t bitstride = size / 16;
2492 size_t bitbase = pn * (PAGESIZE / 16);
2493 size_t bittop = bitbase + (PAGESIZE / 16);
2494 byte* p;
2495
2496 biti = bitbase;
2497 for (biti = bitbase; biti < bittop; biti += bitstride)
2498 { if (!pool.freebits.test(biti))
2499 goto Lnotfree;
2500 }
2501 pool.pagetable[pn] = B_FREE;
2502 recoveredpages++;
2503 continue;
2504
2505 Lnotfree:
2506 p = pool.baseAddr + pn * PAGESIZE;
2507 for (u = 0; u < PAGESIZE; u += size)
2508 { biti = bitbase + u / 16;
2509 if (pool.freebits.test(biti))
2510 { List *list;
2511
2512 list = cast(List *)(p + u);
2513 if (list.next != bucket[bin]) // avoid unnecessary writes
2514 list.next = bucket[bin];
2515 bucket[bin] = list;
2516 }
2517 }
2518 }
2519 }
2520 }
2521
2522 debug(COLLECT_PRINTF) printf("recovered pages = %d\n", recoveredpages);
2523 debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedpages, npools);
2524
2525 return freedpages + recoveredpages;
2526 }
2527
2528
2529 /**
2530 *
2531 */
2532 uint getBits(Pool* pool, size_t biti)
2533 in
2534 {
2535 assert( pool );
2536 }
2537 body
2538 {
2539 uint bits;
2540
2541 if (pool.finals.nbits &&
2542 pool.finals.test(biti))
2543 bits |= BlkAttr.FINALIZE;
2544 if (pool.noscan.test(biti))
2545 bits |= BlkAttr.NO_SCAN;
2546 // if (pool.nomove.nbits &&
2547 // pool.nomove.test(biti))
2548 // bits |= BlkAttr.NO_MOVE;
2549 return bits;
2550 }
2551
2552
2553 /**
2554 *
2555 */
2556 void setBits(Pool* pool, size_t biti, uint mask)
2557 in
2558 {
2559 assert( pool );
2560 }
2561 body
2562 {
2563 if (mask & BlkAttr.FINALIZE)
2564 {
2565 if (!pool.finals.nbits)
2566 pool.finals.alloc(pool.mark.nbits);
2567 pool.finals.set(biti);
2568 }
2569 if (mask & BlkAttr.NO_SCAN)
2570 {
2571 pool.noscan.set(biti);
2572 }
2573 // if (mask & BlkAttr.NO_MOVE)
2574 // {
2575 // if (!pool.nomove.nbits)
2576 // pool.nomove.alloc(pool.mark.nbits);
2577 // pool.nomove.set(biti);
2578 // }
2579 }
2580
2581
2582 /**
2583 *
2584 */
2585 void clrBits(Pool* pool, size_t biti, uint mask)
2586 in
2587 {
2588 assert( pool );
2589 }
2590 body
2591 {
2592 if (mask & BlkAttr.FINALIZE && pool.finals.nbits)
2593 pool.finals.clear(biti);
2594 if (mask & BlkAttr.NO_SCAN)
2595 pool.noscan.clear(biti);
2596 // if (mask & BlkAttr.NO_MOVE && pool.nomove.nbits)
2597 // pool.nomove.clear(biti);
2598 }
2599
2600
2601 /***** Leak Detector ******/
2602
2603
2604 debug (LOGGING)
2605 {
2606 LogArray current;
2607 LogArray prev;
2608
2609
2610 void log_init()
2611 {
2612 //debug(PRINTF) printf("+log_init()\n");
2613 current.reserve(1000);
2614 prev.reserve(1000);
2615 //debug(PRINTF) printf("-log_init()\n");
2616 }
2617
2618
2619 void log_malloc(void *p, size_t size)
2620 {
2621 //debug(PRINTF) printf("+log_malloc(p = %x, size = %d)\n", p, size);
2622 Log log;
2623
2624 log.p = p;
2625 log.size = size;
2626 log.line = GC.line;
2627 log.file = GC.file;
2628 log.parent = null;
2629
2630 GC.line = 0;
2631 GC.file = null;
2632
2633 current.push(log);
2634 //debug(PRINTF) printf("-log_malloc()\n");
2635 }
2636
2637
2638 void log_free(void *p)
2639 {
2640 //debug(PRINTF) printf("+log_free(%x)\n", p);
2641 size_t i;
2642
2643 i = current.find(p);
2644 if (i == OPFAIL)
2645 {
2646 debug(PRINTF) printf("free'ing unallocated memory %x\n", p);
2647 }
2648 else
2649 current.remove(i);
2650 //debug(PRINTF) printf("-log_free()\n");
2651 }
2652
2653
2654 void log_collect()
2655 {
2656 //debug(PRINTF) printf("+log_collect()\n");
2657 // Print everything in current that is not in prev
2658
2659 debug(PRINTF) printf("New pointers this cycle: --------------------------------\n");
2660 size_t used = 0;
2661 for (size_t i = 0; i < current.dim; i++)
2662 {
2663 size_t j;
2664
2665 j = prev.find(current.data[i].p);
2666 if (j == OPFAIL)
2667 current.data[i].print();
2668 else
2669 used++;
2670 }
2671
2672 debug(PRINTF) printf("All roots this cycle: --------------------------------\n");
2673 for (size_t i = 0; i < current.dim; i++)
2674 {
2675 void *p;
2676 size_t j;
2677
2678 p = current.data[i].p;
2679 if (!findPool(current.data[i].parent))
2680 {
2681 j = prev.find(current.data[i].p);
2682 if (j == OPFAIL)
2683 debug(PRINTF) printf("N");
2684 else
2685 debug(PRINTF) printf(" ");;
2686 current.data[i].print();
2687 }
2688 }
2689
2690 debug(PRINTF) printf("Used = %d-------------------------------------------------\n", used);
2691 prev.copy(&current);
2692
2693 debug(PRINTF) printf("-log_collect()\n");
2694 }
2695
2696
2697 void log_parent(void *p, void *parent)
2698 {
2699 //debug(PRINTF) printf("+log_parent()\n");
2700 size_t i;
2701
2702 i = current.find(p);
2703 if (i == OPFAIL)
2704 {
2705 debug(PRINTF) printf("parent'ing unallocated memory %x, parent = %x\n", p, parent);
2706 Pool *pool;
2707 pool = findPool(p);
2708 assert(pool);
2709 size_t offset = cast(size_t)(p - pool.baseAddr);
2710 size_t biti;
2711 size_t pn = offset / PAGESIZE;
2712 Bins bin = cast(Bins)pool.pagetable[pn];
2713 biti = (offset & notbinsize[bin]);
2714 debug(PRINTF) printf("\tbin = %d, offset = x%x, biti = x%x\n", bin, offset, biti);
2715 }
2716 else
2717 {
2718 current.data[i].parent = parent;
2719 }
2720 //debug(PRINTF) printf("-log_parent()\n");
2721 }
2722
2723 }
2724 else
2725 {
2726 void log_init() { }
2727 void log_malloc(void *p, size_t size) { }
2728 void log_free(void *p) { }
2729 void log_collect() { }
2730 void log_parent(void *p, void *parent) { }
2731 }
2732 }
2733
2734
2735 /* ============================ Pool =============================== */
2736
2737
2738 struct Pool
2739 {
2740 byte* baseAddr;
2741 byte* topAddr;
2742 GCBits mark; // entries already scanned, or should not be scanned
2743 GCBits scan; // entries that need to be scanned
2744 GCBits freebits; // entries that are on the free list
2745 GCBits finals; // entries that need finalizer run on them
2746 GCBits noscan; // entries that should not be scanned
2747
2748 size_t npages;
2749 size_t ncommitted; // ncommitted <= npages
2750 ubyte* pagetable;
2751
2752
2753 void initialize(size_t npages)
2754 {
2755 size_t poolsize;
2756
2757 //debug(PRINTF) printf("Pool::Pool(%u)\n", npages);
2758 poolsize = npages * PAGESIZE;
2759 assert(poolsize >= POOLSIZE);
2760 baseAddr = cast(byte *)os_mem_map(poolsize);
2761
2762 // Some of the code depends on page alignment of memory pools
2763 assert((cast(size_t)baseAddr & (PAGESIZE - 1)) == 0);
2764
2765 if (!baseAddr)
2766 {
2767 //debug(PRINTF) printf("GC fail: poolsize = x%x, errno = %d\n", poolsize, errno);
2768 //debug(PRINTF) printf("message = '%s'\n", sys_errlist[errno]);
2769
2770 npages = 0;
2771 poolsize = 0;
2772 }
2773 //assert(baseAddr);
2774 topAddr = baseAddr + poolsize;
2775
2776 mark.alloc(cast(size_t)poolsize / 16);
2777 scan.alloc(cast(size_t)poolsize / 16);
2778 freebits.alloc(cast(size_t)poolsize / 16);
2779 noscan.alloc(cast(size_t)poolsize / 16);
2780
2781 pagetable = cast(ubyte*)cstdlib.malloc(npages);
2782 if (!pagetable)
2783 onOutOfMemoryError();
2784 memset(pagetable, B_UNCOMMITTED, npages);
2785
2786 this.npages = npages;
2787 ncommitted = 0;
2788 }
2789
2790
2791 void Dtor()
2792 {
2793 if (baseAddr)
2794 {
2795 int result;
2796
2797 if (ncommitted)
2798 {
2799 result = os_mem_decommit(baseAddr, 0, ncommitted * PAGESIZE);
2800 assert(result == 0);
2801 ncommitted = 0;
2802 }
2803
2804 if (npages)
2805 {
2806 result = os_mem_unmap(baseAddr, npages * PAGESIZE);
2807 assert(result == 0);
2808 npages = 0;
2809 }
2810
2811 baseAddr = null;
2812 topAddr = null;
2813 }
2814 if (pagetable)
2815 cstdlib.free(pagetable);
2816
2817 mark.Dtor();
2818 scan.Dtor();
2819 freebits.Dtor();
2820 finals.Dtor();
2821 noscan.Dtor();
2822 }
2823
2824
2825 void Invariant() { }
2826
2827
2828 invariant()
2829 {
2830 //mark.Invariant();
2831 //scan.Invariant();
2832 //freebits.Invariant();
2833 //finals.Invariant();
2834 //noscan.Invariant();
2835
2836 if (baseAddr)
2837 {
2838 //if (baseAddr + npages * PAGESIZE != topAddr)
2839 //printf("baseAddr = %p, npages = %d, topAddr = %p\n", baseAddr, npages, topAddr);
2840 assert(baseAddr + npages * PAGESIZE == topAddr);
2841 assert(ncommitted <= npages);
2842 }
2843
2844 for (size_t i = 0; i < npages; i++)
2845 { Bins bin = cast(Bins)pagetable[i];
2846
2847 assert(bin < B_MAX);
2848 }
2849 }
2850
2851
2852 /**
2853 * Allocate n pages from Pool.
2854 * Returns OPFAIL on failure.
2855 */
2856 size_t allocPages(size_t n)
2857 {
2858 size_t i;
2859 size_t n2;
2860
2861 //debug(PRINTF) printf("Pool::allocPages(n = %d)\n", n);
2862 n2 = n;
2863 for (i = 0; i < ncommitted; i++)
2864 {
2865 if (pagetable[i] == B_FREE)
2866 {
2867 if (--n2 == 0)
2868 { //debug(PRINTF) printf("\texisting pn = %d\n", i - n + 1);
2869 return i - n + 1;
2870 }
2871 }
2872 else
2873 n2 = n;
2874 }
2875 return extendPages(n);
2876 }
2877
2878 /**
2879 * Extend Pool by n pages.
2880 * Returns OPFAIL on failure.
2881 */
2882 size_t extendPages(size_t n)
2883 {
2884 //debug(PRINTF) printf("Pool::extendPages(n = %d)\n", n);
2885 if (ncommitted + n <= npages)
2886 {
2887 size_t tocommit;
2888
2889 tocommit = (n + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1);
2890 if (ncommitted + tocommit > npages)
2891 tocommit = npages - ncommitted;
2892 //debug(PRINTF) printf("\tlooking to commit %d more pages\n", tocommit);
2893 //fflush(stdout);
2894 if (os_mem_commit(baseAddr, ncommitted * PAGESIZE, tocommit * PAGESIZE) == 0)
2895 {
2896 memset(pagetable + ncommitted, B_FREE, tocommit);
2897 auto i = ncommitted;
2898 ncommitted += tocommit;
2899
2900 while (i && pagetable[i - 1] == B_FREE)
2901 i--;
2902
2903 return i;
2904 }
2905 //debug(PRINTF) printf("\tfailed to commit %d pages\n", tocommit);
2906 }
2907
2908 return OPFAIL;
2909 }
2910
2911
2912 /**
2913 * Free npages pages starting with pagenum.
2914 */
2915 void freePages(size_t pagenum, size_t npages)
2916 {
2917 memset(&pagetable[pagenum], B_FREE, npages);
2918 }
2919
2920
2921 /**
2922 * Used for sorting pooltable[]
2923 */
2924 int opCmp(Pool *p2)
2925 {
2926 if (baseAddr < p2.baseAddr)
2927 return -1;
2928 else
2929 return cast(int)(baseAddr > p2.baseAddr);
2930 }
2931 }
2932
2933
2934 /* ============================ SENTINEL =============================== */
2935
2936
2937 version (SENTINEL)
2938 {
2939 const size_t SENTINEL_PRE = cast(size_t) 0xF4F4F4F4F4F4F4F4UL; // 32 or 64 bits
2940 const ubyte SENTINEL_POST = 0xF5; // 8 bits
2941 const uint SENTINEL_EXTRA = 2 * size_t.sizeof + 1;
2942
2943
2944 size_t* sentinel_size(void *p) { return &(cast(size_t *)p)[-2]; }
2945 size_t* sentinel_pre(void *p) { return &(cast(size_t *)p)[-1]; }
2946 ubyte* sentinel_post(void *p) { return &(cast(ubyte *)p)[*sentinel_size(p)]; }
2947
2948
2949 void sentinel_init(void *p, size_t size)
2950 {
2951 *sentinel_size(p) = size;
2952 *sentinel_pre(p) = SENTINEL_PRE;
2953 *sentinel_post(p) = SENTINEL_POST;
2954 }
2955
2956
2957 void sentinel_Invariant(void *p)
2958 {
2959 assert(*sentinel_pre(p) == SENTINEL_PRE);
2960 assert(*sentinel_post(p) == SENTINEL_POST);
2961 }
2962
2963
2964 void *sentinel_add(void *p)
2965 {
2966 return p + 2 * size_t.sizeof;
2967 }
2968
2969
2970 void *sentinel_sub(void *p)
2971 {
2972 return p - 2 * size_t.sizeof;
2973 }
2974 }
2975 else
2976 {
2977 const uint SENTINEL_EXTRA = 0;
2978
2979
2980 void sentinel_init(void *p, size_t size)
2981 {
2982 }
2983
2984
2985 void sentinel_Invariant(void *p)
2986 {
2987 }
2988
2989
2990 void *sentinel_add(void *p)
2991 {
2992 return p;
2993 }
2994
2995
2996 void *sentinel_sub(void *p)
2997 {
2998 return p;
2999 }
3000 }