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