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