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

root/trunk/lib/gc/basic/gcx.d

Revision 4095, 74.8 kB (checked in by fawzi, 2 days ago)

correct import for debug=THREADINVARIANT

  • 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
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 gcbits;
50 private import gcstats;
51 private import 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 private import tango.stdc.stdio;
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
68 private
69 {
70     enum BlkAttr : uint
71     {
72         FINALIZE = 0b0000_0001,
73         NO_SCAN  = 0b0000_0010,
74         NO_MOVE  = 0b0000_0100,
75         ALL_BITS = 0b1111_1111
76     }
77
78     struct BlkInfo
79     {
80         void*  base;
81         size_t size;
82         uint   attr;
83     }
84
85     extern (C) void* rt_stackBottom();
86     extern (C) void* rt_stackTop();
87
88     extern (C) void rt_finalize( void* p, bool det = true );
89
90     alias void delegate( void*, void* ) scanFn;
91
92     extern (C) void rt_scanStaticData( scanFn scan );
93
94     version (MULTI_THREADED)
95     {
96         extern (C) bool thread_needLock();
97         extern (C) void thread_suspendAll();
98         extern (C) void thread_resumeAll();
99
100         extern (C) void thread_scanAll( scanFn fn, void* curStackTop = null );
101     }
102
103     extern (C) void onOutOfMemoryError();
104
105     enum
106     {
107         OPFAIL = ~cast(size_t)0
108     }
109 }
110
111
112 alias GC gc_t;
113
114
115 /* ======================= Leak Detector =========================== */
116
117
118 debug (LOGGING)
119 {
120     struct Log
121     {
122         void*  p;
123         size_t size;
124         size_t line;
125         char*  file;
126         void*  parent;
127
128         void print()
129         {
130             printf("    p = %x, size = %d, parent = %x ", p, size, parent);
131             if (file)
132             {
133                 printf("%s(%u)", file, line);
134             }
135             printf("\n");
136         }
137     }
138
139
140     struct LogArray
141     {
142         size_t dim;
143         size_t allocdim;
144         Log *data;
145
146         void Dtor()
147         {
148             if (data)
149                 cstdlib.free(data);
150             data = null;
151         }
152
153         void reserve(size_t nentries)
154         {
155             assert(dim <= allocdim);
156             if (allocdim - dim < nentries)
157             {
158                 allocdim = (dim + nentries) * 2;
159                 assert(dim + nentries <= allocdim);
160                 if (!data)
161                 {
162                     data = cast(Log*)cstdlib.malloc(allocdim * Log.sizeof);
163                     if (!data && allocdim)
164                         onOutOfMemoryError();
165                 }
166                 else
167                 {   Log *newdata;
168
169                     newdata = cast(Log*)cstdlib.malloc(allocdim * Log.sizeof);
170                     if (!newdata && allocdim)
171                         onOutOfMemoryError();
172                     cstring.memcpy(newdata, data, dim * Log.sizeof);
173                     cstdlib.free(data);
174                     data = newdata;
175                 }
176             }
177         }
178
179
180         void push(Log log)
181         {
182             reserve(1);
183             data[dim++] = log;
184         }
185
186         void remove(size_t i)
187         {
188             cstring.memmove(data + i, data + i + 1, (dim - i) * Log.sizeof);
189             dim--;
190         }
191
192
193         size_t find(void *p)
194         {
195             for (size_t i = 0; i < dim; i++)
196             {
197                 if (data[i].p == p)
198                     return i;
199             }
200             return OPFAIL; // not found
201         }
202
203
204         void copy(LogArray *from)
205         {
206             reserve(from.dim - dim);
207             assert(from.dim <= allocdim);
208             cstring.memcpy(data, from.data, from.dim * Log.sizeof);
209             dim = from.dim;
210         }
211     }
212 }
213
214
215 /* ============================ GC =============================== */
216
217
218 class GCLock { }                // just a dummy so we can get a global lock
219
220
221 const uint GCVERSION = 1;       // increment every time we change interface
222                                 // to GC.
223
224 class GC
225 {
226     // For passing to debug code
227     static size_t line;
228     static char*  file;
229
230     uint gcversion = GCVERSION;
231
232     Gcx *gcx;                   // implementation
233     static ClassInfo gcLock;    // global lock
234
235
236     void initialize()
237     {
238         gcLock = GCLock.classinfo;
239         gcx = cast(Gcx*)cstdlib.calloc(1, Gcx.sizeof);
240         if (!gcx)
241             onOutOfMemoryError();
242         gcx.initialize();
243         setStackBottom(rt_stackBottom());
244     }
245
246
247     void Dtor()
248     {
249         version (linux)
250         {
251             //debug(PRINTF) printf("Thread %x ", pthread_self());
252             //debug(PRINTF) printf("GC.Dtor()\n");
253         }
254
255         if (gcx)
256         {
257             gcx.Dtor();
258             cstdlib.free(gcx);
259             gcx = null;
260         }
261     }
262
263
264     invariant
265     {
266         if (gcx)
267         {
268             gcx.thread_Invariant();
269         }
270     }
271
272
273     /**
274      *
275      */
276     void enable()
277     {
278         if (!thread_needLock())
279         {
280             assert(gcx.disabled > 0);
281             gcx.disabled--;
282         }
283         else synchronized (gcLock)
284         {
285             assert(gcx.disabled > 0);
286             gcx.disabled--;
287         }
288     }
289
290
291     /**
292      *
293      */
294     void disable()
295     {
296         if (!thread_needLock())
297         {
298             gcx.disabled++;
299         }
300         else synchronized (gcLock)
301         {
302             gcx.disabled++;
303         }
304     }
305
306
307     /**
308      *
309      */
310     uint getAttr(void* p)
311     {
312         if (!p)
313         {
314             return 0;
315         }
316
317         uint go()
318         {
319             Pool* pool = gcx.findPool(p);
320             uint  oldb = 0;
321
322             if (pool)
323             {
324                 auto biti = cast(size_t)(p - pool.baseAddr) / 16;
325
326                 oldb = gcx.getBits(pool, biti);
327             }
328             return oldb;
329         }
330
331         if (!thread_needLock())
332         {
333             return go();
334         }
335         else synchronized (gcLock)
336         {
337             return go();
338         }
339     }
340
341
342     /**
343      *
344      */
345     uint setAttr(void* p, uint mask)
346     {
347         if (!p)
348         {
349             return 0;
350         }
351
352         uint go()
353         {
354             Pool* pool = gcx.findPool(p);
355             uint  oldb = 0;
356
357             if (pool)
358             {
359                 auto biti = cast(size_t)(p - pool.baseAddr) / 16;
360
361                 oldb = gcx.getBits(pool, biti);
362                 gcx.setBits(pool, biti, mask);
363             }
364             return oldb;
365         }
366
367         if (!thread_needLock())
368         {
369             return go();
370         }
371         else synchronized (gcLock)
372         {
373             return go();
374         }
375     }
376
377
378     /**
379      *
380      */
381     uint clrAttr(void* p, uint mask)
382     {
383         if (!p)
384         {
385             return 0;
386         }
387
388         uint go()
389         {
390             Pool* pool = gcx.findPool(p);
391             uint  oldb = 0;
392
393             if (pool)
394             {
395                 auto biti = cast(size_t)(p - pool.baseAddr) / 16;
396
397                 oldb = gcx.getBits(pool, biti);
398                 gcx.clrBits(pool, biti, mask);
399             }
400             return oldb;
401         }
402
403         if (!thread_needLock())
404         {
405             return go();
406         }
407         else synchronized (gcLock)
408         {
409             return go();
410         }
411     }
412
413
414     /**
415      *
416      */
417     void *malloc(size_t size, uint bits = 0)
418     {
419         if (!size)
420         {
421             return null;
422         }
423
424         if (!thread_needLock())
425         {
426             return mallocNoSync(size, bits);
427         }
428         else synchronized (gcLock)
429         {
430             return mallocNoSync(size, bits);
431         }
432     }
433
434
435     //
436     //
437     //
438     private void *mallocNoSync(size_t size, uint bits = 0)
439     {
440         assert(size != 0);
441
442         void *p = null;
443         Bins bin;
444
445         //debug(PRINTF) printf("GC::malloc(size = %d, gcx = %p)\n", size, gcx);
446         assert(gcx);
447         //debug(PRINTF) printf("gcx.self = %x, pthread_self() = %x\n", gcx.self, pthread_self());
448
449         size += SENTINEL_EXTRA;
450
451         // Compute size bin
452         // Cache previous binsize lookup - Dave Fladebo.
453         static size_t lastsize = -1;
454         static Bins lastbin;
455         if (size == lastsize)
456             bin = lastbin;
457         else
458         {
459             bin = gcx.findBin(size);
460             lastsize = size;
461             lastbin = bin;
462         }
463
464         if (bin < B_PAGE)
465         {
466             p = gcx.bucket[bin];
467             if (p is null)
468             {
469                 if (!gcx.allocPage(bin) && !gcx.disabled)   // try to find a new page
470                 {
471                     if (!thread_needLock())
472                     {
473                         /* Then we haven't locked it yet. Be sure
474                          * and lock for a collection, since a finalizer
475                          * may start a new thread.
476                          */
477                         synchronized (gcLock)
478                         {
479                             gcx.fullcollectshell();
480                         }
481                     }
482                     else if (!gcx.fullcollectshell())       // collect to find a new page
483                     {
484                         //gcx.newPool(1);
485                     }
486                 }
487                 if (!gcx.bucket[bin] && !gcx.allocPage(bin))
488                 {   int result;
489
490                     gcx.newPool(1);         // allocate new pool to find a new page
491                     result = gcx.allocPage(bin);
492                     if (!result)
493                         onOutOfMemoryError();
494                 }
495                 p = gcx.bucket[bin];
496             }
497
498             // Return next item from free list
499             gcx.bucket[bin] = (cast(List*)p).next;
500             if( !(bits & BlkAttr.NO_SCAN) )
501                 cstring.memset(p + size, 0, binsize[bin] - size);
502             //debug(PRINTF) printf("\tmalloc => %x\n", p);
503             debug (MEMSTOMP) cstring.memset(p, 0xF0, size);
504         }
505         else
506         {
507             p = gcx.bigAlloc(size);
508             if (!p)
509                 onOutOfMemoryError();
510         }
511         size -= SENTINEL_EXTRA;
512         p = sentinel_add(p);
513         sentinel_init(p, size);
514         gcx.log_malloc(p, size);
515
516         if (bits)
517         {
518             Pool *pool = gcx.findPool(p);
519             assert(pool);
520
521             gcx.setBits(pool, cast(size_t)(p - pool.baseAddr) / 16, bits);
522         }
523         return p;
524     }
525
526
527     /**
528      *
529      */
530     void *calloc(size_t size, uint bits = 0)
531     {
532         if (!size)
533         {
534             return null;
535         }
536
537         if (!thread_needLock())
538         {
539             return callocNoSync(size, bits);
540         }
541         else synchronized (gcLock)
542         {
543             return callocNoSync(size, bits);
544         }
545     }
546
547
548     //
549     //
550     //
551     private void *callocNoSync(size_t size, uint bits = 0)
552     {
553         assert(size != 0);
554
555         //debug(PRINTF) printf("calloc: %x len %d\n", p, len);
556         void *p = mallocNoSync(size, bits);
557         cstring.memset(p, 0, size);
558         return p;
559     }
560
561
562     /**
563      *
564      */
565     void *realloc(void *p, size_t size, uint bits = 0)
566     {
567         if (!thread_needLock())
568         {
569             return reallocNoSync(p, size, bits);
570         }
571         else synchronized (gcLock)
572         {
573             return reallocNoSync(p, size, bits);
574         }
575     }
576
577
578     //
579     //
580     //
581     private void *reallocNoSync(void *p, size_t size, uint bits = 0)
582     {
583         if (!size)
584         {   if (p)
585             {   freeNoSync(p);
586                 p = null;
587             }
588         }
589         else if (!p)
590         {
591             p = mallocNoSync(size, bits);
592         }
593         else
594         {   void *p2;
595             size_t psize;
596
597             //debug(PRINTF) printf("GC::realloc(p = %x, size = %u)\n", p, size);
598             version (SENTINEL)
599             {
600                 sentinel_Invariant(p);
601                 psize = *sentinel_size(p);
602                 if (psize != size)
603                 {
604                     if (psize)
605                     {
606                         Pool *pool = gcx.findPool(p);
607
608                         if (pool)
609                         {
610                             auto biti = cast(size_t)(p - pool.baseAddr) / 16;
611
612                             if (bits)
613                             {
614                                 gcx.clrBits(pool, biti, BlkAttr.ALL_BITS);
615                                 gcx.setBits(pool, biti, bits);
616                             }
617                             else
618                             {
619                                 bits = gcx.getBits(pool, biti);
620                             }
621                         }
622                     }
623                     p2 = mallocNoSync(size, bits);
624                     if (psize < size)
625                         size = psize;
626                     //debug(PRINTF) printf("\tcopying %d bytes\n",size);
627                     cstring.memcpy(p2, p, size);
628                     p = p2;
629                 }
630             }
631             else
632             {
633                 psize = gcx.findSize(p);        // find allocated size
634                 if (psize >= PAGESIZE && size >= PAGESIZE)
635                 {
636                     auto psz = psize / PAGESIZE;
637                     auto newsz = (size + PAGESIZE - 1) / PAGESIZE;
638                     if (newsz == psz)
639                         return p;
640
641                     auto pool = gcx.findPool(p);
642                     auto pagenum = (p - pool.baseAddr) / PAGESIZE;
643
644                     if (newsz < psz)
645                     {   // Shrink in place
646                         synchronized (gcLock)
647                         {
648                             debug (MEMSTOMP) cstring.memset(p + size, 0xF2, psize - size);
649                             pool.freePages(pagenum + newsz, psz - newsz);
650                         }
651                         return p;
652                     }
653                     else if (pagenum + newsz <= pool.npages)
654                     {
655                         // Attempt to expand in place
656                         synchronized (gcLock)
657                         {
658                             for (size_t i = pagenum + psz; 1;)
659                             {
660                                 if (i == pagenum + newsz)
661                                 {
662                                     debug (MEMSTOMP) cstring.memset(p + psize, 0xF0, size - psize);
663                                     cstring.memset(&pool.pagetable[pagenum + psz], B_PAGEPLUS, newsz - psz);
664                                     return p;
665                                 }
666                                 if (i == pool.ncommitted)
667                                 {
668                                     auto u = pool.extendPages(pagenum + newsz - pool.ncommitted);
669                                     if (u == OPFAIL)
670                                         break;
671                                     i = pagenum + newsz;
672                     &n