comparison druntime/src/gc/basic/gcx.d @ 1458:e0b2d67cfe7c

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