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