Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

root/tags/releases/0.99.9/tango/core/rt/gc/basic/gcx.d

Revision 5299, 79.3 kB (checked in by kris, 2 years ago)

made some changes recommended by Tom :: http://dsource.org/projects/tango/ticket/1038#comment:13

  • Property svn:eol-style set to native
Line 
1 /**
2  * This module contains the garbage collector implementation.
3  *
4  * Copyright: Copyright (C) 2001-2007 Digital Mars, www.digitalmars.com.
5  *            All rights reserved.
6  * License:
7  *  This software is provided 'as-is', without any express or implied
8  *  warranty. In no event will the authors be held liable for any damages
9  *  arising from the use of this software.
10  *
11  *  Permission is granted to anyone to use this software for any purpose,
12  *  including commercial applications, and to alter it and redistribute it
13  *  freely, in both source and binary form, subject to the following
14  *  restrictions:
15  *
16  *  o  The origin of this software must not be misrepresented; you must not
17  *     claim that you wrote the original software. If you use this software
18  *     in a product, an acknowledgment in the product documentation would be
19  *     appreciated but is not required.
20  *  o  Altered source versions must be plainly marked as such, and must not
21  *     be misrepresented as being the original software.
22  *  o  This notice may not be removed or altered from any source
23  *     distribution.
24  * Authors:   Walter Bright, David Friedman, Sean Kelly
25  */
26 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;
56 debug(PRINTF) private import tango.stdc.posix.pthread : pthread_self, pthread_t;
57 debug private import tango.stdc.stdio : printf;
58
59 version (GNU)
60 {
61     // BUG: The following import will likely not work, since the gcc
62     //      subdirectory is elsewhere.  Instead, perhaps the functions
63     //      could be declared directly or some other resolution could
64     //      be found.
65     private import gcc.builtins; // for __builtin_unwind_init
66 }
67
68
69 struct BlkInfo
70 {
71     void*  base;
72     size_t size;
73     uint   attr;
74 }
75
76 private
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_detachDisposeEvent(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     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     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
278     /**
279      *
280      */
281     void enable()
282     {
283         if (!thread_needLock())
284         {
285             assert(gcx.disabled > 0);
286             gcx.disabled--;
287         }
288         else synchronized (gcLock)
289         {
290             assert(gcx.disabled > 0);
291             gcx.disabled--;
292         }
293     }
294
295
296     /**
297      *
298      */
299     void disable()
300     {
301         if (!thread_needLock())
302         {
303             gcx.disabled++;
304         }
305         else synchronized (gcLock)
306         {
307             gcx.disabled++;
308         }
309     }
310
311
312     /**
313      *
314      */
315     uint getAttr(void* p)
316     {
317         if (!p)
318         {
319             return 0;
320         }
321
322         uint go()
323         {
324             Pool* pool = gcx.findPool(p);
325             uint  oldb = 0;
326
327             if (pool)
328             {
329                 auto biti = cast(size_t)(p - pool.baseAddr) / 16;
330
331                 oldb = gcx.getBits(pool, biti);
332             }
333             return oldb;
334         }
335
336         if (!thread_needLock())
337         {
338             return go();
339         }
340         else synchronized (gcLock)
341         {
342             return go();
343         }
344     }
345
346
347     /**
348      *
349      */
350     uint setAttr(void* p, uint mask)
351     {
352         if (!p)
353         {
354             return 0;
355         }
356
357         uint go()
358         {
359             Pool* pool = gcx.findPool(p);
360             uint  oldb = 0;
361
362             if (pool)
363             {
364                 auto biti = cast(size_t)(p - pool.baseAddr) / 16;
365
366                 oldb = gcx.getBits(pool, biti);
367                 gcx.setBits(pool, biti, mask);
368             }
369             return oldb;
370         }
371
372         if (!thread_needLock())
373         {
374             return go();
375         }
376         else synchronized (gcLock)
377         {
378             return go();
379         }
380     }
381
382
383     /**
384      *
385      */
386     uint clrAttr(void* p, uint mask)
387     {
388         if (!p)
389         {
390             return 0;
391         }
392
393         uint go()
394         {
395             Pool* pool = gcx.findPool(p);
396             uint  oldb = 0;
397
398             if (pool)
399             {
400                 auto biti = cast(size_t)(p - pool.baseAddr) / 16;
401
402                 oldb = gcx.getBits(pool, biti);
403                 gcx.clrBits(pool, biti, mask);
404             }
405             return oldb;
406         }
407
408         if (!thread_needLock())
409         {
410             return go();
411         }
412         else synchronized (gcLock)
413         {
414             return go();
415         }
416     }
417
418
419     /**
420      *
421      */
422     void *malloc(size_t size, uint bits = 0)
423     {
424         if (!size)
425         {
426             return null;
427         }
428
429         if (!thread_needLock())
430         {
431             return mallocNoSync(size, bits);
432         }
433         else synchronized (gcLock)
434         {
435             return mallocNoSync(size, bits);
436         }
437     }
438
439
440     //
441     //
442     //
443     private void *mallocNoSync(size_t size, uint bits = 0)
444     {
445         assert(size != 0);
446
447         void *p = null;
448         Bins bin;
449
450         //debug(PRINTF) printf("GC::malloc(size = %d, gcx = %p)\n", size, gcx);
451         assert(gcx);
452         //debug(PRINTF) printf("gcx.self = %x, pthread_self() = %x\n", gcx.self, pthread_self());
453
454         size += SENTINEL_EXTRA;
455
456         // Compute size bin
457         // Cache previous binsize lookup - Dave Fladebo.
458         static size_t lastsize = -1;
459         static Bins lastbin;
460         if (size == lastsize)
461             bin = lastbin;
462         else
463         {
464             bin = gcx.findBin(size);
465             lastsize = size;
466             lastbin = bin;
467         }
468
469         if (bin < B_PAGE)
470         {
471             p = gcx.bucket[bin];
472             if (p is null)
473             {
474                 if (!gcx.allocPage(bin) && !gcx.disabled)   // try to find a new page
475                 {
476                     if (!thread_needLock())
477                     {
478                         /* Then we haven't locked it yet. Be sure
479                          * and lock for a collection, since a finalizer
480                          * may start a new thread.
481                          */
482                         synchronized (gcLock)
483                         {
484                             gcx.fullcollectshell();
485                         }
486                     }
487                     else if (!gcx.fullcollectshell())       // collect to find a new page
488                     {
489                         //gcx.newPool(1);
490                     }
491                 }
492                 if (!gcx.bucket[bin] && !gcx.allocPage(bin))
493                 {   int result;
494
495                     gcx.newPool(1);         // allocate new pool to find a new page
496                     result = gcx.allocPage(bin);
497                     if (!result)
498                         onOutOfMemoryError();
499                 }
500                 p = gcx.bucket[bin];
501             }
502
503             // Return next item from free list
504             gcx.bucket[bin] = (cast(List*)p).next;
505             if( !(bits & BlkAttr.NO_SCAN) )
506                 cstring.memset(p + size, 0, binsize[bin] - size);
507             //debug(PRINTF) printf("\tmalloc => %x\n", p);
508             debug (MEMSTOMP) cstring.memset(p, 0xF0, size);
509         }
510         else
511         {
512             p = gcx.bigAlloc(size);
513             if (!p)
514                 onOutOfMemoryError();
515         }
516         size -= SENTINEL_EXTRA;
517         p = sentinel_add(p);
518         sentinel_init(p, size);
519         gcx.log_malloc(p, size);
520
521         if (bits)
522         {
523             Pool *pool = gcx.findPool(p);
524             assert(pool);
525
526             gcx.setBits(pool, cast(size_t)(p - pool.baseAddr) / 16, bits);
527         }
528         return p;
529     }
530
531
532     /**
533      *
534      */
535     void *calloc(size_t size, uint bits = 0)
536     {
537         if (!size)
538         {
539             return null;
540         }
541
542         if (!thread_needLock())
543         {
544             return callocNoSync(size, bits);
545         }
546         else synchronized (gcLock)
547         {
548             return callocNoSync(size, bits);
549         }
550     }
551
552
553     //
554     //
555     //
556     private void *callocNoSync(size_t size, uint bits = 0)
557     {
558         assert(size != 0);
559
560         //debug(PRINTF) printf("calloc: %x len %d\n", p, len);
561         void *p = mallocNoSync(size, bits);
562         cstring.memset(p, 0, size);
563         return p;
564     }
565
566
567     /**
568      *
569      */
570     void *realloc(void *p, size_t size, uint bits = 0)
571     {
572         if (!thread_needLock())
573         {
574             return reallocNoSync(p, size, bits);
575         }
576         else synchronized (gcLock)
577         {
578             return reallocNoSync(p, size, bits);
579         }
580     }
581
582
583     //
584     //
585     //
586     private void *reallocNoSync(void *p, size_t size, uint bits = 0)
587     {
588         if (!size)
589         {   if (p)
590             {   freeNoSync(p);
591                 p = null;
592             }
593         }
594         else if (!p)
595         {
596             p = mallocNoSync(size, bits);
597         }
598         else
599         {   void *p2;
600             size_t psize;
601
602             //debug(PRINTF) printf("GC::realloc(p = %x, size = %u)\n", p, size);
603             version (SENTINEL)
604             {
605                 sentinel_Invariant(p);
606                 psize = *sentinel_size(p);
607                 if (psize != size)
608                 {
609                     if (psize)
610                     {
611                         Pool *pool = gcx.findPool(p);
612
613                         if (pool)
614                         {
615                             auto biti = cast(size_t)(p - pool.baseAddr) / 16;
616
617                             if (bits)
618                             {
619                                 gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
620                                 gcx.setBits(pool, biti, bits);
621                             }
622                             else
623                             {
624                                 bits = gcx.getBits(pool, biti);
625                             }
626                         }
627                     }
628                     p2 = mallocNoSync(size, bits);
629                     if (psize < size)
630                         size = psize;
631                     //debug(PRINTF) printf("\tcopying %d bytes\n",size);
632                     cstring.memcpy(p2, p, size);
633                     p = p2;
634                 }
635             }
636             else
637             {
638                 psize = gcx.findSize(p);        // find allocated size
639                 if (psize >= PAGESIZE && size >= PAGESIZE)
640                 {
641                     auto psz = psize / PAGESIZE;
642                     auto newsz = (size + PAGESIZE - 1) / PAGESIZE;
643                     if (newsz == psz)
644                         return p;
645
646                     auto pool = gcx.findPool(p);
647                     auto pagenum = (p - pool.baseAddr) / PAGESIZE;
648
649                     if (newsz < psz)
650                     {   // Shrink in place
651                         synchronized (gcLock)
652                         {
653                             debug (MEMSTOMP) cstring.memset(p + size, 0xF2, psize - size);
654                             pool.freePages(pagenum + newsz, psz - newsz);
655                         }
656                         return p;
657                     }
658                     else if (pagenum + newsz <= pool.npages)
659                     {
660                         // Attempt to expand in place
661                         synchronized (gcLock)
662                         {
663                             for (size_t i = pagenum + psz; 1;)
664                             {
665                                 if (i == pagenum + newsz)
666                                 {
667                                     debug (MEMSTOMP) cstring.memset(p + psize, 0xF0, size - psize);
668                                     cstring.memset(&pool.pagetable[pagenum + psz], B_PAGEPLUS, newsz - psz);
669                                     return p;
670                                 }
671                                 if (i == pool.ncommitted)
672                                 {
673                                     auto u = pool.extendPages(pagenum + newsz - pool.ncommitted);
674                                     if (u == OPFAIL)
675                                         break;
676                                     i = pagenum + newsz;
677                                     continue;
678                                 }
679                                 if (pool.pagetable[i] != B_FREE)
680                                     break;
681                                 i++;
682                             }
683                         }
684                     }
685                 }
686                 if (psize < size ||             // if new size is bigger
687                     psize > size * 2)           // or less than half
688                 {
689                     if (psize)
690                     {
691                         Pool *pool = gcx.findPool(p);
692
693                         if (pool)
694                         {
695                             auto biti = cast(size_t)(p - pool.baseAddr) / 16;
696
697                             if (bits)
698                             {
699                                 gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
700                                 gcx.setBits(pool, biti, bits);
701                             }
702                             else
703                             {
704                                 bits = gcx.getBits(pool, biti);
705                             }
706                         }
707                     }
708                     p2 = mallocNoSync(size, bits);
709                     if (psize < size)
710                         size = psize;
711                     //debug(PRINTF) printf("\tcopying %d bytes\n",size);
712                     cstring.memcpy(p2, p, size);
713                     p = p2;
714                 }
715             }
716         }
717         return p;
718     }
719
720
721     /**
722      * Attempt to in-place enlarge the memory block pointed to by p by at least
723      * minbytes beyond its current capacity, up to a maximum of maxsize.  This
724      * does not attempt to move the memory block (like realloc() does).
725      *
726      * Returns:
727      *  0 if could not extend p,
728      *  total size of entire memory block if successful.
729      */
730     size_t extend(void* p, size_t minsize, size_t maxsize)
731     {
732         if (!thread_needLock())
733         {
734             return extendNoSync(p, minsize, maxsize);
735         }
736         else synchronized (gcLock)
737         {
738             return extendNoSync(p, minsize, maxsize);
739         }
740     }
741
742
743     //
744     //
745     //
746     private size_t extendNoSync(void* p, size_t minsize, size_t maxsize)
747     in
748     {
749         assert( minsize <= maxsize );
750     }
751     body
752     {
753         //debug(PRINTF) printf("GC::extend(p = %x, minsize = %u, maxsize = %u)\n", p, minsize, maxsize);
754         version (SENTINEL)
755         {
756             return 0;
757         }
758         auto psize = gcx.findSize(p);   // find allocated size
759         if (psize < PAGESIZE)
760             return 0;                   // cannot extend buckets
761
762         auto psz = psize / PAGESIZE;
763         auto minsz = (minsize + PAGESIZE - 1) / PAGESIZE;
764         auto maxsz = (maxsize + PAGESIZE - 1) / PAGESIZE;
765
766         auto pool = gcx.findPool(p);
767         auto pagenum = (p - pool.baseAddr) / PAGESIZE;
768
769         size_t sz;
770         for (sz = 0; sz < maxsz; sz++)
771         {
772             auto i = pagenum + psz + sz;
773             if (i == pool.ncommitted)
774                 break;
775             if (pool.pagetable[i] != B_FREE)
776             {   if (sz < minsz)
777                     return 0;
778                 break;
779             }
780         }
781         if (sz >= minsz)
782         {
783         }
784         else if (pagenum + psz + sz == pool.ncommitted)
785         {
786             auto u = pool.extendPages(minsz - sz);
787             if (u == OPFAIL)
788                 return 0;
789             sz = minsz;
790         }
791         else
792             return 0;
793         debug (MEMSTOMP) cstring.memset(p + psize, 0xF0, (psz + sz) * PAGESIZE - psize);
794         cstring.memset(pool.pagetable + pagenum + psz, B_PAGEPLUS, sz);
795         gcx.p_cache = null;
796         gcx.size_cache = 0;
797         return (psz + sz) * PAGESIZE;
798     }
799
800
801     /**
802      *
803      */
804     size_t reserve(size_t size)
805     {
806         if (!size)
807         {
808             return 0;
809         }
810
811         if (!thread_needLock())
812         {
813             return reserveNoSync(size);
814         }
815         else synchronized (gcLock)
816         {
817             return reserveNoSync(size);
818         }
819     }
820
821
822     //
823     //
824     //
825     private size_t reserveNoSync(size_t size)
826     {
827         assert(size != 0);
828         assert(gcx);
829
830         return gcx.reserve(size);
831     }
832
833
834     /**
835      *
836      */
837     void free(void *p)
838     {
839         if (!p)
840         {
841             return;
842         }
843
844         if (!thread_needLock())
845         {
846             return freeNoSync(p);
847         }
848         else synchronized (gcLock)
849         {
850             return freeNoSync(p);
851         }
852     }
853
854
855     //
856     //
857     //
858     private void freeNoSync(void *p)
859     {
860         assert (p);
861
862         Pool*  pool;
863         size_t pagenum;
864         Bins   bin;
865         size_t biti;
866
867         // Find which page it is in
868         pool = gcx.findPool(p);
869         if (!pool)                              // if not one of ours
870             return;                             // ignore
871         sentinel_Invariant(p);
872         p = sentinel_sub(p);
873         pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE;
874         biti = cast(size_t)(p - pool.baseAddr) / 16;
875         gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
876
877         bin = cast(Bins)pool.pagetable[pagenum];
878         if (bin == B_PAGE)              // if large alloc
879         {   size_t npages;
880             size_t n;
881
882             // Free pages
883             npages = 1;
884             n = pagenum;
885             while (++n < pool.ncommitted && pool.pagetable[n] == B_PAGEPLUS)
886                 npages++;
887             debug (MEMSTOMP) cstring.memset(p, 0xF2, npages * PAGESIZE);
888             pool.freePages(pagenum, npages);
889         }
890         else
891         {   // Add to free list
892             List *list = cast(List*)p;
893
894             debug (MEMSTOMP) cstring.memset(p, 0xF2, binsize[bin]);
895
896             list.next = gcx.bucket[bin];
897             gcx.bucket[bin] = list;
898         }
899         gcx.log_free(sentinel_add(p));
900     }
901
902
903     /**
904      * Determine the base address of the block containing p.  If p is not a gc
905      * allocated pointer, return null.
906      */
907     void* addrOf(void *p)
908     {
909         if (!p)
910         {
911             return null;
912         }
913
914         if (!thread_needLock())
915         {
916             return addrOfNoSync(p);
917         }
918         else synchronized (gcLock)
919         {
920             return addrOfNoSync(p);
921         }
922     }
923
924
925     //
926     //
927     //
928     void* addrOfNoSync(void *p)
929     {
930         if (!p)
931         {
932             return null;
933         }
934
935         return gcx.findBase(p);
936     }
937
938
939     /**
940      * Determine the allocated size of pointer p.  If p is an interior pointer
941      * or not a gc allocated pointer, return 0.
942      */
943     size_t sizeOf(void *p)
944     {
945         if (!p)
946         {
947             return 0;
948         }
949
950         if (!thread_needLock())
951         {
952             return sizeOfNoSync(p);
953         }
954         else synchronized (gcLock)
955         {
956             return sizeOfNoSync(p);
957         }
958     }
959
960
961     //
962     //
963     //
964     private size_t sizeOfNoSync(void *p)
965     {
966         assert (p);
967
968         version (SENTINEL)
969         {
970             p = sentinel_sub(p);
971             size_t size = gcx.findSize(p);
972
973             // Check for interior pointer
974             // This depends on:
975             // 1) size is a power of 2 for less than PAGESIZE values
976             // 2) base of memory pool is aligned on PAGESIZE boundary
977             if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
978                 size = 0;
979             return size ? size - SENTINEL_EXTRA : 0;
980         }
981         else
982         {
983             if (p == gcx.p_cache)
984                 return gcx.size_cache;
985
986             size_t size = gcx.findSize(p);
987
988             // Check for interior pointer
989             // This depends on:
990             // 1) size is a power of 2 for less than PAGESIZE values
991             // 2) base of memory pool is aligned on PAGESIZE boundary
992             if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
993                 size = 0;
994             else
995             {
996                 gcx.p_cache = p;
997                 gcx.size_cache = size;
998             }
999
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     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     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     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     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     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     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     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     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     void fullCollectNoStack()
1249     {
1250         if (!thread_needLock())
1251         {
1252             gcx.noStack++;
1253             gcx.fullcollectshell();
1254             gcx.noStack--;
1255         }
1256         else synchronized (gcLock)
1257         {
1258             gcx.noStack++;
1259             gcx.fullcollectshell();
1260             gcx.noStack--;
1261         }
1262     }
1263
1264
1265     /**
1266      * minimize free space usage
1267      */
1268     void minimize()
1269     {
1270         if (!thread_needLock())
1271         {
1272             gcx.minimize();
1273         }
1274         else synchronized (gcLock)
1275         {
1276             gcx.minimize();
1277         }
1278     }
1279
1280
1281     /**
1282      * Retrieve statistics about garbage collection.
1283      * Useful for debugging and tuning.
1284      */
1285     void getStats(out GCStats stats)
1286     {
1287         if (!thread_needLock())
1288         {
1289             getStatsNoSync(stats);
1290         }
1291         else synchronized (gcLock)
1292         {
1293             getStatsNoSync(stats);
1294         }
1295     }
1296
1297
1298     //
1299     //
1300     //
1301     private void getStatsNoSync(out GCStats stats)
1302     {
1303         size_t psize = 0;
1304         size_t usize = 0;
1305         size_t flsize = 0;
1306
1307         size_t n;
1308         size_t bsize = 0;
1309
1310         //debug(PRINTF) printf("getStats()\n");
1311         cstring.memset(&stats, 0, GCStats.sizeof);
1312
1313         for (n = 0; n < gcx.npools; n++)
1314         {   Pool *pool = gcx.pooltable[n];
1315
1316             psize += pool.ncommitted * PAGESIZE;
1317             for (size_t j = 0; j < pool.ncommitted; j++)
1318             {
1319                 Bins bin = cast(Bins)pool.pagetable[j];
1320                 if (bin == B_FREE)
1321                     stats.freeblocks++;
1322                 else if (bin == B_PAGE)
1323                     stats.pageblocks++;
1324                 else if (bin < B_PAGE)
1325                     bsize += PAGESIZE;
1326             }
1327         }
1328
1329         for (n = 0; n < B_PAGE; n++)
1330         {
1331             //debug(PRINTF) printf("bin %d\n", n);
1332             for (List *list = gcx.bucket[n]; list; list = list.next)
1333             {
1334                 //debug(PRINTF) printf("\tlist %x\n", list);
1335                 flsize += binsize[n];
1336             }
1337         }
1338
1339         usize = bsize - flsize;
1340
1341         stats.poolsize = psize;
1342         stats.usedsize = bsize - flsize;
1343         stats.freelistsize = flsize;
1344     }
1345
1346     /******************* weak-reference support *********************/
1347
1348     //call locked if necessary
1349     private T locked(T)(in T delegate() code)
1350     {
1351         if (thread_needLock)
1352             synchronized(gcLock) return code();
1353         else
1354            return code();
1355     }
1356
1357     private struct WeakPointer
1358     {
1359         Object reference;
1360
1361         void ondestroy(Object r)
1362         {
1363             assert(r is reference);
1364             //lock for memory consistency (parallel readers)
1365             //
1366             //also ensures that weakpointerDestroy can be called while another
1367             //thread is freeing the reference with "delete"                   
1368             locked!(void)({ reference = null; });
1369         }
1370     }
1371
1372     /**
1373      * Create a weak pointer to the given object.
1374      * Returns a pointer to an opaque struct allocated in C memory.
1375      */
1376     void* weakpointerCreate( Object r )
1377     {
1378         if (r)
1379            {
1380            //must be allocated in C memory
1381            //1. to hide the reference from the GC
1382            //2. the GC doesn't scan delegates added by rt_attachDisposeEvent for references
1383            auto wp = cast(WeakPointer*)(.malloc(WeakPointer.sizeof));
1384            if (!wp)
1385                onOutOfMemoryError();
1386            wp.reference = r;
1387            rt_attachDisposeEvent(r, &wp.ondestroy);
1388            return wp;
1389            }
1390         return null;
1391     }
1392
1393     /**
1394      * Destroy a weak pointer returned by weakpointerCreate().
1395      * If null is passed, nothing happens.
1396      */
1397     void weakpointerDestroy( void* p )
1398     {
1399         if (p)       
1400            {
1401            auto wp = cast(WeakPointer*)p;
1402            //must be extra careful about the GC or parallel threads finalizing the
1403            //reference at the same time
1404            locked!(void)({
1405                    if (wp.reference)
1406                        rt_detachDisposeEvent(wp.reference, &wp.ondestroy);
1407                   });
1408            .free(wp);
1409            }
1410     }
1411
1412     /**
1413      * Query a weak pointer and return either the object passed to
1414      * weakpointerCreate, or null if it was free'd in the meantime.
1415      * If null is passed, null is returned.
1416      */
1417     Object weakpointerGet( void* p )
1418     {
1419         if (p)
1420            {
1421            //NOTE: could avoid the lock by using Fawzi style GC counters
1422            // but that'd require core.sync.Atomic and lots of care about memory consistency
1423            // it's an optional optimization
1424            //see http://dsource.org/projects/tango/browser/trunk/user/tango/core/Lifetime.d?rev=5100#L158
1425            return locked!(Object)({
1426                   return (cast(WeakPointer*)p).reference;
1427                   });
1428            }
1429     }
1430 }
1431
1432
1433 /* ============================ Gcx =============================== */
1434
1435 enum
1436 {   PAGESIZE =    4096,
1437     COMMITSIZE = (4096*16),
1438     POOLSIZE =   (4096*256),
1439 }
1440
1441
1442 enum
1443 {
1444     B_16,
1445     B_32,
1446     B_64,
1447     B_128,
1448     B_256,
1449     B_512,
1450     B_1024,
1451     B_2048,
1452     B_PAGE,             // start of large alloc
1453     B_PAGEPLUS,         // continuation of large alloc
1454     B_FREE,             // free page
1455     B_UNCOMMITTED,      // memory not committed for this page
1456     B_MAX
1457 }
1458
1459
1460 alias ubyte Bins;
1461
1462
1463 struct List
1464 {
1465     List *next;
1466 }
1467
1468
1469 struct Range
1470 {
1471     void *pbot;
1472     void *ptop;
1473 }
1474
1475
1476 const uint binsize[B_MAX] = [ 16,32,64,128,256,512,1024,2048,4096 ];
1477 const uint notbinsize[B_MAX] = [ ~(16u-1),~(32u-1),~(64u-1),~(128u-1),~(256u-1),
1478                                 ~(512u-1),~(1024u-1),~(2048u-1),~(4096u-1) ];
1479
1480 /* ============================ Gcx =============================== */
1481
1482
1483 struct Gcx
1484 {
1485     debug (THREADINVARIANT)
1486     {
1487         pthread_t self;
1488         void thread_Invariant()
1489         {
1490             if (self != pthread_self())
1491                 printf("thread_Invariant(): gcx = %x, self = %x, pthread_self() = %x\n", this, self, pthread_self());
1492             assert(self == pthread_self());
1493         }
1494     }
1495     else
1496     {
1497         void thread_Invariant() { }
1498     }
1499
1500     void *p_cache;
1501     size_t size_cache;
1502
1503     size_t nroots;
1504     size_t rootdim;
1505     void **roots;
1506
1507     size_t nranges;
1508     size_t rangedim;
1509     Range *ranges;
1510
1511     uint noStack;       // !=0 means don't scan stack
1512     uint log;           // turn on logging
1513     uint anychanges;
1514     void *stackBottom;
1515     uint inited;
1516     int disabled;       // turn off collections if >0
1517
1518     byte *minAddr;      // min(baseAddr)
1519     byte *maxAddr;      // max(topAddr)
1520
1521     size_t npools;
1522     Pool **pooltable;
1523
1524     List *bucket[B_MAX];        // free list for each size
1525
1526
1527     void initialize()
1528     {   int dummy;
1529
1530         (cast(byte*)this)[0 .. Gcx.sizeof] = 0;
1531         stackBottom = cast(char*)&dummy;
1532         log_init();
1533         debug (THREADINVARIANT)
1534             self = pthread_self();
1535         //printf("gcx = %p, self = %x\n", this, self);
1536         inited = 1;
1537     }
1538
1539
1540     void Dtor()
1541     {
1542         inited = 0;
1543
1544         for (size_t i = 0; i < npools; i++)
1545         {   Pool *pool = pooltable[i];
1546
1547             pool.Dtor();
1548             cstdlib.free(pool);
1549         }
1550         if (pooltable)
1551             cstdlib.free(pooltable);
1552
1553         if (roots)
1554             cstdlib.free(roots);
1555
1556         if (ranges)
1557             cstdlib.free(ranges);
1558     }
1559
1560
1561     void Invariant() { }
1562
1563
1564     invariant
1565     {
1566         if (inited)
1567         {
1568         //printf("Gcx.invariant(): this = %p\n", this);
1569             size_t i;
1570
1571             // Assure we're called on the right thread
1572             debug (THREADINVARIANT) assert(self == pthread_self());
1573
1574             for (i = 0; i < npools; i++)
1575             {   Pool *pool = pooltable[i];
1576
1577                 pool.Invariant();
1578                 if (i == 0)
1579                 {
1580                     assert(minAddr == pool.baseAddr);
1581                 }
1582                 if (i + 1 < npools)
1583                 {
1584                     assert(pool.opCmp(pooltable[i + 1]) < 0);
1585                 }
1586                 else if (i + 1 == npools)
1587                 {
1588                     assert(maxAddr == pool.topAddr);
1589                 }
1590             }
1591
1592             if (roots)
1593             {
1594                 assert(rootdim != 0);
1595                 assert(nroots <= rootdim);
1596             }
1597
1598             if (ranges)
1599             {
1600                 assert(rangedim != 0);
1601                 assert(nranges <= rangedim);
1602
1603                 for (i = 0; i < nranges; i++)
1604                 {
1605                     assert(ranges[i].pbot);
1606                     assert(ranges[i].ptop);
1607                     assert(ranges[i].pbot <= ranges[i].ptop);
1608                 }
1609             }
1610
1611             for (i = 0; i < B_PAGE; i++)
1612             {
1613                 for (List *list = bucket[i]; list; list = list.next)
1614                 {
1615                 }
1616             }
1617         }
1618     }
1619
1620
1621     /**
1622      *
1623      */
1624     void addRoot(void *p)
1625     {
1626         if (nroots == rootdim)
1627         {
1628             size_t newdim = rootdim * 2 + 16;
1629             void** newroots;
1630
1631             newroots = cast(void**)cstdlib.malloc(newdim * newroots[0].sizeof);
1632             if (!newroots)
1633                 onOutOfMemoryError();
1634             if (roots)
1635             {   cstring.memcpy(newroots, roots, nroots * newroots[0].sizeof);
1636                 cstdlib.free(roots);
1637             }
1638             roots = newroots;
1639             rootdim = newdim;
1640         }
1641         roots[nroots] = p;
1642         nroots++;
1643     }
1644
1645
1646     /**
1647      *
1648      */
1649     void removeRoot(void *p)
1650     {
1651         for (size_t i = nroots; i--;)
1652         {
1653             if (roots[i] == p)
1654             {
1655                 nroots--;
1656                 cstring.memmove(roots + i, roots + i + 1, (nroots - i) * roots[0].sizeof);
1657                 return;
1658             }
1659         }
1660         assert(0);
1661     }
1662
1663
1664     /**
1665      *
1666      */
1667     void addRange(void *pbot, void *ptop)
1668     {
1669         debug(PRINTF) printf("Thread %x ", pthread_self());
1670         debug(PRINTF) printf("%x.Gcx::addRange(%x, %x), nranges = %d\n", this, pbot, ptop, nranges);
1671         if (nranges == rangedim)
1672         {
1673             size_t newdim = rangedim * 2 + 16;
1674             Range *newranges;
1675
1676             newranges = cast(Range*)cstdlib.malloc(newdim * newranges[0].sizeof);
1677             if (!newranges)
1678                 onOutOfMemoryError();
1679             if (ranges)
1680             {   cstring.memcpy(newranges, ranges, nranges * newranges[0].sizeof);
1681                 cstdlib.free(ranges);
1682             }
1683             ranges = newranges;
1684             rangedim = newdim;
1685         }
1686         ranges[nranges].pbot = pbot;
1687         ranges[nranges].ptop = ptop;
1688         nranges++;
1689     }
1690
1691
1692     /**
1693      *
1694      */
1695     void removeRange(void *pbot)
1696     {
1697         debug(PRINTF) printf("Thread %x ", pthread_self());
1698         debug(PRINTF) printf("%x.Gcx.removeRange(%x), nranges = %d\n", this, pbot, nranges);
1699         for (size_t i = nranges; i--;)
1700         {
1701             if (ranges[i].pbot == pbot)
1702             {
1703                 nranges--;
1704                 cstring.memmove(ranges + i, ranges + i + 1, (nranges - i) * ranges[0].sizeof);
1705                 return;
1706             }
1707         }
1708         debug(PRINTF) printf("Wrong thread\n");
1709
1710         // This is a fatal error, but ignore it.
1711         // The problem is that we can get a Close() call on a thread
1712         // other than the one the range was allocated on.
1713         //assert(zero);
1714     }
1715
1716
1717     /**
1718      * Find Pool that pointer is in.
1719      * Return null if not in a Pool.
1720      * Assume pooltable[] is sorted.
1721      */
1722     Pool *findPool(void *p)
1723     {
1724         if (p >= minAddr && p < maxAddr)
1725         {
1726             if (npools == 1)
1727             {
1728                 return pooltable[0];
1729             }
1730
1731             for (size_t i = 0; i < npools; i++)
1732             {   Pool *pool;
1733
1734                 pool = pooltable[i];
1735                 if (p < pool.topAddr)
1736                 {   if (pool.baseAddr <= p)
1737                         return pool;
1738                     break;
1739                 }
1740             }
1741         }
1742         return null;
1743     }
1744
1745
1746     /**
1747      * Find base address of block containing pointer p.
1748      * Returns null if not a gc'd pointer
1749      */
1750     void* findBase(void *p)
1751     {
1752         Pool *pool;
1753
1754         pool = findPool(p);
1755         if (pool)
1756         {
1757             size_t offset = cast(size_t)(p - pool.baseAddr);
1758             size_t pn = offset / PAGESIZE;
1759             Bins   bin = cast(Bins)pool.pagetable[pn];
1760
1761             // Adjust bit to be at start of allocated memory block
1762             if (bin <= B_PAGE)
1763             {
1764                 return pool.baseAddr + (offset & notbinsize[bin]);
1765             }
1766             else if (bin == B_PAGEPLUS)
1767             {
1768                 do
1769                 {   --pn, offset -= PAGESIZE;
1770                 } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
1771
1772                 return pool.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
1773             }
1774             else
1775             {
1776                 // we are in a B_FREE or B_UNCOMMITTED page
1777                 return null;
1778             }
1779         }
1780         return null;
1781     }
1782
1783
1784     /**
1785      * Find size of pointer p.
1786      * Returns 0 if not a gc'd pointer
1787      */
1788     size_t findSize(void *p)
1789     {
1790         Pool*  pool;
1791         size_t size = 0;
1792
1793         pool = findPool(p);
1794         if (pool)
1795         {
1796             size_t pagenum;
1797             Bins   bin;
1798
1799             pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE;
1800             bin = cast(Bins)pool.pagetable[pagenum];
1801             size = binsize[bin];
1802             if (bin == B_PAGE)
1803             {   size_t npages = pool.ncommitted;
1804                 ubyte* pt;
1805                 size_t i;
1806
1807                 pt = &pool.pagetable[0];
1808                 for (i = pagenum + 1; i < npages; i++)
1809                 {
1810                     if (pt[i] != B_PAGEPLUS)
1811                         break;
1812                 }
1813                 size = (i - pagenum) * PAGESIZE;
1814             }
1815         }
1816         return size;
1817     }
1818
1819
1820     /**
1821      *
1822      */
1823     BlkInfo getInfo(void* p)
1824     {
1825         Pool*   pool;
1826         BlkInfo info;
1827
1828         pool = findPool(p);
1829         if (pool)
1830         {
1831             size_t offset = cast(size_t)(p - pool.baseAddr);
1832             size_t pn = offset / PAGESIZE;
1833             Bins   bin = cast(Bins)pool.pagetable[pn];
1834
1835             ////////////////////////////////////////////////////////////////////
1836             // findAddr
1837             ////////////////////////////////////////////////////////////////////
1838
1839             if (bin <= B_PAGE)
1840             {
1841                 info.base = pool.baseAddr + (offset & notbinsize[bin]);
1842             }
1843             else if (bin == B_PAGEPLUS)
1844             {
1845                 do
1846                 {   --pn, offset -= PAGESIZE;
1847                 } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
1848
1849                 info.base = pool.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
1850
1851                 // fix bin for use by size calc below
1852                 bin = cast(Bins)pool.pagetable[pn];
1853             }
1854
1855             ////////////////////////////////////////////////////////////////////
1856             // findSize
1857             ////////////////////////////////////////////////////////////////////
1858
1859             info.size = binsize[bin];
1860             if (bin == B_PAGE)
1861             {   size_t npages = pool.ncommitted;
1862                 ubyte* pt;
1863                 size_t i;
1864
1865                 pt = &pool.pagetable[0];
1866                 for (i = pn + 1; i < npages; i++)
1867                 {
1868                     if (pt[i] != B_PAGEPLUS)
1869                         break;
1870                 }
1871                 info.size = (i - pn) * PAGESIZE;
1872             }
1873
1874             ////////////////////////////////////////////////////////////////////
1875             // getBits
1876             ////////////////////////////////////////////////////////////////////
1877
1878             info.attr = getBits(pool, cast(size_t)(offset / 16));
1879         }
1880         return info;
1881     }
1882
1883
1884     /**
1885      * Compute bin for size.
1886      */
1887     static Bins findBin(size_t size)
1888     {   Bins bin;
1889
1890         if (size <= 256)
1891         {
1892             if (size <= 64)
1893             {
1894                 if (size <= 16)
1895                     bin = B_16;
1896                 else if (size <= 32)
1897                     bin = B_32;
1898                 else
1899                     bin = B_64;
1900             }
1901             else
1902             {
1903                 if (size <= 128)
1904                     bin = B_128;
1905                 else
1906                     bin = B_256;
1907             }
1908         }
1909         else
1910         {
1911             if (size <= 1024)
1912             {
1913                 if (size <= 512)
1914                     bin = B_512;
1915                 else
1916                     bin = B_1024;
1917             }
1918             else
1919             {
1920                 if (size <= 2048)
1921                     bin = B_2048;
1922                 else
1923                     bin = B_PAGE;
1924             }
1925         }
1926         return bin;
1927     }
1928
1929
1930     /**
1931      * Allocate a new pool of at least size bytes.
1932      * Sort it into pooltable[].
1933      * Mark all memory in the pool as B_FREE.
1934      * Return the actual number of bytes reserved or 0 on error.
1935      */
1936     size_t reserve(size_t size)
1937     {
1938         size_t npages = (size + PAGESIZE - 1) / PAGESIZE;
1939         Pool*  pool = newPool(npages);
1940
1941         if (!pool || pool.extendPages(npages) == OPFAIL)
1942             return 0;
1943         return pool.ncommitted * PAGESIZE;
1944     }
1945
1946
1947     /**
1948      * Minimizes physical memory usage by returning free pools to the OS.
1949      */
1950     void minimize()
1951     {
1952         size_t n;
1953         size_t pn;
1954         Pool*  pool;
1955         size_t ncommitted;
1956
1957         for (n = 0; n < npools; n++)
1958         {
1959             pool = pooltable[n];
1960             ncommitted = pool.ncommitted;
1961             for (pn = 0; pn < ncommitted; pn++)
1962             {
1963                 if (cast(Bins)pool.pagetable[pn] != B_FREE)
1964                     break;
1965             }
1966             if (pn < ncommitted)
1967             {
1968                 n++;
1969                 continue;
1970             }
1971             pool.Dtor();
1972             cstdlib.free(pool);
1973             cstring.memmove(pooltable + n,
1974                             pooltable + n + 1,
1975                             (--npools - n) * (Pool*).sizeof);
1976             minAddr = pooltable[0].baseAddr;
1977             maxAddr = pooltable[npools - 1].topAddr;
1978         }
1979     }
1980
1981
1982     /**
1983      * Allocate a chunk of memory that is larger than a page.
1984      * Return null if out of memory.
1985      */
1986     void *bigAlloc(size_t size)
1987     {
1988         Pool*  pool;
1989         size_t npages;
1990         size_t n;
1991         size_t pn;
1992         size_t freedpages;
1993         void*  p;
1994         int    state;
1995
1996         npages = (size + PAGESIZE - 1) / PAGESIZE;
1997
1998         for (state = 0; ; )
1999         {
2000             // This code could use some refinement when repeatedly
2001             // allocating very large arrays.
2002
2003             for (n = 0; n < npools; n++)
2004             {
2005                 pool = pooltable[n];
2006                 pn = pool.allocPages(npages);
2007                 if (pn != OPFAIL)
2008                     goto L1;
2009             }
2010
2011             // Failed
2012             switch (state)
2013             {
2014             case 0:
2015                 if (disabled)
2016                 {   state = 1;
2017                     continue;
2018                 }
2019                 // Try collecting
2020                 freedpages = fullcollectshell();
2021                 if (freedpages >= npools * ((POOLSIZE / PAGESIZE) / 4))
2022                 {   state = 1;
2023                     continue;
2024                 }
2025                 // Release empty pools to prevent bloat
2026                 minimize();
2027                 // Allocate new pool
2028                 pool = newPool(npages);
2029                 if (!pool)
2030                 {   state = 2;
2031                     continue;
2032                 }
2033                 pn = pool.allocPages(npages);
2034                 assert(pn != OPFAIL);
2035                 goto L1;
2036             case 1:
2037                 // Release empty pools to prevent bloat
2038                 minimize();
2039                 // Allocate new pool
2040                 pool = newPool(npages);
2041                 if (!pool)
2042                     goto Lnomemory;
2043                 pn = pool.allocPages(npages);
2044                 assert(pn != OPFAIL);
2045                 goto L1;
2046             case 2:
2047                 goto Lnomemory;
2048             default:
2049                 assert(false);
2050             }
2051         }
2052
2053       L1:
2054         pool.pagetable[pn] = B_PAGE;
2055         if (npages > 1)
2056             cstring.memset(&pool.pagetable[pn + 1], B_PAGEPLUS, npages - 1);
2057         p = pool.baseAddr + pn * PAGESIZE;
2058         cstring.memset(cast(char *)p + size, 0, npages * PAGESIZE - size);
2059         debug (MEMSTOMP) cstring.memset(p, 0xF1, size);
2060         //debug(PRINTF) printf("\tp = %x\n", p);
2061         return p;
2062
2063       Lnomemory:
2064         return null; // let mallocNoSync handle the error
2065     }
2066
2067
2068     /**
2069      * Allocate a new pool with at least npages in it.
2070      * Sort it into pooltable[].
2071      * Return null if failed.
2072      */
2073     Pool *newPool(size_t npages)
2074     {
2075         Pool*  pool;
2076         Pool** newpooltable;
2077         size_t newnpools;
2078         size_t i;
2079
2080         //debug(PRINTF) printf("************Gcx::newPool(npages = %d)****************\n", npages);
2081
2082         // Round up to COMMITSIZE pages
2083         npages = (npages + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1);
2084
2085         // Minimum of POOLSIZE
2086         if (npages < POOLSIZE/PAGESIZE)
2087             npages = POOLSIZE/PAGESIZE;
2088         else if (npages > POOLSIZE/PAGESIZE)
2089         {   // Give us 150% of requested size, so there's room to extend
2090             auto n = npages + (npages >> 1);
2091             if (n < size_t.max/PAGESIZE)
2092                 npages = n;
2093         }
2094
2095         // Allocate successively larger pools up to 8 megs
2096         if (npools)
2097         {   size_t n;
2098
2099             n = npools;
2100             if (n > 8)
2101                 n = 8;                  // cap pool size at 8 megs
2102             n *= (POOLSIZE / PAGESIZE);
2103             if (npages < n)
2104                 npages = n;
2105         }
2106
2107         pool = cast(Pool *)cstdlib.calloc(1, Pool.sizeof);
2108         if (pool)
2109         {
2110             pool.initialize(npages);
2111             if (!pool.baseAddr)
2112                 goto Lerr;
2113
2114             newnpools = npools + 1;
2115             newpooltable = cast(Pool **)cstdlib.realloc(pooltable, newnpools * (Pool *).sizeof);
2116             if (!newpooltable)
2117                 goto Lerr;
2118
2119             // Sort pool into newpooltable[]
2120             for (i = 0; i < npools; i++)
2121             {
2122                 if (pool.opCmp(newpooltable[i]) < 0)
2123                      break;
2124             }
2125             cstring.memmove(newpooltable + i + 1, newpooltable + i, (npools - i) * (Pool *).sizeof);
2126             newpooltable[i] = pool;
2127
2128             pooltable = newpooltable;
2129             npools = newnpools;
2130
2131             minAddr = pooltable[0].baseAddr;
2132             maxAddr = pooltable[npools - 1].topAddr;
2133         }
2134         return pool;
2135
2136       Lerr:
2137         pool.Dtor();
2138         cstdlib.free(pool);
2139         return null;
2140     }
2141
2142
2143     /**
2144      * Allocate a page of bin's.
2145      * Returns:
2146      *  0       failed
2147      */
2148     int allocPage(Bins bin)
2149     {
2150         Pool*  pool;
2151         size_t n;
2152         size_t pn;
2153         byte*  p;
2154         byte*  ptop;
2155
2156         //debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin);
2157         for (n = 0; n < npools; n++)
2158         {
2159             pool = pooltable[n];
2160             pn = pool.allocPages(1);
2161             if (pn != OPFAIL)
2162                 goto L1;
2163         }
2164         return 0;               // failed
2165
2166       L1:
2167         pool.pagetable[pn] = cast(ubyte)bin;
2168
2169         // Convert page to free list
2170         size_t size = binsize[bin];
2171         List **b = &bucket[bin];
2172
2173         p = pool.baseAddr + pn * PAGESIZE;
2174         ptop = p + PAGESIZE;
2175         for (; p < ptop; p += size)
2176         {
2177             (cast(List *)p).next = *b;
2178             *b = cast(List *)p;
2179         }
2180         return 1;
2181     }
2182
2183
2184     /**
2185      * Search a range of memory values and mark any pointers into the GC pool.
2186      */
2187     void mark(void *pbot, void *ptop)
2188     {
2189         void **p1 = cast(void **)pbot;
2190         void **p2 = cast(void **)ptop;
2191         size_t pcache = 0;
2192         uint changes = 0;
2193
2194         //printf("marking range: %p -> %p\n", pbot, ptop);
2195         for (; p1 < p2; p1++)
2196         {
2197             Pool *pool;
2198             byte *p = cast(byte *)(*p1);
2199
2200             //if (log) debug(PRINTF) printf("\tmark %x\n", p);
2201             if (p >= minAddr && p < maxAddr)
2202             {
2203                 if ((cast(size_t)p & ~(PAGESIZE-1)) == pcache)
2204                 continue;
2205
2206                 pool = findPool(p);
2207                 if (pool)
2208                 {
2209                     size_t offset = cast(size_t)(p - pool.baseAddr);
2210                     size_t biti;
2211                     size_t pn = offset / PAGESIZE;
2212                     Bins   bin = cast(Bins)pool.pagetable[pn];
2213
2214                     //debug(PRINTF) printf("\t\tfound pool %x, base=%x, pn = %d, bin = %d, biti = x%x\n", pool, pool.baseAddr, pn, bin, biti);
2215
2216                     // Adjust bit to be at start of allocated memory block
2217                     if (bin <= B_PAGE)
2218                     {
2219                         biti = (offset & notbinsize[bin]) >> 4;
2220                         //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
2221                     }
2222                     else if (bin == B_PAGEPLUS)
2223                     {
2224                         do
2225                         {   --pn;
2226                         } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
2227                         biti = pn * (PAGESIZE / 16);
2228                     }
2229                     else
2230                     {
2231                         // Don't mark bits in B_FREE or B_UNCOMMITTED pages
2232                         continue;
2233                     }
2234
2235                     if (bin >= B_PAGE) // Cache B_PAGE and B_PAGEPLUS lookups
2236                         pcache = cast(size_t)p & ~(PAGESIZE-1);
2237
2238                     //debug(PRINTF) printf("\t\tmark(x%x) = %d\n", biti, pool.mark.test(biti));
2239                     if (!pool.mark.test(biti))
2240                     {
2241                         //if (log) debug(PRINTF) printf("\t\tmarking %x\n", p);
2242                         pool.mark.set(biti);
2243                         if (!pool.noscan.test(biti))
2244                         {
2245                             pool.scan.set(biti);
2246                             changes = 1;
2247                         }
2248                         log_parent(sentinel_add(pool.baseAddr + biti * 16), sentinel_add(pbot));
2249                     }
2250                 }
2251             }
2252         }
2253         anychanges |= changes;
2254     }
2255
2256
2257     /**
2258      * Return number of full pages free'd.
2259      */
2260     size_t fullcollectshell()
2261     {
2262         // The purpose of the 'shell' is to ensure all the registers
2263         // get put on the stack so they'll be scanned
2264         void *sp;
2265         size_t result;
2266         version (GNU)
2267         {
2268             __builtin_unwind_init();
2269             sp = & sp;
2270         }
2271         else version(LDC)
2272         {
2273             version(X86)
2274             {
2275                 uint eax,ecx,edx,ebx,ebp,esi,edi;
2276                 asm
2277                 {
2278                     mov eax[EBP], EAX      ;
2279                     mov ecx[EBP], ECX      ;
2280                     mov edx[EBP], EDX      ;
2281                     mov ebx[EBP], EBX      ;
2282                     mov ebp[EBP], EBP      ;
2283                     mov esi[EBP], ESI      ;
2284                     mov edi[EBP], EDI      ;
2285                     mov  sp[EBP], ESP      ;
2286                 }
2287             }
2288             else version (X86_64)
2289             {
2290                 ulong rax,rbx,rcx,rdx,rbp,rsi,rdi,r8,r9,r10,r11,r12,r13,r14,r15;
2291                 asm
2292                 {
2293                     movq rax[RBP], RAX      ;
2294                     movq rbx[RBP], RBX      ;
2295                     movq rcx[RBP], RCX      ;
2296                     movq rdx[RBP], RDX      ;
2297                     movq rbp[RBP], RBP      ;
2298                     movq rsi[RBP], RSI      ;
2299                     movq rdi[RBP], RDI      ;
2300                     movq r8 [RBP], R8       ;
2301                     movq r9 [RBP], R9       ;
2302                     movq r10[RBP], R10      ;
2303                     movq r11[RBP], R11      ;
2304                     movq r12[RBP], R12      ;
2305                     movq r13[RBP], R13      ;
2306                     movq r14[RBP], R14      ;
2307                     movq r15[RBP], R15      ;
2308                     movq  sp[RBP], RSP      ;
2309                 }
2310             }
2311             else
2312             {
2313                 static assert( false, "Architecture not supported." );
2314             }
2315         }
2316         else
2317         {
2318         asm
2319         {
2320             pushad              ;
2321             mov sp[EBP],ESP     ;
2322         }
2323         }
2324         result = fullcollect(sp);
2325         version (GNU)
2326         {
2327             // nothing to do
2328         }
2329         else version(LDC)
2330         {
2331             // nothing to do
2332         }
2333         else
2334         {
2335         asm
2336         {
2337             popad               ;
2338         }
2339         }
2340         return result;
2341     }
2342
2343
2344     /**
2345      *
2346      */
2347     size_t fullcollect(void *stackTop)
2348     {
2349         size_t n;
2350         Pool*  pool;
2351
2352         debug(COLLECT_PRINTF) printf("Gcx.fullcollect()\n");
2353
2354         thread_suspendAll();
2355
2356         p_cache = null;
2357         size_cache = 0;
2358
2359         anychanges = 0;
2360         for (n = 0; n < npools; n++)
2361         {
2362             pool = pooltable[n];
2363             pool.mark.zero();
2364             pool.scan.zero();
2365             pool.freebits.zero();
2366         }
2367
2368         // Mark each free entry, so it doesn't get scanned
2369         for (n = 0; n < B_PAGE; n++)
2370         {
2371             for (List *list = bucket[n]; list; list = list.next)
2372             {
2373                 pool = findPool(list);
2374                 assert(pool);
2375                 pool.freebits.set(cast(size_t)(cast(byte*)list - pool.baseAddr) / 16);
2376             }
2377         }
2378
2379         for (n = 0; n < npools; n++)
2380         {
2381             pool = pooltable[n];
2382             pool.mark.copy(&pool.freebits);
2383         }
2384
2385         rt_scanStaticData( &mark );
2386
2387         version (MULTI_THREADED)
2388         {
2389             if (!noStack)
2390             {
2391                 // Scan stacks and registers for each paused thread
2392                 thread_scanAll( &mark, stackTop );
2393             }
2394         }
2395         else
2396         {
2397             if (!noStack)
2398             {
2399                 // Scan stack for main thread
2400                 debug(PRINTF) printf(" scan stack bot = %x, top = %x\n", stackTop, stackBottom);
2401                 version (STACKGROWSDOWN)
2402                     mark(stackTop, stackBottom);
2403                 else
2404                     mark(stackBottom, stackTop);
2405             }
2406         }
2407
2408         // Scan roots[]
2409         debug(COLLECT_PRINTF) printf("scan roots[]\n");
2410         mark(roots, roots + nroots);
2411
2412         // Scan ranges[]
2413         debug(COLLECT_PRINTF) printf("scan ranges[]\n");
2414         //log++;
2415         for (n = 0; n < nranges; n++)
2416         {
2417             debug(COLLECT_PRINTF) printf("\t%x .. %x\n", ranges[n].pbot, ranges[n].ptop);
2418             mark(ranges[n].pbot, ranges[n].ptop);
2419         }
2420         //log--;
2421
2422         debug(COLLECT_PRINTF) printf("\tscan heap\n");
2423         while (anychanges)
2424         {
2425             anychanges = 0;
2426             for (n = 0; n < npools; n++)
2427             {
2428                 uint *bbase;
2429                 uint *b;
2430                 uint *btop;
2431
2432                 pool = pooltable[n];
2433
2434                 bbase = pool.scan.base();
2435                 btop = bbase + pool.scan.nwords;
2436                 for (b = bbase; b < btop;)
2437                 {   Bins   bin;
2438                     size_t pn;
2439                     size_t u;
2440                     size_t bitm;
2441                     byte*  o;
2442
2443                     bitm = *b;
2444                     if (!bitm)
2445                     {   b++;
2446                         continue;
2447                     }
2448                     *b = 0;
2449
2450                     o = pool.baseAddr + (b - bbase) * 32 * 16;
2451                     if (!(bitm & 0xFFFF))
2452                     {
2453                         bitm >>= 16;
2454                         o += 16 * 16;
2455                     }
2456                     for (; bitm; o += 16, bitm >>= 1)
2457                     {
2458                         if (!(bitm & 1))
2459                             continue;
2460
2461                         pn = cast(size_t)(o - pool.baseAddr) / PAGESIZE;
2462                         bin = cast(Bins)pool.pagetable[pn];
2463                         if (bin < B_PAGE)
2464                         {
2465                             mark(o, o + binsize[bin]);
2466                         }
2467                         else if (bin == B_PAGE || bin == B_PAGEPLUS)
2468                         {
2469                             if (bin == B_PAGEPLUS)
2470                             {
2471                                 while (pool.pagetable[pn - 1] != B_PAGE)
2472                                     pn--;
2473                             }
2474                             u = 1;
2475                             while (pn + u < pool.ncommitted && pool.pagetable[pn + u] == B_PAGEPLUS)
2476                                 u++;
2477                             mark(o, o + u * PAGESIZE);
2478                         }
2479                     }
2480                 }
2481             }
2482         }
2483
2484         thread_resumeAll();
2485
2486         // Free up everything not marked
2487         debug(COLLECT_PRINTF) printf("\tfree'ing\n");
2488         size_t freedpages = 0;
2489         size_t freed = 0;
2490         for (n = 0; n < npools; n++)
2491         {   size_t pn;
2492             size_t ncommitted;
2493             uint*  bbase;
2494
2495             pool = pooltable[n];
2496             bbase = pool.mark.base();
2497             ncommitted = pool.ncommitted;
2498             for (pn = 0; pn < ncommitted; pn++, bbase += PAGESIZE / (32 * 16))
2499             {
2500                 Bins bin = cast(Bins)pool.pagetable[pn];
2501
2502                 if (bin < B_PAGE)
2503                 {   byte* p;
2504                     byte* ptop;
2505                     size_t biti;
2506                     size_t bitstride;
2507                     auto   size = binsize[bin];
2508
2509                     p = pool.baseAddr + pn * PAGESIZE;
2510                     ptop = p + PAGESIZE;
2511                     biti = pn * (PAGESIZE/16);
2512                     bitstride = size / 16;
2513
2514     version(none) // BUG: doesn't work because freebits() must also be cleared
2515     {
2516                     // If free'd entire page
2517                     if (bbase[0] == 0 && bbase[1] == 0 && bbase[2] == 0 && bbase[3] == 0 &&
2518                         bbase[4] == 0 && bbase[5] == 0 && bbase[6] == 0 && bbase[7] == 0)
2519                     {
2520                         for (; p < ptop; p += size, biti += bitstride)
2521                         {
2522                             if (pool.finals.nbits && pool.finals.testClear(biti))
2523                                 rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/);
2524                             gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
2525
2526                             List *list = cast(List *)p;
2527                             //debug(PRINTF) printf("\tcollecting %x\n", list);
2528                             log_free(sentinel_add(list));
2529
2530                             debug (MEMSTOMP) cstring.memset(p, 0xF3, size);
2531                         }
2532                         pool.pagetable[pn] = B_FREE;
2533                         freed += PAGESIZE;
2534                         //debug(PRINTF) printf("freeing entire page %d\n", pn);
2535                         continue;
2536                     }
2537     }
2538                     for (; p < ptop; p += size, biti += bitstride)
2539                     {
2540                         if (!pool.mark.test(biti))
2541                         {
2542                             sentinel_Invariant(sentinel_add(p));
2543
2544                             pool.freebits.set(biti);
2545                             if (pool.finals.nbits && pool.finals.testClear(biti))
2546                                 rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/);
2547                             clrBits(pool, biti, BlkAttr.ALL_BITS);
2548
2549                             List *list = cast(List *)p;
2550                             debug(PRINTF) printf("\tcollecting %x\n", list);
2551                             log_free(sentinel_add(list));
2552
2553                             debug (MEMSTOMP) cstring.memset(p, 0xF3, size);
2554
2555                             freed += size;
2556                         }
2557                     }
2558                 }
2559                 else if (bin == B_PAGE)
2560                 {   size_t biti = pn * (PAGESIZE / 16);
2561
2562                     if (!pool.mark.test(biti))
2563                     {   byte *p = pool.baseAddr + pn * PAGESIZE;
2564
2565                         sentinel_Invariant(sentinel_add(p));
2566                         if (pool.finals.nbits && pool.finals.testClear(biti))
2567                             rt_finalize(sentinel_add(p), false/*noStack > 0*/);
2568                         clrBits(pool, biti, BlkAttr.ALL_BITS);
2569
2570                         debug(COLLECT_PRINTF) printf("\tcollecting big %x\n", p);
2571                         log_free(sentinel_add(p));
2572                         pool.pagetable[pn] = B_FREE;
2573                         freedpages++;
2574                         debug (MEMSTOMP) cstring.memset(p, 0xF3, PAGESIZE);
2575                         while (pn + 1 < ncommitted && pool.pagetable[pn + 1] == B_PAGEPLUS)
2576                         {
2577                             pn++;
2578                             pool.pagetable[pn] = B_FREE;
2579                             freedpages++;
2580
2581                             debug (MEMSTOMP)
2582                             {   p += PAGESIZE;
2583                                 cstring.memset(p, 0xF3, PAGESIZE);
2584                             }
2585                         }
2586                     }
2587                 }
2588             }
2589         }
2590
2591         // Zero buckets
2592         bucket[] = null;
2593
2594         // Free complete pages, rebuild free list
2595         debug(COLLECT_PRINTF) printf("\tfree complete pages\n");
2596         size_t recoveredpages = 0;
2597         for (n = 0; n < npools; n++)
2598         {   size_t pn;
2599             size_t ncommitted;
2600
2601             pool = pooltable[n];
2602             ncommitted = pool.ncommitted;
2603             for (pn = 0; pn < ncommitted; pn++)
2604             {
2605                 Bins   bin = cast(Bins)pool.pagetable[pn];
2606                 size_t biti;
2607                 size_t u;
2608
2609                 if (bin < B_PAGE)
2610                 {
2611                     size_t size = binsize[bin];
2612                     size_t bitstride = size / 16;
2613                     size_t bitbase = pn * (PAGESIZE / 16);
2614                     size_t bittop = bitbase + (PAGESIZE / 16);
2615                     byte*  p;
2616
2617                     biti = bitbase;
2618                     for (biti = bitbase; biti < bittop; biti += bitstride)
2619                     {   if (!pool.freebits.test(biti))
2620                             goto Lnotfree;
2621                     }
2622                     pool.pagetable[pn] = B_FREE;
2623                     recoveredpages++;
2624                     continue;
2625
2626                  Lnotfree:
2627                     p = pool.baseAddr + pn * PAGESIZE;
2628                     for (u = 0; u < PAGESIZE; u += size)