2 * This module contains all functions related to an object's lifetime:
3 * allocation, resizing, deallocation, and finalization.
5 * Copyright: Copyright (C) 2004-2007 Digital Mars, www.digitalmars.com.
8 * This software is provided 'as-is', without any express or implied
9 * warranty. In no event will the authors be held liable for any damages
10 * arising from the use of this software.
12 * Permission is granted to anyone to use this software for any purpose,
13 * including commercial applications, and to alter it and redistribute it
14 * freely, in both source and binary form, subject to the following
17 * o The origin of this software must not be misrepresented; you must not
18 * claim that you wrote the original software. If you use this software
19 * in a product, an acknowledgment in the product documentation would be
20 * appreciated but is not required.
21 * o Altered source versions must be plainly marked as such, and must not
22 * be misrepresented as being the original software.
23 * o This notice may not be removed or altered from any source
25 * Authors: Walter Bright, Sean Kelly
35 debug(PRINTF) import stdc.stdio;
43 FINALIZE = 0b0000_0001,
44 NO_SCAN = 0b0000_0010,
45 NO_MOVE = 0b0000_0100,
46 ALL_BITS = 0b1111_1111
56 extern (C) uint gc_getAttr( void* p );
57 extern (C) uint gc_setAttr( void* p, uint a );
58 extern (C) uint gc_clrAttr( void* p, uint a );
60 extern (C) void* gc_malloc( size_t sz, uint ba = 0 );
61 extern (C) void* gc_calloc( size_t sz, uint ba = 0 );
62 extern (C) size_t gc_extend( void* p, size_t mx, size_t sz );
63 extern (C) void gc_free( void* p );
65 extern (C) void* gc_addrOf( void* p );
66 extern (C) size_t gc_sizeOf( void* p );
67 extern (C) BlkInfo gc_query( void* p );
69 extern (C) void onFinalizeError( ClassInfo c, Exception e );
70 extern (C) void onOutOfMemoryError();
72 extern (C) void _d_monitordelete(Object h, bool det = true);
79 alias bool function(Object) CollectHandler;
80 CollectHandler collectHandler = null;
87 extern (C) void* _d_allocmemory(size_t sz)
96 extern (C) Object _d_newclass(ClassInfo ci)
100 debug(PRINTF) printf("_d_newclass(ci = %p, %s)\n", ci, cast(char *)ci.name);
101 if (ci.flags & 1) // if COM object
102 { /* COM objects are not garbage collected, they are reference counted
103 * using AddRef() and Release(). They get free'd by C's free()
104 * function called by Release() when Release()'s reference count goes
107 p = malloc(ci.init.length);
109 onOutOfMemoryError();
113 p = gc_malloc(ci.init.length,
114 BlkAttr.FINALIZE | (ci.flags & 2 ? BlkAttr.NO_SCAN : 0));
115 debug(PRINTF) printf(" p = %p\n", p);
120 printf("p = %p\n", p);
121 printf("ci = %p, ci.init = %p, len = %d\n", ci, ci.init, ci.init.length);
122 printf("vptr = %p\n", *cast(void**) ci.init);
123 printf("vtbl[0] = %p\n", (*cast(void***) ci.init)[0]);
124 printf("vtbl[1] = %p\n", (*cast(void***) ci.init)[1]);
125 printf("init[0] = %x\n", (cast(uint*) ci.init)[0]);
126 printf("init[1] = %x\n", (cast(uint*) ci.init)[1]);
127 printf("init[2] = %x\n", (cast(uint*) ci.init)[2]);
128 printf("init[3] = %x\n", (cast(uint*) ci.init)[3]);
129 printf("init[4] = %x\n", (cast(uint*) ci.init)[4]);
133 (cast(byte*) p)[0 .. ci.init.length] = ci.init[];
135 debug(PRINTF) printf("initialization done\n");
136 return cast(Object) p;
143 extern (C) void _d_delinterface(void** p)
147 Interface* pi = **cast(Interface ***)*p;
148 Object o = cast(Object)(*p - pi.offset);
157 private extern (D) alias void (*fp_t)(Object);
163 extern (C) void _d_delclass(Object* p)
167 debug(PRINTF) printf("_d_delclass(%p)\n", *p);
169 ClassInfo **pc = cast(ClassInfo **)*p;
174 rt_finalize(cast(void*) *p);
178 fp_t fp = cast(fp_t)c.deallocator;
179 (*fp)(*p); // call deallocator
186 rt_finalize(cast(void*) *p);
188 gc_free(cast(void*) *p);
195 * Allocate a new array of length elements.
196 * ti is the type of the resulting array, or pointer to element.
197 * (For when the array is initialized to 0)
199 extern (C) ulong _d_newarrayT(TypeInfo ti, size_t length)
203 auto size = ti.next.tsize(); // array element size
205 debug(PRINTF) printf("_d_newarrayT(length = x%x, size = %d)\n", length, size);
206 if (length == 0 || size == 0)
210 version (D_InlineAsm_X86)
222 p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
223 debug(PRINTF) printf(" p = %p\n", p);
225 result = cast(ulong)length + (cast(ulong)cast(uint)p << 32);
230 onOutOfMemoryError();
234 * For when the array has a non-zero initializer.
236 extern (C) ulong _d_newarrayiT(TypeInfo ti, size_t length)
239 auto size = ti.next.tsize(); // array element size
241 debug(PRINTF) printf("_d_newarrayiT(length = %d, size = %d)\n", length, size);
243 if (length == 0 || size == 0)
247 auto initializer = ti.next.init();
248 auto isize = initializer.length;
249 auto q = initializer.ptr;
250 version (D_InlineAsm_X86)
262 auto p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
263 debug(PRINTF) printf(" p = %p\n", p);
265 memset(p, *cast(ubyte*)q, size);
266 else if (isize == int.sizeof)
268 int init = *cast(int*)q;
270 for (size_t u = 0; u < size; u++)
272 (cast(int*)p)[u] = init;
277 for (size_t u = 0; u < size; u += isize)
279 memcpy(p + u, q, isize);
283 result = cast(ulong)length + (cast(ulong)cast(uint)p << 32);
288 onOutOfMemoryError();
294 extern (C) ulong _d_newarraymT(TypeInfo ti, int ndims, ...)
298 debug(PRINTF) printf("_d_newarraymT(ndims = %d)\n", ndims);
303 va_start!(int)(q, ndims);
305 void[] foo(TypeInfo ti, size_t* pdim, int ndims)
310 debug(PRINTF) printf("foo(ti = %p, ti.next = %p, dim = %d, ndims = %d\n", ti, ti.next, dim, ndims);
313 auto r = _d_newarrayT(ti, dim);
314 p = *cast(void[]*)(&r);
318 p = gc_malloc(dim * (void[]).sizeof + 1)[0 .. dim];
319 for (int i = 0; i < dim; i++)
321 (cast(void[]*)p.ptr)[i] = foo(ti.next, pdim + 1, ndims - 1);
327 size_t* pdim = cast(size_t *)q;
328 result = cast(ulong)foo(ti, pdim, ndims);
329 debug(PRINTF) printf("result = %llx\n", result);
333 for (int i = 0; i < ndims; i++)
335 printf("index %d: %d\n", i, va_arg!(int)(q));
347 extern (C) ulong _d_newarraymiT(TypeInfo ti, int ndims, ...)
351 debug(PRINTF) printf("_d_newarraymiT(ndims = %d)\n", ndims);
357 va_start!(int)(q, ndims);
359 void[] foo(TypeInfo ti, size_t* pdim, int ndims)
366 auto r = _d_newarrayiT(ti, dim);
367 p = *cast(void[]*)(&r);
371 p = gc_malloc(dim * (void[]).sizeof + 1)[0 .. dim];
372 for (int i = 0; i < dim; i++)
374 (cast(void[]*)p.ptr)[i] = foo(ti.next, pdim + 1, ndims - 1);
380 size_t* pdim = cast(size_t *)q;
381 result = cast(ulong)foo(ti, pdim, ndims);
382 debug(PRINTF) printf("result = %llx\n", result);
386 for (int i = 0; i < ndims; i++)
388 printf("index %d: %d\n", i, va_arg!(int)(q));
389 printf("init = %d\n", va_arg!(int)(q));
409 * This function has been replaced by _d_delarray_t
411 extern (C) void _d_delarray(Array *p)
415 assert(!p.length || p.data);
428 extern (C) void _d_delarray_t(Array *p, TypeInfo ti)
432 assert(!p.length || p.data);
437 // Call destructors on all the sub-objects
438 auto sz = ti.tsize();
440 auto pend = pe + p.length * sz;
458 extern (C) void _d_delmemory(void* *p)
471 extern (C) void _d_callinterfacefinalizer(void *p)
475 Interface *pi = **cast(Interface ***)p;
476 Object o = cast(Object)(p - pi.offset);
477 rt_finalize(cast(void*)o);
485 extern (C) void _d_callfinalizer(void* p)
494 extern (C) void rt_setCollectHandler(CollectHandler h)
503 extern (C) void rt_finalize(void* p, bool det = true)
505 debug(PRINTF) printf("rt_finalize(p = %p)\n", p);
507 if (p) // not necessary if called from gc
509 ClassInfo** pc = cast(ClassInfo**)p;
517 if (det || collectHandler is null || collectHandler(cast(Object)p))
523 fp_t fp = cast(fp_t)c.destructor;
524 (*fp)(cast(Object)p); // call destructor
529 if ((cast(void**)p)[1]) // if monitor is not null
530 _d_monitordelete(cast(Object)p, det);
534 onFinalizeError(**pc, e);
538 *pc = null; // zero vptr
546 * Resize dynamic arrays with 0 initializers.
548 extern (C) byte[] _d_arraysetlengthT(TypeInfo ti, size_t newlength, Array *p)
552 assert(!p.length || p.data);
557 size_t sizeelem = ti.next.tsize();
561 printf("_d_arraysetlengthT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
563 printf("\tp.data = %p, p.length = %d\n", p.data, p.length);
568 version (D_InlineAsm_X86)
570 size_t newsize = void;
582 size_t newsize = sizeelem * newlength;
584 if (newsize / newlength != sizeelem)
588 debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength);
593 if (newlength > p.length)
595 size_t size = p.length * sizeelem;
596 auto info = gc_query(p.data);
598 if (info.size <= newsize || info.base != p.data)
600 if (info.size >= PAGESIZE && info.base == p.data)
601 { // Try to extend in-place
602 auto u = gc_extend(p.data, (newsize + 1) - info.size, (newsize + 1) - info.size);
608 newdata = cast(byte *)gc_malloc(newsize + 1, info.attr);
609 newdata[0 .. size] = p.data[0 .. size];
612 newdata[size .. newsize] = 0;
617 newdata = cast(byte *)gc_calloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
626 p.length = newlength;
627 return newdata[0 .. newlength];
630 onOutOfMemoryError();
635 * Resize arrays for non-zero initializers.
636 * p pointer to array lvalue to be updated
637 * newlength new .length property of array
638 * sizeelem size of each element of array
639 * initsize size of initializer
642 extern (C) byte[] _d_arraysetlengthiT(TypeInfo ti, size_t newlength, Array *p)
645 assert(!p.length || p.data);
650 size_t sizeelem = ti.next.tsize();
651 void[] initializer = ti.next.init();
652 size_t initsize = initializer.length;
656 assert(initsize <= sizeelem);
657 assert((sizeelem / initsize) * initsize == sizeelem);
661 printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d, initsize = %d)\n", p, sizeelem, newlength, initsize);
663 printf("\tp.data = %p, p.length = %d\n", p.data, p.length);
668 version (D_InlineAsm_X86)
670 size_t newsize = void;
682 size_t newsize = sizeelem * newlength;
684 if (newsize / newlength != sizeelem)
687 debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength);
689 size_t size = p.length * sizeelem;
694 if (newlength > p.length)
696 auto info = gc_query(p.data);
698 if (info.size <= newsize || info.base != p.data)
700 if (info.size >= PAGESIZE && info.base == p.data)
701 { // Try to extend in-place
702 auto u = gc_extend(p.data, (newsize + 1) - info.size, (newsize + 1) - info.size);
708 newdata = cast(byte *)gc_malloc(newsize + 1, info.attr);
709 newdata[0 .. size] = p.data[0 .. size];
716 newdata = cast(byte *)gc_malloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
719 auto q = initializer.ptr; // pointer to initializer
725 debug(PRINTF) printf("newdata = %p, size = %d, newsize = %d, *q = %d\n", newdata, size, newsize, *cast(byte*)q);
726 newdata[size .. newsize] = *(cast(byte*)q);
730 for (size_t u = size; u < newsize; u += initsize)
732 memcpy(newdata + u, q, initsize);
743 p.length = newlength;
744 return newdata[0 .. newlength];
747 onOutOfMemoryError();
752 * Append y[] to array x[].
753 * size is size of each array element.
755 extern (C) long _d_arrayappendT(TypeInfo ti, Array *px, byte[] y)
757 auto sizeelem = ti.next.tsize(); // array element size
758 auto info = gc_query(px.data);
759 auto length = px.length;
760 auto newlength = length + y.length;
761 auto newsize = newlength * sizeelem;
763 if (info.size < newsize || info.base != px.data)
766 if (info.size >= PAGESIZE && info.base == px.data)
767 { // Try to extend in-place
768 auto u = gc_extend(px.data, (newsize + 1) - info.size, (newsize + 1) - info.size);
774 newdata = cast(byte *)gc_malloc(newCapacity(newlength, sizeelem) + 1, info.attr);
775 memcpy(newdata, px.data, length * sizeelem);
779 px.length = newlength;
780 memcpy(px.data + length * sizeelem, y.ptr, y.length * sizeelem);
781 return *cast(long*)px;
788 size_t newCapacity(size_t newlength, size_t size)
792 size_t newcap = newlength * size;
797 * Better version by Dave Fladebo:
798 * This uses an inverse logorithmic algorithm to pre-allocate a bit more
799 * space for larger arrays.
800 * - Arrays smaller than PAGESIZE bytes are left as-is, so for the most
801 * common cases, memory allocation is 1 to 1. The small overhead added
802 * doesn't affect small array perf. (it's virtually the same as
804 * - Larger arrays have some space pre-allocated.
805 * - As the arrays grow, the relative pre-allocated space shrinks.
806 * - The logorithmic algorithm allocates relatively more space for
807 * mid-size arrays, making it very fast for medium arrays (for
808 * mid-to-large arrays, this turns out to be quite a bit faster than the
809 * equivalent realloc() code in C, on Linux at least. Small arrays are
810 * just as fast as GCC).
811 * - Perhaps most importantly, overall memory usage and stress on the GC
812 * is decreased significantly for demanding environments.
814 size_t newcap = newlength * size;
817 if (newcap > PAGESIZE)
819 //double mult2 = 1.0 + (size / log10(pow(newcap * 2.0,2.0)));
821 // redo above line using only integer math
823 static int log2plus1(size_t c)
829 for (i = 1; c >>= 1; i++)
835 /* The following setting for mult sets how much bigger
836 * the new size will be over what is actually needed.
837 * 100 means the same size, more means proportionally more.
838 * More means faster but more memory consumption.
840 //long mult = 100 + (1000L * size) / (6 * log2plus1(newcap));
841 long mult = 100 + (1000L * size) / log2plus1(newcap);
843 // testing shows 1.02 for large arrays is about the point of diminishing return
846 newext = cast(size_t)((newcap * mult) / 100);
847 newext -= newext % size;
848 debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/100.0,newext / cast(double)size);
850 newcap = newext > newcap ? newext : newcap;
851 debug(PRINTF) printf("newcap = %d, newlength = %d, size = %d\n", newcap, newlength, size);
860 extern (C) byte[] _d_arrayappendcT(TypeInfo ti, inout byte[] x, ...)
862 auto sizeelem = ti.next.tsize(); // array element size
863 auto info = gc_query(x.ptr);
864 auto length = x.length;
865 auto newlength = length + 1;
866 auto newsize = newlength * sizeelem;
868 assert(info.size == 0 || length * sizeelem <= info.size);
870 debug(PRINTF) printf("_d_arrayappendcT(sizeelem = %d, ptr = %p, length = %d, cap = %d)\n", sizeelem, x.ptr, x.length, info.size);
872 if (info.size <= newsize || info.base != x.ptr)
875 if (info.size >= PAGESIZE && info.base == x.ptr)
876 { // Try to extend in-place
877 auto u = gc_extend(x.ptr, (newsize + 1) - info.size, (newsize + 1) - info.size);
883 debug(PRINTF) printf("_d_arrayappendcT(length = %d, newlength = %d, cap = %d)\n", length, newlength, info.size);
884 auto newcap = newCapacity(newlength, sizeelem);
885 assert(newcap >= newlength * sizeelem);
886 newdata = cast(byte *)gc_malloc(newcap + 1, info.attr);
887 memcpy(newdata, x.ptr, length * sizeelem);
888 (cast(void**)(&x))[1] = newdata;
891 byte *argp = cast(byte *)(&ti + 2);
893 *cast(size_t *)&x = newlength;
894 x.ptr[length * sizeelem .. newsize] = argp[0 .. sizeelem];
895 assert((cast(size_t)x.ptr & 15) == 0);
896 assert(gc_sizeOf(x.ptr) > x.length * sizeelem);
904 extern (C) byte[] _d_arraycatT(TypeInfo ti, byte[] x, byte[] y)
907 auto sizeelem = ti.next.tsize(); // array element size
908 debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d => %d,%p)\n", x.length, x.ptr, y.length, y.ptr, sizeelem, result.length, result.ptr);
909 assert(result.length == x.length + y.length);
910 for (size_t i = 0; i < x.length * sizeelem; i++)
911 assert((cast(byte*)result)[i] == (cast(byte*)x)[i]);
912 for (size_t i = 0; i < y.length * sizeelem; i++)
913 assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]);
915 size_t cap = gc_sizeOf(result.ptr);
916 assert(!cap || cap > result.length * sizeelem);
922 /* Cannot use this optimization because:
927 * will change the contents of b.
935 debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p)\n", x.length, x.ptr, y.length, y.ptr);
936 auto sizeelem = ti.next.tsize(); // array element size
937 debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d)\n", x.length, x.ptr, y.length, y.ptr, sizeelem);
938 size_t xlen = x.length * sizeelem;
939 size_t ylen = y.length * sizeelem;
940 size_t len = xlen + ylen;
945 byte* p = cast(byte*)gc_malloc(len + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
946 memcpy(p, x.ptr, xlen);
947 memcpy(p + xlen, y.ptr, ylen);
949 return p[0 .. x.length + y.length];
956 extern (C) byte[] _d_arraycatnT(TypeInfo ti, uint n, ...)
962 auto size = ti.next.tsize(); // array element size
964 p = cast(byte[]*)(&n + 1);
966 for (i = 0; i < n; i++)
974 a = gc_malloc(length * size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
975 p = cast(byte[]*)(&n + 1);
978 for (i = 0; i < n; i++)
983 memcpy(a + j, b.ptr, b.length * size);
984 j += b.length * size;
989 *cast(int *)&result = length; // jam length
990 (cast(void **)&result)[1] = a; // jam ptr
998 extern (C) void* _d_arrayliteralT(TypeInfo ti, size_t length, ...)
1000 auto sizeelem = ti.next.tsize(); // array element size
1003 debug(PRINTF) printf("_d_arrayliteralT(sizeelem = %d, length = %d)\n", sizeelem, length);
1004 if (length == 0 || sizeelem == 0)
1008 result = gc_malloc(length * sizeelem, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
1011 va_start!(size_t)(q, length);
1013 size_t stacksize = (sizeelem + int.sizeof - 1) & ~(int.sizeof - 1);
1015 if (stacksize == sizeelem)
1017 memcpy(result, q, length * sizeelem);
1021 for (size_t i = 0; i < length; i++)
1023 memcpy(result + i * sizeelem, q, sizeelem);
1035 * Support for array.dup property.
1047 extern (C) long _adDupT(TypeInfo ti, Array2 a)
1050 auto sizeelem = ti.next.tsize(); // array element size
1051 assert(memcmp((*cast(Array2*)&result).ptr, a.ptr, a.length * sizeelem) == 0);
1059 auto sizeelem = ti.next.tsize(); // array element size
1060 auto size = a.length * sizeelem;
1061 r.ptr = gc_malloc(size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
1062 r.length = a.length;
1063 memcpy(r.ptr, a.ptr, size);
1065 return *cast(long*)(&r);
1076 a[0] = 1; a[1] = 2; a[2] = 3;
1078 assert(b.length == 3);
1079 for (i = 0; i < 3; i++)
1080 assert(b[i] == i + 1);