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