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