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) Object _d_newclass(ClassInfo ci)
91 debug(PRINTF) printf("_d_newclass(ci = %p, %s)\n", ci, cast(char *)ci.name);
92 if (ci.flags & 1) // if COM object
93 { /* COM objects are not garbage collected, they are reference counted
94 * using AddRef() and Release(). They get free'd by C's free()
95 * function called by Release() when Release()'s reference count goes
98 p = malloc(ci.init.length);
100 onOutOfMemoryError();
104 p = gc_malloc(ci.init.length,
105 BlkAttr.FINALIZE | (ci.flags & 2 ? BlkAttr.NO_SCAN : 0));
106 debug(PRINTF) printf(" p = %p\n", p);
111 printf("p = %p\n", p);
112 printf("ci = %p, ci.init = %p, len = %d\n", ci, ci.init, ci.init.length);
113 printf("vptr = %p\n", *cast(void**) ci.init);
114 printf("vtbl[0] = %p\n", (*cast(void***) ci.init)[0]);
115 printf("vtbl[1] = %p\n", (*cast(void***) ci.init)[1]);
116 printf("init[0] = %x\n", (cast(uint*) ci.init)[0]);
117 printf("init[1] = %x\n", (cast(uint*) ci.init)[1]);
118 printf("init[2] = %x\n", (cast(uint*) ci.init)[2]);
119 printf("init[3] = %x\n", (cast(uint*) ci.init)[3]);
120 printf("init[4] = %x\n", (cast(uint*) ci.init)[4]);
124 (cast(byte*) p)[0 .. ci.init.length] = ci.init[];
126 debug(PRINTF) printf("initialization done\n");
127 return cast(Object) p;
134 extern (C) void _d_delinterface(void** p)
138 Interface* pi = **cast(Interface ***)*p;
139 Object o = cast(Object)(*p - pi.offset);
148 private extern (D) alias void (*fp_t)(Object);
154 extern (C) void _d_delclass(Object* p)
158 debug(PRINTF) printf("_d_delclass(%p)\n", *p);
160 ClassInfo **pc = cast(ClassInfo **)*p;
165 rt_finalize(cast(void*) *p);
169 fp_t fp = cast(fp_t)c.deallocator;
170 (*fp)(*p); // call deallocator
177 rt_finalize(cast(void*) *p);
179 gc_free(cast(void*) *p);
186 * Allocate a new array of length elements.
187 * ti is the type of the resulting array, or pointer to element.
188 * (For when the array is initialized to 0)
190 extern (C) ulong _d_newarrayT(TypeInfo ti, size_t length)
194 auto size = ti.next.tsize(); // array element size
196 debug(PRINTF) printf("_d_newarrayT(length = x%x, size = %d)\n", length, size);
197 if (length == 0 || size == 0)
201 version (D_InlineAsm_X86)
213 p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
214 debug(PRINTF) printf(" p = %p\n", p);
216 result = cast(ulong)length + (cast(ulong)cast(uint)p << 32);
221 onOutOfMemoryError();
225 * For when the array has a non-zero initializer.
227 extern (C) ulong _d_newarrayiT(TypeInfo ti, size_t length)
230 auto size = ti.next.tsize(); // array element size
232 debug(PRINTF) printf("_d_newarrayiT(length = %d, size = %d)\n", length, size);
234 if (length == 0 || size == 0)
238 auto initializer = ti.next.init();
239 auto isize = initializer.length;
240 auto q = initializer.ptr;
241 version (D_InlineAsm_X86)
253 auto p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
254 debug(PRINTF) printf(" p = %p\n", p);
256 memset(p, *cast(ubyte*)q, size);
257 else if (isize == int.sizeof)
259 int init = *cast(int*)q;
261 for (size_t u = 0; u < size; u++)
263 (cast(int*)p)[u] = init;
268 for (size_t u = 0; u < size; u += isize)
270 memcpy(p + u, q, isize);
274 result = cast(ulong)length + (cast(ulong)cast(uint)p << 32);
279 onOutOfMemoryError();
285 extern (C) ulong _d_newarraymT(TypeInfo ti, int ndims, ...)
289 debug(PRINTF) printf("_d_newarraymT(ndims = %d)\n", ndims);
294 va_start!(int)(q, ndims);
296 void[] foo(TypeInfo ti, size_t* pdim, int ndims)
301 debug(PRINTF) printf("foo(ti = %p, ti.next = %p, dim = %d, ndims = %d\n", ti, ti.next, dim, ndims);
304 auto r = _d_newarrayT(ti, dim);
305 p = *cast(void[]*)(&r);
309 p = gc_malloc(dim * (void[]).sizeof + 1)[0 .. dim];
310 for (int i = 0; i < dim; i++)
312 (cast(void[]*)p.ptr)[i] = foo(ti.next, pdim + 1, ndims - 1);
318 size_t* pdim = cast(size_t *)q;
319 result = cast(ulong)foo(ti, pdim, ndims);
320 debug(PRINTF) printf("result = %llx\n", result);
324 for (int i = 0; i < ndims; i++)
326 printf("index %d: %d\n", i, va_arg!(int)(q));
338 extern (C) ulong _d_newarraymiT(TypeInfo ti, int ndims, ...)
342 debug(PRINTF) printf("_d_newarraymiT(ndims = %d)\n", ndims);
348 va_start!(int)(q, ndims);
350 void[] foo(TypeInfo ti, size_t* pdim, int ndims)
357 auto r = _d_newarrayiT(ti, dim);
358 p = *cast(void[]*)(&r);
362 p = gc_malloc(dim * (void[]).sizeof + 1)[0 .. dim];
363 for (int i = 0; i < dim; i++)
365 (cast(void[]*)p.ptr)[i] = foo(ti.next, pdim + 1, ndims - 1);
371 size_t* pdim = cast(size_t *)q;
372 result = cast(ulong)foo(ti, pdim, ndims);
373 debug(PRINTF) printf("result = %llx\n", result);
377 for (int i = 0; i < ndims; i++)
379 printf("index %d: %d\n", i, va_arg!(int)(q));
380 printf("init = %d\n", va_arg!(int)(q));
402 void* _d_allocmemory(size_t nbytes)
404 return gc_malloc(nbytes);
411 extern (C) void _d_delarray(Array *p)
415 assert(!p.length || p.data);
428 extern (C) void _d_delmemory(void* *p)
441 extern (C) void _d_callinterfacefinalizer(void *p)
445 Interface *pi = **cast(Interface ***)p;
446 Object o = cast(Object)(p - pi.offset);
447 rt_finalize(cast(void*)o);
455 extern (C) void _d_callfinalizer(void* p)
464 extern (C) void rt_setCollectHandler(CollectHandler h)
473 extern (C) void rt_finalize(void* p, bool det = true)
475 debug(PRINTF) printf("rt_finalize(p = %p)\n", p);
477 if (p) // not necessary if called from gc
479 ClassInfo** pc = cast(ClassInfo**)p;
487 if (det || collectHandler is null || collectHandler(cast(Object)p))
493 fp_t fp = cast(fp_t)c.destructor;
494 (*fp)(cast(Object)p); // call destructor
499 if ((cast(void**)p)[1]) // if monitor is not null
500 _d_monitordelete(cast(Object)p, det);
504 onFinalizeError(**pc, e);
508 *pc = null; // zero vptr
516 * Resize dynamic arrays with 0 initializers.
518 extern (C) byte[] _d_arraysetlengthT(TypeInfo ti, size_t newlength, Array *p)
522 assert(!p.length || p.data);
527 size_t sizeelem = ti.next.tsize();
531 printf("_d_arraysetlengthT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
533 printf("\tp.data = %p, p.length = %d\n", p.data, p.length);
538 version (D_InlineAsm_X86)
540 size_t newsize = void;
552 size_t newsize = sizeelem * newlength;
554 if (newsize / newlength != sizeelem)
558 debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength);
563 if (newlength > p.length)
565 size_t size = p.length * sizeelem;
566 auto info = gc_query(p.data);
568 if (info.size <= newsize || info.base != p.data)
570 if (info.size >= PAGESIZE && info.base == p.data)
571 { // Try to extend in-place
572 auto u = gc_extend(p.data, (newsize + 1) - info.size, (newsize + 1) - info.size);
578 newdata = cast(byte *)gc_malloc(newsize + 1, info.attr);
579 newdata[0 .. size] = p.data[0 .. size];
582 newdata[size .. newsize] = 0;
587 newdata = cast(byte *)gc_calloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
596 p.length = newlength;
597 return newdata[0 .. newlength];
600 onOutOfMemoryError();
605 * Resize arrays for non-zero initializers.
606 * p pointer to array lvalue to be updated
607 * newlength new .length property of array
608 * sizeelem size of each element of array
609 * initsize size of initializer
612 extern (C) byte[] _d_arraysetlengthiT(TypeInfo ti, size_t newlength, Array *p)
615 assert(!p.length || p.data);
620 size_t sizeelem = ti.next.tsize();
621 void[] initializer = ti.next.init();
622 size_t initsize = initializer.length;
626 assert(initsize <= sizeelem);
627 assert((sizeelem / initsize) * initsize == sizeelem);
631 printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d, initsize = %d)\n", p, sizeelem, newlength, initsize);
633 printf("\tp.data = %p, p.length = %d\n", p.data, p.length);
638 version (D_InlineAsm_X86)
640 size_t newsize = void;
652 size_t newsize = sizeelem * newlength;
654 if (newsize / newlength != sizeelem)
657 debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength);
659 size_t size = p.length * sizeelem;
664 if (newlength > p.length)
666 auto info = gc_query(p.data);
668 if (info.size <= newsize || info.base != p.data)
670 if (info.size >= PAGESIZE && info.base == p.data)
671 { // Try to extend in-place
672 auto u = gc_extend(p.data, (newsize + 1) - info.size, (newsize + 1) - info.size);
678 newdata = cast(byte *)gc_malloc(newsize + 1, info.attr);
679 newdata[0 .. size] = p.data[0 .. size];
686 newdata = cast(byte *)gc_malloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
689 auto q = initializer.ptr; // pointer to initializer
695 debug(PRINTF) printf("newdata = %p, size = %d, newsize = %d, *q = %d\n", newdata, size, newsize, *cast(byte*)q);
696 newdata[size .. newsize] = *(cast(byte*)q);
700 for (size_t u = size; u < newsize; u += initsize)
702 memcpy(newdata + u, q, initsize);
713 p.length = newlength;
714 return newdata[0 .. newlength];
717 onOutOfMemoryError();
722 * Append y[] to array x[].
723 * size is size of each array element.
725 extern (C) long _d_arrayappendT(TypeInfo ti, Array *px, byte[] y)
727 auto sizeelem = ti.next.tsize(); // array element size
728 auto info = gc_query(px.data);
729 auto length = px.length;
730 auto newlength = length + y.length;
731 auto newsize = newlength * sizeelem;
733 if (info.size < newsize || info.base != px.data)
736 if (info.size >= PAGESIZE && info.base == px.data)
737 { // Try to extend in-place
738 auto u = gc_extend(px.data, (newsize + 1) - info.size, (newsize + 1) - info.size);
744 newdata = cast(byte *)gc_malloc(newCapacity(newlength, sizeelem) + 1, info.attr);
745 memcpy(newdata, px.data, length * sizeelem);
749 px.length = newlength;
750 memcpy(px.data + length * sizeelem, y.ptr, y.length * sizeelem);
751 return *cast(long*)px;
758 size_t newCapacity(size_t newlength, size_t size)
762 size_t newcap = newlength * size;
767 * Better version by Dave Fladebo:
768 * This uses an inverse logorithmic algorithm to pre-allocate a bit more
769 * space for larger arrays.
770 * - Arrays smaller than PAGESIZE bytes are left as-is, so for the most
771 * common cases, memory allocation is 1 to 1. The small overhead added
772 * doesn't affect small array perf. (it's virtually the same as
774 * - Larger arrays have some space pre-allocated.
775 * - As the arrays grow, the relative pre-allocated space shrinks.
776 * - The logorithmic algorithm allocates relatively more space for
777 * mid-size arrays, making it very fast for medium arrays (for
778 * mid-to-large arrays, this turns out to be quite a bit faster than the
779 * equivalent realloc() code in C, on Linux at least. Small arrays are
780 * just as fast as GCC).
781 * - Perhaps most importantly, overall memory usage and stress on the GC
782 * is decreased significantly for demanding environments.
784 size_t newcap = newlength * size;
787 if (newcap > PAGESIZE)
789 //double mult2 = 1.0 + (size / log10(pow(newcap * 2.0,2.0)));
791 // redo above line using only integer math
793 static int log2plus1(size_t c)
799 for (i = 1; c >>= 1; i++)
805 /* The following setting for mult sets how much bigger
806 * the new size will be over what is actually needed.
807 * 100 means the same size, more means proportionally more.
808 * More means faster but more memory consumption.
810 //long mult = 100 + (1000L * size) / (6 * log2plus1(newcap));
811 long mult = 100 + (1000L * size) / log2plus1(newcap);
813 // testing shows 1.02 for large arrays is about the point of diminishing return
816 newext = cast(size_t)((newcap * mult) / 100);
817 newext -= newext % size;
818 debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/100.0,newext / cast(double)size);
820 newcap = newext > newcap ? newext : newcap;
821 debug(PRINTF) printf("newcap = %d, newlength = %d, size = %d\n", newcap, newlength, size);
830 extern (C) byte[] _d_arrayappendcT(TypeInfo ti, inout byte[] x, ...)
832 auto sizeelem = ti.next.tsize(); // array element size
833 auto info = gc_query(x.ptr);
834 auto length = x.length;
835 auto newlength = length + 1;
836 auto newsize = newlength * sizeelem;
838 assert(info.size == 0 || length * sizeelem <= info.size);
840 debug(PRINTF) printf("_d_arrayappendcT(sizeelem = %d, ptr = %p, length = %d, cap = %d)\n", sizeelem, x.ptr, x.length, info.size);
842 if (info.size <= newsize || info.base != x.ptr)
845 if (info.size >= PAGESIZE && info.base == x.ptr)
846 { // Try to extend in-place
847 auto u = gc_extend(x.ptr, (newsize + 1) - info.size, (newsize + 1) - info.size);
853 debug(PRINTF) printf("_d_arrayappendcT(length = %d, newlength = %d, cap = %d)\n", length, newlength, info.size);
854 auto newcap = newCapacity(newlength, sizeelem);
855 assert(newcap >= newlength * sizeelem);
856 newdata = cast(byte *)gc_malloc(newcap + 1, info.attr);
857 memcpy(newdata, x.ptr, length * sizeelem);
858 (cast(void**)(&x))[1] = newdata;
861 byte *argp = cast(byte *)(&ti + 2);
863 *cast(size_t *)&x = newlength;
864 x.ptr[length * sizeelem .. newsize] = argp[0 .. sizeelem];
865 assert((cast(size_t)x.ptr & 15) == 0);
866 assert(gc_sizeOf(x.ptr) > x.length * sizeelem);
874 extern (C) byte[] _d_arraycatT(TypeInfo ti, byte[] x, byte[] y)
877 auto sizeelem = ti.next.tsize(); // array element size
878 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);
879 assert(result.length == x.length + y.length);
880 for (size_t i = 0; i < x.length * sizeelem; i++)
881 assert((cast(byte*)result)[i] == (cast(byte*)x)[i]);
882 for (size_t i = 0; i < y.length * sizeelem; i++)
883 assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]);
885 size_t cap = gc_sizeOf(result.ptr);
886 assert(!cap || cap > result.length * sizeelem);
892 /* Cannot use this optimization because:
897 * will change the contents of b.
905 debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p)\n", x.length, x.ptr, y.length, y.ptr);
906 auto sizeelem = ti.next.tsize(); // array element size
907 debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d)\n", x.length, x.ptr, y.length, y.ptr, sizeelem);
908 size_t xlen = x.length * sizeelem;
909 size_t ylen = y.length * sizeelem;
910 size_t len = xlen + ylen;
915 byte* p = cast(byte*)gc_malloc(len + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
916 memcpy(p, x.ptr, xlen);
917 memcpy(p + xlen, y.ptr, ylen);
919 return p[0 .. x.length + y.length];
926 extern (C) byte[] _d_arraycatnT(TypeInfo ti, uint n, ...)
932 auto size = ti.next.tsize(); // array element size
934 p = cast(byte[]*)(&n + 1);
936 for (i = 0; i < n; i++)
944 a = gc_malloc(length * size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
945 p = cast(byte[]*)(&n + 1);
948 for (i = 0; i < n; i++)
953 memcpy(a + j, b.ptr, b.length * size);
954 j += b.length * size;
959 *cast(int *)&result = length; // jam length
960 (cast(void **)&result)[1] = a; // jam ptr
968 extern (C) void* _d_arrayliteralT(TypeInfo ti, size_t length, ...)
970 auto sizeelem = ti.next.tsize(); // array element size
973 debug(PRINTF) printf("_d_arrayliteralT(sizeelem = %d, length = %d)\n", sizeelem, length);
974 if (length == 0 || sizeelem == 0)
978 result = gc_malloc(length * sizeelem, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
981 va_start!(size_t)(q, length);
983 size_t stacksize = (sizeelem + int.sizeof - 1) & ~(int.sizeof - 1);
985 if (stacksize == sizeelem)
987 memcpy(result, q, length * sizeelem);
991 for (size_t i = 0; i < length; i++)
993 memcpy(result + i * sizeelem, q, sizeelem);
1005 * Support for array.dup property.
1017 extern (C) long _adDupT(TypeInfo ti, Array2 a)
1020 auto sizeelem = ti.next.tsize(); // array element size
1021 assert(memcmp((*cast(Array2*)&result).ptr, a.ptr, a.length * sizeelem) == 0);
1029 auto sizeelem = ti.next.tsize(); // array element size
1030 auto size = a.length * sizeelem;
1031 r.ptr = gc_malloc(size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0);
1032 r.length = a.length;
1033 memcpy(r.ptr, a.ptr, size);
1035 return *cast(long*)(&r);
1046 a[0] = 1; a[1] = 2; a[2] = 3;
1048 assert(b.length == 3);
1049 for (i = 0; i < 3; i++)
1050 assert(b[i] == i + 1);