PageRenderTime 59ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/lphobos/gc/gcx.d

https://bitbucket.org/lindquist/ldc/
D | 2681 lines | 1911 code | 423 blank | 347 comment | 372 complexity | 3fb13c24ef939fe28f405f5e96843672 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0

Large files files are truncated, but you can click here to view the full file

  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. /* NOTE: This file has been patched from the original DMD distribution to
  27. work with the GDC compiler.
  28. Modified by David Friedman, July 2006
  29. */
  30. module gcx;
  31. // D Garbage Collector implementation
  32. /************** Debugging ***************************/
  33. //debug = PRINTF; // turn on printf's
  34. //debug = COLLECT_PRINTF; // turn on printf's
  35. //debug = THREADINVARIANT; // check thread integrity
  36. //debug = LOGGING; // log allocations / frees
  37. //debug = MEMSTOMP; // stomp on memory
  38. //debug = SENTINEL; // add underrun/overrrun protection
  39. //debug = PTRCHECK; // more pointer checking
  40. //debug = PTRCHECK2; // thorough but slow pointer checking
  41. /*************** Configuration *********************/
  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. private import gcbits;
  48. private import gcstats;
  49. private import cstdlib = std.c.stdlib : calloc, free, malloc, realloc;
  50. private import cstring = std.c.string : memcpy, memmove, memset;
  51. debug private import std.c.stdio;
  52. import std.outofmemory;
  53. import std.gc;
  54. version (GNU)
  55. {
  56. private import gcc.builtins;
  57. }
  58. version (Win32)
  59. {
  60. import win32;
  61. import std.c.windows.windows;
  62. }
  63. else version (GNU)
  64. {
  65. private import gcgcc;
  66. }
  67. else version (linux)
  68. {
  69. import gclinux;
  70. }
  71. /*version (BigEndian)
  72. private import std.intrinsic;*/
  73. version (MULTI_THREADED)
  74. {
  75. import std.thread;
  76. }
  77. private void onOutOfMemoryError()
  78. {
  79. _d_OutOfMemory();
  80. }
  81. private void* rt_stackBottom()
  82. {
  83. version (Win32)
  84. {
  85. return win32.os_query_stackBottom();
  86. }
  87. else version (GNU)
  88. {
  89. return gcgcc.os_query_stackBottom();
  90. }
  91. else version (linux)
  92. {
  93. return gclinux.os_query_stackBottom();
  94. }
  95. }
  96. private bool thread_needLock()
  97. {
  98. return std.thread.Thread.nthreads != 1;
  99. }
  100. alias GC gc_t;
  101. version (X86) version (D_InlineAsm) { version = Asm86; }
  102. /* ======================= Leak Detector =========================== */
  103. debug (LOGGING)
  104. {
  105. struct Log
  106. {
  107. void* p;
  108. size_t size;
  109. uint line;
  110. char* file;
  111. void* parent;
  112. void print()
  113. {
  114. printf(" p = %x, size = %d, parent = %x ", p, size, parent);
  115. if (file)
  116. {
  117. printf("%s(%u)", file, line);
  118. }
  119. printf("\n");
  120. }
  121. }
  122. struct LogArray
  123. {
  124. size_t dim;
  125. size_t allocdim;
  126. Log *data;
  127. void Dtor()
  128. {
  129. if (data)
  130. cstdlib.free(data);
  131. data = null;
  132. }
  133. void reserve(size_t nentries)
  134. {
  135. assert(dim <= allocdim);
  136. if (allocdim - dim < nentries)
  137. {
  138. allocdim = (dim + nentries) * 2;
  139. assert(dim + nentries <= allocdim);
  140. if (!data)
  141. {
  142. data = cast(Log *)cstdlib.malloc(allocdim * Log.sizeof);
  143. if (!data && allocdim)
  144. onOutOfMemoryError();
  145. }
  146. else
  147. { Log *newdata;
  148. newdata = cast(Log *)cstdlib.malloc(allocdim * Log.sizeof);
  149. if (!newdata && allocdim)
  150. onOutOfMemoryError();
  151. cstring.memcpy(newdata, data, dim * Log.sizeof);
  152. cstdlib.free(data);
  153. data = newdata;
  154. }
  155. }
  156. }
  157. void push(Log log)
  158. {
  159. reserve(1);
  160. data[dim++] = log;
  161. }
  162. void remove(size_t i)
  163. {
  164. cstring.memmove(data + i, data + i + 1, (dim - i) * Log.sizeof);
  165. dim--;
  166. }
  167. size_t find(void *p)
  168. {
  169. for (size_t i = 0; i < dim; i++)
  170. {
  171. if (data[i].p == p)
  172. return i;
  173. }
  174. return ~0u; // not found
  175. }
  176. void copy(LogArray *from)
  177. {
  178. reserve(from.dim - dim);
  179. assert(from.dim <= allocdim);
  180. cstring.memcpy(data, from.data, from.dim * Log.sizeof);
  181. dim = from.dim;
  182. }
  183. }
  184. }
  185. /* ============================ GC =============================== */
  186. alias void (*GC_FINALIZER)(void *p, bool dummy);
  187. class GCLock { } // just a dummy so we can get a global lock
  188. const uint GCVERSION = 1; // increment every time we change interface
  189. // to GC.
  190. class GC
  191. {
  192. // For passing to debug code
  193. static size_t line;
  194. static char* file;
  195. uint gcversion = GCVERSION;
  196. Gcx *gcx; // implementation
  197. static ClassInfo gcLock; // global lock
  198. void initialize()
  199. {
  200. gcLock = GCLock.classinfo;
  201. gcx = cast(Gcx *)cstdlib.calloc(1, Gcx.sizeof);
  202. if (!gcx)
  203. onOutOfMemoryError();
  204. printf("GCX set to %p\n", gcx);
  205. gcx.initialize();
  206. setStackBottom(rt_stackBottom());
  207. }
  208. void Dtor()
  209. {
  210. version (linux)
  211. {
  212. //debug(PRINTF) printf("Thread %x ", pthread_self());
  213. //debug(PRINTF) printf("GC.Dtor()\n");
  214. }
  215. if (gcx)
  216. {
  217. gcx.Dtor();
  218. cstdlib.free(gcx);
  219. gcx = null;
  220. }
  221. }
  222. invariant()
  223. {
  224. if (gcx)
  225. {
  226. gcx.thread_Invariant();
  227. }
  228. }
  229. /**
  230. *
  231. */
  232. void enable()
  233. {
  234. if (!thread_needLock())
  235. {
  236. assert(gcx.disabled > 0);
  237. gcx.disabled--;
  238. }
  239. else synchronized (gcLock)
  240. {
  241. assert(gcx.disabled > 0);
  242. gcx.disabled--;
  243. }
  244. }
  245. /**
  246. *
  247. */
  248. void disable()
  249. {
  250. if (!thread_needLock())
  251. {
  252. gcx.disabled++;
  253. }
  254. else synchronized (gcLock)
  255. {
  256. gcx.disabled++;
  257. }
  258. }
  259. /**
  260. *
  261. */
  262. void *malloc(size_t size)
  263. {
  264. if (!size)
  265. {
  266. return null;
  267. }
  268. if (!thread_needLock())
  269. {
  270. return mallocNoSync(size);
  271. }
  272. else synchronized (gcLock)
  273. {
  274. return mallocNoSync(size);
  275. }
  276. }
  277. //
  278. //
  279. //
  280. private void *mallocNoSync(size_t size)
  281. {
  282. assert(size != 0);
  283. void *p = null;
  284. Bins bin;
  285. //debug(PRINTF) printf("GC::malloc(size = %d, gcx = %p)\n", size, gcx);
  286. assert(gcx);
  287. //debug(PRINTF) printf("gcx.self = %x, pthread_self() = %x\n", gcx.self, pthread_self());
  288. size += SENTINEL_EXTRA;
  289. // Compute size bin
  290. // Cache previous binsize lookup - Dave Fladebo.
  291. static size_t lastsize = -1;
  292. static Bins lastbin;
  293. if (size == lastsize)
  294. bin = lastbin;
  295. else
  296. {
  297. bin = gcx.findBin(size);
  298. lastsize = size;
  299. lastbin = bin;
  300. }
  301. if (bin < B_PAGE)
  302. {
  303. p = gcx.bucket[bin];
  304. if (p == null)
  305. {
  306. if (!gcx.allocPage(bin) && !gcx.disabled) // try to find a new page
  307. {
  308. if (!thread_needLock())
  309. {
  310. /* Then we haven't locked it yet. Be sure
  311. * and lock for a collection, since a finalizer
  312. * may start a new thread.
  313. */
  314. synchronized (gcLock)
  315. {
  316. gcx.fullcollectshell();
  317. }
  318. }
  319. else if (!gcx.fullcollectshell()) // collect to find a new page
  320. {
  321. //gcx.newPool(1);
  322. }
  323. }
  324. if (!gcx.bucket[bin] && !gcx.allocPage(bin))
  325. { int result;
  326. gcx.newPool(1); // allocate new pool to find a new page
  327. result = gcx.allocPage(bin);
  328. if (!result)
  329. return null;
  330. }
  331. p = gcx.bucket[bin];
  332. }
  333. // Return next item from free list
  334. gcx.bucket[bin] = (cast(List *)p).next;
  335. cstring.memset(p + size, 0, binsize[bin] - size);
  336. //debug(PRINTF) printf("\tmalloc => %x\n", p);
  337. debug (MEMSTOMP) cstring.memset(p, 0xF0, size);
  338. }
  339. else
  340. {
  341. p = gcx.bigAlloc(size);
  342. if (!p)
  343. return null;
  344. }
  345. size -= SENTINEL_EXTRA;
  346. p = sentinel_add(p);
  347. sentinel_init(p, size);
  348. gcx.log_malloc(p, size);
  349. return p;
  350. }
  351. /**
  352. *
  353. */
  354. void *calloc(size_t size, size_t n)
  355. {
  356. size_t len = size * n;
  357. if (!len)
  358. {
  359. return null;
  360. }
  361. if (!thread_needLock())
  362. {
  363. return callocNoSync(len);
  364. }
  365. else synchronized (gcLock)
  366. {
  367. return callocNoSync(len);
  368. }
  369. }
  370. //
  371. //
  372. //
  373. private void *callocNoSync(size_t size)
  374. {
  375. assert(size != 0);
  376. void *p = mallocNoSync(size);
  377. if (p)
  378. { //debug(PRINTF) printf("calloc: %x len %d\n", p, len);
  379. cstring.memset(p, 0, size);
  380. }
  381. return p;
  382. }
  383. /**
  384. *
  385. */
  386. void *realloc(void *p, size_t size)
  387. {
  388. if (!thread_needLock())
  389. {
  390. return reallocNoSync(p, size);
  391. }
  392. else synchronized (gcLock)
  393. {
  394. return reallocNoSync(p, size);
  395. }
  396. }
  397. //
  398. //
  399. //
  400. private void *reallocNoSync(void *p, size_t size)
  401. {
  402. gcx.p_cache = null;
  403. if (!size)
  404. { if (p)
  405. { freeNoSync(p);
  406. p = null;
  407. }
  408. }
  409. else if (!p)
  410. {
  411. p = mallocNoSync(size);
  412. }
  413. else
  414. { void *p2;
  415. size_t psize;
  416. //debug(PRINTF) printf("GC::realloc(p = %x, size = %u)\n", p, size);
  417. version (SENTINEL)
  418. {
  419. sentinel_Invariant(p);
  420. psize = *sentinel_size(p);
  421. if (psize != size)
  422. {
  423. p2 = mallocNoSync(size);
  424. if (psize < size)
  425. size = psize;
  426. //debug(PRINTF) printf("\tcopying %d bytes\n",size);
  427. cstring.memcpy(p2, p, size);
  428. p = p2;
  429. }
  430. }
  431. else
  432. {
  433. psize = gcx.findSize(p); // find allocated size
  434. if (psize >= PAGESIZE && size >= PAGESIZE)
  435. {
  436. auto psz = psize / PAGESIZE;
  437. auto newsz = (size + PAGESIZE - 1) / PAGESIZE;
  438. if (newsz == psz)
  439. return p;
  440. auto pool = gcx.findPool(p);
  441. auto pagenum = (p - pool.baseAddr) / PAGESIZE;
  442. if (newsz < psz)
  443. { // Shrink in place
  444. synchronized (gcLock)
  445. {
  446. debug (MEMSTOMP) cstring.memset(p + size, 0xF2, psize - size);
  447. pool.freePages(pagenum + newsz, psz - newsz);
  448. }
  449. return p;
  450. }
  451. else if (pagenum + newsz <= pool.npages)
  452. {
  453. // Attempt to expand in place
  454. synchronized (gcLock)
  455. {
  456. for (size_t i = pagenum + psz; 1;)
  457. {
  458. if (i == pagenum + newsz)
  459. {
  460. debug (MEMSTOMP) cstring.memset(p + psize, 0xF0, size - psize);
  461. cstring.memset(&pool.pagetable[pagenum + psz], B_PAGEPLUS, newsz - psz);
  462. return p;
  463. }
  464. if (i == pool.ncommitted)
  465. {
  466. auto u = pool.extendPages(pagenum + newsz - pool.ncommitted);
  467. if (u == ~0u)
  468. break;
  469. i = pagenum + newsz;
  470. continue;
  471. }
  472. if (pool.pagetable[i] != B_FREE)
  473. break;
  474. i++;
  475. }
  476. }
  477. }
  478. }
  479. if (psize < size || // if new size is bigger
  480. psize > size * 2) // or less than half
  481. {
  482. p2 = mallocNoSync(size);
  483. if (psize < size)
  484. size = psize;
  485. //debug(PRINTF) printf("\tcopying %d bytes\n",size);
  486. cstring.memcpy(p2, p, size);
  487. p = p2;
  488. }
  489. }
  490. }
  491. return p;
  492. }
  493. /**
  494. * Attempt to in-place enlarge the memory block pointed to by p by at least
  495. * minbytes beyond its current capacity, up to a maximum of maxsize. This
  496. * does not attempt to move the memory block (like realloc() does).
  497. *
  498. * Returns:
  499. * 0 if could not extend p,
  500. * total size of entire memory block if successful.
  501. */
  502. size_t extend(void* p, size_t minsize, size_t maxsize)
  503. {
  504. if (!thread_needLock())
  505. {
  506. return extendNoSync(p, minsize, maxsize);
  507. }
  508. else synchronized (gcLock)
  509. {
  510. return extendNoSync(p, minsize, maxsize);
  511. }
  512. }
  513. //
  514. //
  515. //
  516. private size_t extendNoSync(void* p, size_t minsize, size_t maxsize)
  517. in
  518. {
  519. assert( minsize <= maxsize );
  520. }
  521. body
  522. {
  523. //debug(PRINTF) printf("GC::extend(p = %x, minsize = %u, maxsize = %u)\n", p, minsize, maxsize);
  524. version (SENTINEL)
  525. {
  526. return 0;
  527. }
  528. auto psize = gcx.findSize(p); // find allocated size
  529. if (psize < PAGESIZE)
  530. return 0; // cannot extend buckets
  531. auto psz = psize / PAGESIZE;
  532. auto minsz = (minsize + PAGESIZE - 1) / PAGESIZE;
  533. auto maxsz = (maxsize + PAGESIZE - 1) / PAGESIZE;
  534. auto pool = gcx.findPool(p);
  535. auto pagenum = (p - pool.baseAddr) / PAGESIZE;
  536. size_t sz;
  537. for (sz = 0; sz < maxsz; sz++)
  538. {
  539. auto i = pagenum + psz + sz;
  540. if (i == pool.ncommitted)
  541. break;
  542. if (pool.pagetable[i] != B_FREE)
  543. { if (sz < minsz)
  544. return 0;
  545. break;
  546. }
  547. }
  548. if (sz >= minsz)
  549. {
  550. }
  551. else if (pagenum + psz + sz == pool.ncommitted)
  552. {
  553. auto u = pool.extendPages(minsz - sz);
  554. if (u == ~0u)
  555. return 0;
  556. sz = minsz;
  557. }
  558. else
  559. return 0;
  560. debug (MEMSTOMP) cstring.memset(p + psize, 0xF0, (psz + sz) * PAGESIZE - psize);
  561. cstring.memset(pool.pagetable + pagenum + psz, B_PAGEPLUS, sz);
  562. gcx.p_cache = null;
  563. gcx.size_cache = 0;
  564. return (psz + sz) * PAGESIZE;
  565. }
  566. /**
  567. *
  568. */
  569. void free(void *p)
  570. {
  571. if (!p)
  572. {
  573. return;
  574. }
  575. if (!thread_needLock())
  576. {
  577. return freeNoSync(p);
  578. }
  579. else synchronized (gcLock)
  580. {
  581. return freeNoSync(p);
  582. }
  583. }
  584. //
  585. //
  586. //
  587. private void freeNoSync(void *p)
  588. {
  589. assert (p);
  590. Pool *pool;
  591. uint pagenum;
  592. Bins bin;
  593. uint biti;
  594. // Find which page it is in
  595. pool = gcx.findPool(p);
  596. if (!pool) // if not one of ours
  597. return; // ignore
  598. sentinel_Invariant(p);
  599. p = sentinel_sub(p);
  600. pagenum = (p - pool.baseAddr) / PAGESIZE;
  601. biti = cast(uint)(p - pool.baseAddr) / 16;
  602. pool.noscan.clear(biti);
  603. if (pool.finals.nbits && pool.finals.testClear(biti))
  604. gcx.rt_finalize(sentinel_add(p), false);
  605. gcx.p_cache = null;
  606. bin = cast(Bins)pool.pagetable[pagenum];
  607. if (bin == B_PAGE) // if large alloc
  608. { int npages;
  609. uint n;
  610. // Free pages
  611. npages = 1;
  612. n = pagenum;
  613. while (++n < pool.ncommitted && pool.pagetable[n] == B_PAGEPLUS)
  614. npages++;
  615. debug (MEMSTOMP) cstring.memset(p, 0xF2, npages * PAGESIZE);
  616. pool.freePages(pagenum, npages);
  617. }
  618. else
  619. { // Add to free list
  620. List *list = cast(List *)p;
  621. debug (MEMSTOMP) cstring.memset(p, 0xF2, binsize[bin]);
  622. list.next = gcx.bucket[bin];
  623. gcx.bucket[bin] = list;
  624. }
  625. gcx.log_free(sentinel_add(p));
  626. }
  627. /**
  628. * Determine the allocated size of pointer p. If p is an interior pointer
  629. * or not a gc allocated pointer, return 0.
  630. */
  631. size_t capacity(void *p)
  632. {
  633. if (!p)
  634. {
  635. return 0;
  636. }
  637. if (!thread_needLock())
  638. {
  639. return sizeOfNoSync(p);
  640. }
  641. else synchronized (gcLock)
  642. {
  643. return sizeOfNoSync(p);
  644. }
  645. }
  646. //
  647. //
  648. //
  649. private size_t sizeOfNoSync(void *p)
  650. {
  651. assert (p);
  652. version (SENTINEL)
  653. {
  654. p = sentinel_sub(p);
  655. size_t size = gcx.findSize(p);
  656. // Check for interior pointer
  657. // This depends on:
  658. // 1) size is a power of 2 for less than PAGESIZE values
  659. // 2) base of memory pool is aligned on PAGESIZE boundary
  660. if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
  661. size = 0;
  662. return size ? size - SENTINAL_EXTRA : 0;
  663. }
  664. else
  665. {
  666. if (p !is null && p == gcx.p_cache)
  667. return gcx.size_cache;
  668. size_t size = gcx.findSize(p);
  669. // Check for interior pointer
  670. // This depends on:
  671. // 1) size is a power of 2 for less than PAGESIZE values
  672. // 2) base of memory pool is aligned on PAGESIZE boundary
  673. if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
  674. size = 0;
  675. else
  676. {
  677. gcx.p_cache = p;
  678. gcx.size_cache = size;
  679. }
  680. return size;
  681. }
  682. }
  683. /**
  684. * Verify that pointer p:
  685. * 1) belongs to this memory pool
  686. * 2) points to the start of an allocated piece of memory
  687. * 3) is not on a free list
  688. */
  689. void check(void *p)
  690. {
  691. if (!p)
  692. {
  693. return;
  694. }
  695. if (!thread_needLock())
  696. {
  697. checkNoSync(p);
  698. }
  699. else synchronized (gcLock)
  700. {
  701. checkNoSync(p);
  702. }
  703. }
  704. //
  705. //
  706. //
  707. private void checkNoSync(void *p)
  708. {
  709. assert(p);
  710. sentinel_Invariant(p);
  711. debug (PTRCHECK)
  712. {
  713. Pool* pool;
  714. uint pagenum;
  715. Bins bin;
  716. size_t size;
  717. p = sentinel_sub(p);
  718. pool = gcx.findPool(p);
  719. assert(pool);
  720. pagenum = (p - pool.baseAddr) / PAGESIZE;
  721. bin = cast(Bins)pool.pagetable[pagenum];
  722. assert(bin <= B_PAGE);
  723. size = binsize[bin];
  724. assert((cast(size_t)p & (size - 1)) == 0);
  725. debug (PTRCHECK2)
  726. {
  727. if (bin < B_PAGE)
  728. {
  729. // Check that p is not on a free list
  730. List *list;
  731. for (list = gcx.bucket[bin]; list; list = list.next)
  732. {
  733. assert(cast(void *)list != p);
  734. }
  735. }
  736. }
  737. }
  738. }
  739. //
  740. //
  741. //
  742. private void setStackBottom(void *p)
  743. {
  744. version (STACKGROWSDOWN)
  745. {
  746. //p = (void *)((uint *)p + 4);
  747. if (p > gcx.stackBottom)
  748. {
  749. //debug(PRINTF) printf("setStackBottom(%x)\n", p);
  750. gcx.stackBottom = p;
  751. }
  752. }
  753. else
  754. {
  755. //p = (void *)((uint *)p - 4);
  756. if (p < gcx.stackBottom)
  757. {
  758. //debug(PRINTF) printf("setStackBottom(%x)\n", p);
  759. gcx.stackBottom = cast(char *)p;
  760. }
  761. }
  762. }
  763. static void scanStaticData(gc_t g)
  764. {
  765. void *pbot;
  766. void *ptop;
  767. size_t nbytes;
  768. //debug(PRINTF) printf("+GC.scanStaticData()\n");
  769. os_query_staticdataseg(&pbot, &nbytes);
  770. ptop = pbot + nbytes;
  771. version (GNU) {
  772. if (pbot) {
  773. g.addRange(pbot, ptop);
  774. }
  775. } else {
  776. g.addRange(pbot, ptop);
  777. }
  778. //debug(PRINTF) printf("-GC.scanStaticData()\n");
  779. }
  780. static void unscanStaticData(gc_t g)
  781. {
  782. void *pbot;
  783. size_t nbytes;
  784. os_query_staticdataseg(&pbot, &nbytes);
  785. version (GNU) {
  786. if (pbot) {
  787. g.removeRange(pbot);
  788. }
  789. } else {
  790. g.removeRange(pbot);
  791. }
  792. }
  793. /**
  794. * add p to list of roots
  795. */
  796. void addRoot(void *p)
  797. {
  798. if (!p)
  799. {
  800. return;
  801. }
  802. if (!thread_needLock())
  803. {
  804. gcx.addRoot(p);
  805. }
  806. else synchronized (gcLock)
  807. {
  808. gcx.addRoot(p);
  809. }
  810. }
  811. /**
  812. * remove p from list of roots
  813. */
  814. void removeRoot(void *p)
  815. {
  816. if (!p)
  817. {
  818. return;
  819. }
  820. if (!thread_needLock())
  821. {
  822. gcx.removeRoot(p);
  823. }
  824. else synchronized (gcLock)
  825. {
  826. gcx.removeRoot(p);
  827. }
  828. }
  829. /**
  830. * add range to scan for roots
  831. */
  832. void addRange(void *pbot, void *ptop)
  833. {
  834. if (!pbot || !ptop)
  835. {
  836. return;
  837. }
  838. //debug(PRINTF) printf("+GC.addRange(pbot = x%x, ptop = x%x)\n", pbot, ptop);
  839. if (!thread_needLock())
  840. {
  841. gcx.addRange(pbot, ptop);
  842. }
  843. else synchronized (gcLock)
  844. {
  845. gcx.addRange(pbot, ptop);
  846. }
  847. //debug(PRINTF) printf("-GC.addRange()\n");
  848. }
  849. /**
  850. * remove range
  851. */
  852. void removeRange(void *p)
  853. {
  854. if (!p)
  855. {
  856. return;
  857. }
  858. if (!thread_needLock())
  859. {
  860. gcx.removeRange(p);
  861. }
  862. else synchronized (gcLock)
  863. {
  864. gcx.removeRange(p);
  865. }
  866. }
  867. /**
  868. * do full garbage collection
  869. */
  870. void fullCollect()
  871. {
  872. debug(PRINTF) printf("GC.fullCollect()\n");
  873. if (!thread_needLock())
  874. {
  875. gcx.fullcollectshell();
  876. }
  877. else synchronized (gcLock)
  878. {
  879. gcx.fullcollectshell();
  880. }
  881. version (none)
  882. {
  883. GCStats stats;
  884. getStats(stats);
  885. debug(PRINTF) printf("poolsize = %x, usedsize = %x, freelistsize = %x\n",
  886. stats.poolsize, stats.usedsize, stats.freelistsize);
  887. }
  888. gcx.log_collect();
  889. }
  890. /**
  891. * do full garbage collection ignoring roots
  892. */
  893. void fullCollectNoStack()
  894. {
  895. if (!thread_needLock())
  896. {
  897. gcx.noStack++;
  898. gcx.fullcollectshell();
  899. gcx.noStack--;
  900. }
  901. else synchronized (gcLock)
  902. {
  903. gcx.noStack++;
  904. gcx.fullcollectshell();
  905. gcx.noStack--;
  906. }
  907. }
  908. /**
  909. * do generational garbage collection
  910. */
  911. void genCollect()
  912. {
  913. synchronized (gcLock)
  914. {
  915. gcx.fullcollectshell();
  916. }
  917. }
  918. void minimize() // minimize physical memory usage
  919. {
  920. // Not implemented, ignore
  921. }
  922. void setFinalizer(void *p, GC_FINALIZER pFn)
  923. {
  924. synchronized (gcLock)
  925. {
  926. gcx.finalizer = pFn;
  927. gcx.doFinalize(p);
  928. }
  929. }
  930. void hasPointers(void *p)
  931. {
  932. synchronized (gcLock)
  933. {
  934. gcx.HasPointers(p);
  935. }
  936. }
  937. void hasNoPointers(void *p)
  938. {
  939. if (!gcx.conservative)
  940. { synchronized (gcLock)
  941. {
  942. gcx.HasNoPointers(p);
  943. }
  944. }
  945. }
  946. void setV1_0()
  947. {
  948. gcx.conservative = 1;
  949. }
  950. /**
  951. * Retrieve statistics about garbage collection.
  952. * Useful for debugging and tuning.
  953. */
  954. void getStats(out GCStats stats)
  955. {
  956. if (!thread_needLock())
  957. {
  958. getStatsNoSync(stats);
  959. }
  960. else synchronized (gcLock)
  961. {
  962. getStatsNoSync(stats);
  963. }
  964. }
  965. //
  966. //
  967. //
  968. private void getStatsNoSync(out GCStats stats)
  969. {
  970. size_t psize = 0;
  971. size_t usize = 0;
  972. size_t flsize = 0;
  973. size_t n;
  974. size_t bsize = 0;
  975. //debug(PRINTF) printf("getStats()\n");
  976. cstring.memset(&stats, 0, GCStats.sizeof);
  977. for (n = 0; n < gcx.npools; n++)
  978. { Pool *pool = gcx.pooltable[n];
  979. psize += pool.ncommitted * PAGESIZE;
  980. for (uint j = 0; j < pool.ncommitted; j++)
  981. {
  982. Bins bin = cast(Bins)pool.pagetable[j];
  983. if (bin == B_FREE)
  984. stats.freeblocks++;
  985. else if (bin == B_PAGE)
  986. stats.pageblocks++;
  987. else if (bin < B_PAGE)
  988. bsize += PAGESIZE;
  989. }
  990. }
  991. for (n = 0; n < B_PAGE; n++)
  992. {
  993. //debug(PRINTF) printf("bin %d\n", n);
  994. for (List *list = gcx.bucket[n]; list; list = list.next)
  995. {
  996. //debug(PRINTF) printf("\tlist %x\n", list);
  997. flsize += binsize[n];
  998. }
  999. }
  1000. usize = bsize - flsize;
  1001. stats.poolsize = psize;
  1002. stats.usedsize = bsize - flsize;
  1003. stats.freelistsize = flsize;
  1004. }
  1005. }
  1006. /* ============================ Gcx =============================== */
  1007. enum
  1008. { PAGESIZE = 4096,
  1009. COMMITSIZE = (4096*16),
  1010. POOLSIZE = (4096*256),
  1011. }
  1012. enum
  1013. {
  1014. B_16,
  1015. B_32,
  1016. B_64,
  1017. B_128,
  1018. B_256,
  1019. B_512,
  1020. B_1024,
  1021. B_2048,
  1022. B_PAGE, // start of large alloc
  1023. B_PAGEPLUS, // continuation of large alloc
  1024. B_FREE, // free page
  1025. B_UNCOMMITTED, // memory not committed for this page
  1026. B_MAX
  1027. }
  1028. alias ubyte Bins;
  1029. struct List
  1030. {
  1031. List *next;
  1032. }
  1033. struct Range
  1034. {
  1035. void *pbot;
  1036. void *ptop;
  1037. }
  1038. const uint binsize[B_MAX] = [ 16,32,64,128,256,512,1024,2048,4096 ];
  1039. const uint notbinsize[B_MAX] = [ ~(16u-1),~(32u-1),~(64u-1),~(128u-1),~(256u-1),
  1040. ~(512u-1),~(1024u-1),~(2048u-1),~(4096u-1) ];
  1041. /* ============================ Gcx =============================== */
  1042. struct Gcx
  1043. {
  1044. debug (THREADINVARIANT)
  1045. {
  1046. pthread_t self;
  1047. void thread_Invariant()
  1048. {
  1049. if (self != pthread_self())
  1050. printf("thread_Invariant(): gcx = %x, self = %x, pthread_self() = %x\n", this, self, pthread_self());
  1051. assert(self == pthread_self());
  1052. }
  1053. }
  1054. else
  1055. {
  1056. void thread_Invariant() { }
  1057. }
  1058. void *p_cache;
  1059. size_t size_cache;
  1060. size_t nroots;
  1061. size_t rootdim;
  1062. void **roots;
  1063. size_t nranges;
  1064. size_t rangedim;
  1065. Range *ranges;
  1066. uint conservative; // !=0 means conservative behavior
  1067. uint noStack; // !=0 means don't scan stack
  1068. uint log; // turn on logging
  1069. uint anychanges;
  1070. void *stackBottom;
  1071. uint inited;
  1072. int disabled; // turn off collections if >0
  1073. byte *minAddr; // min(baseAddr)
  1074. byte *maxAddr; // max(topAddr)
  1075. uint npools;
  1076. Pool **pooltable;
  1077. List *bucket[B_MAX]; // free list for each size
  1078. GC_FINALIZER finalizer; // finalizer function (one per GC)
  1079. private void rt_finalize( void* p, bool dummy )
  1080. {
  1081. if (finalizer)
  1082. (*finalizer)(p, dummy);
  1083. }
  1084. void initialize()
  1085. { int dummy;
  1086. (cast(byte *)this)[0 .. Gcx.sizeof] = 0;
  1087. stackBottom = cast(char *)&dummy;
  1088. log_init();
  1089. debug (THREADINVARIANT)
  1090. self = pthread_self();
  1091. //printf("gcx = %p, self = %x\n", this, self);
  1092. inited = 1;
  1093. }
  1094. void Dtor()
  1095. {
  1096. inited = 0;
  1097. for (uint i = 0; i < npools; i++)
  1098. { Pool *pool = pooltable[i];
  1099. pool.Dtor();
  1100. cstdlib.free(pool);
  1101. }
  1102. if (pooltable)
  1103. cstdlib.free(pooltable);
  1104. if (roots)
  1105. cstdlib.free(roots);
  1106. if (ranges)
  1107. cstdlib.free(ranges);
  1108. }
  1109. void Invariant() { }
  1110. invariant()
  1111. {
  1112. if (inited)
  1113. {
  1114. //printf("Gcx.invariant(): this = %p\n", this);
  1115. uint i;
  1116. // Assure we're called on the right thread
  1117. debug (THREADINVARIANT) assert(self == pthread_self());
  1118. for (i = 0; i < npools; i++)
  1119. { Pool *pool = pooltable[i];
  1120. pool.Invariant();
  1121. if (i == 0)
  1122. {
  1123. assert(minAddr == pool.baseAddr);
  1124. }
  1125. if (i + 1 < npools)
  1126. {
  1127. assert(pool.opCmp(pooltable[i + 1]) < 0);
  1128. }
  1129. else if (i + 1 == npools)
  1130. {
  1131. assert(maxAddr == pool.topAddr);
  1132. }
  1133. }
  1134. if (roots)
  1135. {
  1136. assert(rootdim != 0);
  1137. assert(nroots <= rootdim);
  1138. }
  1139. if (ranges)
  1140. {
  1141. assert(rangedim != 0);
  1142. assert(nranges <= rangedim);
  1143. for (i = 0; i < nranges; i++)
  1144. {
  1145. assert(ranges[i].pbot);
  1146. assert(ranges[i].ptop);
  1147. assert(ranges[i].pbot <= ranges[i].ptop);
  1148. }
  1149. }
  1150. for (i = 0; i < B_PAGE; i++)
  1151. {
  1152. for (List *list = bucket[i]; list; list = list.next)
  1153. {
  1154. }
  1155. }
  1156. }
  1157. }
  1158. /**
  1159. *
  1160. */
  1161. void addRoot(void *p)
  1162. {
  1163. if (nroots == rootdim)
  1164. {
  1165. size_t newdim = rootdim * 2 + 16;
  1166. void** newroots;
  1167. newroots = cast(void **)cstdlib.malloc(newdim * newroots[0].sizeof);
  1168. if (!newroots)
  1169. onOutOfMemoryError();
  1170. if (roots)
  1171. { cstring.memcpy(newroots, roots, nroots * newroots[0].sizeof);
  1172. cstdlib.free(roots);
  1173. }
  1174. roots = newroots;
  1175. rootdim = newdim;
  1176. }
  1177. roots[nroots] = p;
  1178. nroots++;
  1179. }
  1180. /**
  1181. *
  1182. */
  1183. void removeRoot(void *p)
  1184. {
  1185. for (size_t i = nroots; i--;)
  1186. {
  1187. if (roots[i] == p)
  1188. {
  1189. nroots--;
  1190. cstring.memmove(roots + i, roots + i + 1, (nroots - i) * roots[0].sizeof);
  1191. return;
  1192. }
  1193. }
  1194. assert(0);
  1195. }
  1196. /**
  1197. *
  1198. */
  1199. void addRange(void *pbot, void *ptop)
  1200. {
  1201. debug(THREADINVARIANT) { debug(PRINTF) printf("Thread %x ", pthread_self()); }
  1202. debug(PRINTF) printf("%x.Gcx::addRange(%x, %x), nranges = %d\n", this, pbot, ptop, nranges);
  1203. if (nranges == rangedim)
  1204. {
  1205. size_t newdim = rangedim * 2 + 16;
  1206. Range *newranges;
  1207. newranges = cast(Range *)cstdlib.malloc(newdim * newranges[0].sizeof);
  1208. if (!newranges)
  1209. onOutOfMemoryError();
  1210. if (ranges)
  1211. { cstring.memcpy(newranges, ranges, nranges * newranges[0].sizeof);
  1212. cstdlib.free(ranges);
  1213. }
  1214. ranges = newranges;
  1215. rangedim = newdim;
  1216. }
  1217. ranges[nranges].pbot = pbot;
  1218. ranges[nranges].ptop = ptop;
  1219. nranges++;
  1220. }
  1221. /**
  1222. *
  1223. */
  1224. void removeRange(void *pbot)
  1225. {
  1226. debug(THREADINVARIANT) { debug(PRINTF) printf("Thread %x ", pthread_self()); }
  1227. debug(PRINTF) printf("%x.Gcx.removeRange(%x), nranges = %d\n", this, pbot, nranges);
  1228. for (size_t i = nranges; i--;)
  1229. {
  1230. if (ranges[i].pbot == pbot)
  1231. {
  1232. nranges--;
  1233. cstring.memmove(ranges + i, ranges + i + 1, (nranges - i) * ranges[0].sizeof);
  1234. return;
  1235. }
  1236. }
  1237. debug(PRINTF) printf("Wrong thread\n");
  1238. // This is a fatal error, but ignore it.
  1239. // The problem is that we can get a Close() call on a thread
  1240. // other than the one the range was allocated on.
  1241. //assert(zero);
  1242. }
  1243. /**
  1244. * Find Pool that pointer is in.
  1245. * Return null if not in a Pool.
  1246. * Assume pooltable[] is sorted.
  1247. */
  1248. Pool *findPool(void *p)
  1249. {
  1250. if (p >= minAddr && p < maxAddr)
  1251. {
  1252. if (npools == 1)
  1253. {
  1254. return pooltable[0];
  1255. }
  1256. for (uint i = 0; i < npools; i++)
  1257. { Pool *pool;
  1258. pool = pooltable[i];
  1259. if (p < pool.topAddr)
  1260. { if (pool.baseAddr <= p)
  1261. return pool;
  1262. break;
  1263. }
  1264. }
  1265. }
  1266. return null;
  1267. }
  1268. /**
  1269. * Find size of pointer p.
  1270. * Returns 0 if not a gc'd pointer
  1271. */
  1272. size_t findSize(void *p)
  1273. {
  1274. Pool *pool;
  1275. size_t size = 0;
  1276. pool = findPool(p);
  1277. if (pool)
  1278. {
  1279. uint pagenum;
  1280. Bins bin;
  1281. pagenum = (cast(uint)(p - pool.baseAddr)) / PAGESIZE;
  1282. bin = cast(Bins)pool.pagetable[pagenum];
  1283. size = binsize[bin];
  1284. if (bin == B_PAGE)
  1285. { uint npages = pool.ncommitted;
  1286. ubyte* pt;
  1287. uint i;
  1288. pt = &pool.pagetable[0];
  1289. for (i = pagenum + 1; i < npages; i++)
  1290. {
  1291. if (pt[i] != B_PAGEPLUS)
  1292. break;
  1293. }
  1294. size = (i - pagenum) * PAGESIZE;
  1295. }
  1296. }
  1297. return size;
  1298. }
  1299. /**
  1300. * Compute bin for size.
  1301. */
  1302. static Bins findBin(size_t size)
  1303. { Bins bin;
  1304. if (size <= 256)
  1305. {
  1306. if (size <= 64)
  1307. {
  1308. if (size <= 16)
  1309. bin = B_16;
  1310. else if (size <= 32)
  1311. bin = B_32;
  1312. else
  1313. bin = B_64;
  1314. }
  1315. else
  1316. {
  1317. if (size <= 128)
  1318. bin = B_128;
  1319. else
  1320. bin = B_256;
  1321. }
  1322. }
  1323. else
  1324. {
  1325. if (size <= 1024)
  1326. {
  1327. if (size <= 512)
  1328. bin = B_512;
  1329. else
  1330. bin = B_1024;
  1331. }
  1332. else
  1333. {
  1334. if (size <= 2048)
  1335. bin = B_2048;
  1336. else
  1337. bin = B_PAGE;
  1338. }
  1339. }
  1340. return bin;
  1341. }
  1342. /**
  1343. * Allocate a chunk of memory that is larger than a page.
  1344. * Return null if out of memory.
  1345. */
  1346. void *bigAlloc(size_t size)
  1347. {
  1348. Pool *pool;
  1349. uint npages;
  1350. uint n;
  1351. uint pn;
  1352. uint freedpages;
  1353. void *p;
  1354. int state;
  1355. npages = (size + PAGESIZE - 1) / PAGESIZE;
  1356. for (state = 0; ; )
  1357. {
  1358. // This code could use some refinement when repeatedly
  1359. // allocating very large arrays.
  1360. for (n = 0; n < npools; n++)
  1361. {
  1362. pool = pooltable[n];
  1363. pn = pool.allocPages(npages);
  1364. if (pn != ~0u)
  1365. goto L1;
  1366. }
  1367. // Failed
  1368. switch (state)
  1369. {
  1370. case 0:
  1371. if (disabled)
  1372. { state = 1;
  1373. continue;
  1374. }
  1375. // Try collecting
  1376. freedpages = fullcollectshell();
  1377. if (freedpages >= npools * ((POOLSIZE / PAGESIZE) / 4))
  1378. { state = 1;
  1379. continue;
  1380. }
  1381. // Allocate new pool
  1382. pool = newPool(npages);
  1383. if (!pool)
  1384. { state = 2;
  1385. continue;
  1386. }
  1387. pn = pool.allocPages(npages);
  1388. assert(pn != ~0u);
  1389. goto L1;
  1390. case 1:
  1391. // Allocate new pool
  1392. pool = newPool(npages);
  1393. if (!pool)
  1394. goto Lnomemory;
  1395. pn = pool.allocPages(npages);
  1396. assert(pn != ~0u);
  1397. goto L1;
  1398. case 2:
  1399. goto Lnomemory;
  1400. }
  1401. }
  1402. L1:
  1403. pool.pagetable[pn] = B_PAGE;
  1404. if (npages > 1)
  1405. cstring.memset(&pool.pagetable[pn + 1], B_PAGEPLUS, npages - 1);
  1406. p = pool.baseAddr + pn * PAGESIZE;
  1407. cstring.memset(cast(char *)p + size, 0, npages * PAGESIZE - size);
  1408. debug (MEMSTOMP) cstring.memset(p, 0xF1, size);
  1409. //debug(PRINTF) printf("\tp = %x\n", p);
  1410. return p;
  1411. Lnomemory:
  1412. onOutOfMemoryError();
  1413. return null;
  1414. }
  1415. /**
  1416. * Allocate a new pool with at least npages in it.
  1417. * Sort it into pooltable[].
  1418. * Return null if failed.
  1419. */
  1420. Pool *newPool(uint npages)
  1421. {
  1422. Pool *pool;
  1423. Pool **newpooltable;
  1424. uint newnpools;
  1425. uint i;
  1426. //debug(PRINTF) printf("************Gcx::newPool(npages = %d)****************\n", npages);
  1427. // Round up to COMMITSIZE pages
  1428. npages = (npages + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1);
  1429. // Minimum of POOLSIZE
  1430. if (npages < POOLSIZE/PAGESIZE)
  1431. npages = POOLSIZE/PAGESIZE;
  1432. else if (npages > POOLSIZE/PAGESIZE)
  1433. { // Give us 150% of requested size, so there's room to extend
  1434. auto n = npages + (npages >> 1);
  1435. if (n < size_t.max/PAGESIZE)
  1436. npages = n;
  1437. }
  1438. // Allocate successively larger pools up to 8 megs
  1439. if (npools)
  1440. { uint n;
  1441. n = npools;
  1442. if (n > 8)
  1443. n = 8; // cap pool size at 8 megs
  1444. n *= (POOLSIZE / PAGESIZE);
  1445. if (npages < n)
  1446. npages = n;
  1447. }
  1448. pool = cast(Pool *)cstdlib.calloc(1, Pool.sizeof);
  1449. if (pool)
  1450. {
  1451. pool.initialize(npages);
  1452. if (!pool.baseAddr)
  1453. goto Lerr;
  1454. newnpools = npools + 1;
  1455. newpooltable = cast(Pool **)cstdlib.realloc(pooltable, newnpools * (Pool *).sizeof);
  1456. if (!newpooltable)
  1457. goto Lerr;
  1458. // Sort pool into newpooltable[]
  1459. for (i = 0; i < npools; i++)
  1460. {
  1461. if (pool.opCmp(newpooltable[i]) < 0)
  1462. break;
  1463. }
  1464. cstring.memmove(newpooltable + i + 1, newpooltable + i, (npools - i) * (Pool *).sizeof);
  1465. newpooltable[i] = pool;
  1466. pooltable = newpooltable;
  1467. npools = newnpools;
  1468. minAddr = pooltable[0].baseAddr;
  1469. maxAddr = pooltable[npools - 1].topAddr;
  1470. }
  1471. return pool;
  1472. Lerr:
  1473. pool.Dtor();
  1474. cstdlib.free(pool);
  1475. return null;
  1476. }
  1477. /**
  1478. * Allocate a page of bin's.
  1479. * Returns:
  1480. * 0 failed
  1481. */
  1482. int allocPage(Bins bin)
  1483. {
  1484. Pool *pool;
  1485. uint n;
  1486. uint pn;
  1487. byte *p;
  1488. byte *ptop;
  1489. //debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin);
  1490. for (n = 0; n < npools; n++)
  1491. {
  1492. pool = pooltable[n];
  1493. pn = pool.allocPages(1);
  1494. if (pn != ~0u)
  1495. goto L1;
  1496. }
  1497. return 0; // failed
  1498. L1:
  1499. pool.pagetable[pn] = cast(ubyte)bin;
  1500. // Convert page to free list
  1501. size_t size = binsize[bin];
  1502. List **b = &bucket[bin];
  1503. p = pool.baseAddr + pn * PAGESIZE;
  1504. ptop = p + PAGESIZE;
  1505. for (; p < ptop; p += size)
  1506. {
  1507. (cast(List *)p).next = *b;
  1508. *b = cast(List *)p;
  1509. }
  1510. return 1;
  1511. }
  1512. /**
  1513. * Search a range of memory values and mark any pointers into the GC pool.
  1514. */
  1515. void mark(void *pbot, void *ptop)
  1516. {
  1517. void **p1 = cast(void **)pbot;
  1518. void **p2 = cast(void **)ptop;
  1519. uint changes = 0;
  1520. //printf("marking range: %p -> %p\n", pbot, ptop);
  1521. for (; p1 < p2; p1++)
  1522. {
  1523. Pool *pool;
  1524. byte *p = cast(byte *)(*p1);
  1525. //if (log) debug(PRINTF) printf("\tmark %x\n", p);
  1526. if (p >= minAddr)
  1527. {
  1528. pool = findPool(p);
  1529. if (pool)
  1530. {
  1531. size_t offset = cast(size_t)(p - pool.baseAddr);
  1532. uint biti;
  1533. uint pn = offset / PAGESIZE;
  1534. Bins bin = cast(Bins)pool.pagetable[pn];
  1535. //debug(PRINTF) printf("\t\tfound pool %x, base=%x, pn = %d, bin = %d, biti = x%x\n", pool, pool.baseAddr, pn, bin, biti);
  1536. // Adjust bit to be at start of allocated memory block
  1537. if (bin <= B_PAGE)
  1538. {
  1539. biti = (offset & notbinsize[bin]) >> 4;
  1540. //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
  1541. }
  1542. else if (bin == B_PAGEPLUS)
  1543. {
  1544. do
  1545. { --pn;
  1546. } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
  1547. biti = pn * (PAGESIZE / 16);
  1548. }
  1549. else
  1550. {
  1551. // Don't mark bits in B_FREE or B_UNCOMMITTED pages
  1552. continue;
  1553. }
  1554. //debug(PRINTF) printf("\t\tmark(x%x) = %d\n", biti, pool.mark.test(biti));
  1555. if (!pool.mark.test(biti))
  1556. {
  1557. //if (log) debug(PRINTF) printf("\t\tmarking %x\n", p);
  1558. pool.mark.set(biti);
  1559. if (!pool.noscan.test(biti))
  1560. {
  1561. pool.scan.set(biti);
  1562. changes = 1;
  1563. }
  1564. log_parent(sentinel_add(pool.baseAddr + biti * 16), sentinel_add(pbot));
  1565. }
  1566. }
  1567. }
  1568. }
  1569. anychanges |= changes;
  1570. }
  1571. /**
  1572. * Return number of full pages free'd.
  1573. */
  1574. size_t fullcollectshell()
  1575. {
  1576. // The purpose of the 'shell' is to ensure all the registers
  1577. // get put on the stack so they'll be scanned
  1578. void *sp;
  1579. size_t result;
  1580. version (GNU)
  1581. {
  1582. __builtin_unwind_init();
  1583. sp = & sp;
  1584. }
  1585. else
  1586. {
  1587. /*asm
  1588. {
  1589. pushad ;
  1590. mov sp[EBP],ESP ;
  1591. }*/
  1592. }
  1593. result = fullcollect(sp);
  1594. version (GNU)
  1595. {
  1596. // nothing to do
  1597. }
  1598. else
  1599. {
  1600. /*asm
  1601. {
  1602. popad ;
  1603. }*/
  1604. }
  1605. return result;
  1606. }
  1607. /**
  1608. *
  1609. */
  1610. size_t fullcollect(void *stackTop)
  1611. {
  1612. uint n;
  1613. Pool *pool;
  1614. debug(COLLECT_PRINTF) printf("Gcx.fullcollect()\n");
  1615. Thread.pauseAll();
  1616. p_cache = null;
  1617. size_cache = 0;
  1618. anychanges = 0;
  1619. for (n = 0; n < npools; n++)
  1620. {
  1621. pool = pooltable[n];
  1622. pool.mark.zero();
  1623. pool.scan.zero();
  1624. pool.freebits.zero();
  1625. }
  1626. // Mark each free entry, so it doesn't get scanned
  1627. for (n = 0; n < B_PAGE; n++)
  1628. {
  1629. for (List *list = bucket[n]; list; list = list.next)
  1630. {
  1631. pool = findPool(list);
  1632. assert(pool);
  1633. pool.freebits.set(cast(uint)(cast(byte *)list - pool.baseAddr) / 16);
  1634. }
  1635. }
  1636. for (n = 0; n < npools; n++)
  1637. {
  1638. pool = pooltable[n];
  1639. pool.mark.copy(&pool.freebits);
  1640. }
  1641. version (MULTI_THREADED)
  1642. {
  1643. // Scan stacks and registers for each paused thread
  1644. Thread[] threads = Thread.getAll();
  1645. //thread_id id = cast(thread_id) GetCurrentThread();
  1646. for (n = 0; n < threads.length; n++)
  1647. { Thread t = threads[n];
  1648. if (t && t.getState() == Thread.TS.RUNNING)
  1649. {
  1650. if (noStack && threads.length == 1)
  1651. break;
  1652. version (Win32)
  1653. {
  1654. CONTEXT context;
  1655. context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
  1656. if (!GetThreadContext(t.hdl, &context))
  1657. {
  1658. assert(0);
  1659. }
  1660. debug (PRINTF) printf("mt scan stack bot = %x, top = %x\n", context.Esp, t.stackBottom);
  1661. mark(cast(void *)context.Esp, t.stackBottom);
  1662. mark(&context.Edi, &context.Eip);
  1663. }
  1664. else version (GNU)
  1665. {
  1666. if (t.isSelf())
  1667. t.stackTop = Thread.getESP();
  1668. //%%fry printf("top=%08x bot=%08x ext=%08x\n", t.stackTop, t.stackBottom, Thread.getESP());//%%try
  1669. version (STACKGROWSDOWN)
  1670. mark(t.stackTop, t.stackBottom);
  1671. else
  1672. mark(t.stackBottom, t.stackTop);
  1673. }
  1674. else version (linux)
  1675. {
  1676. // The registers are already stored in the stack
  1677. //printf("Thread: ESP = x%x, stackBottom = x%x, isSelf = %d\n", Thread.getESP(), t.stackBottom, t.isSelf());
  1678. if (t.isSelf())
  1679. t.stackTop = Thread.getESP();
  1680. version (STACKGROWSDOWN)
  1681. mark(t.stackTop, t.stackBottom);
  1682. else
  1683. mark(t.stackBottom, t.stackTop);
  1684. }
  1685. }
  1686. }
  1687. }
  1688. else
  1689. {
  1690. if (!noStack)
  1691. {
  1692. // Scan stack for main thread
  1693. debug(PRINTF) printf(" scan stack bot = %x, top = %x\n", stackTop, stackBottom);
  1694. version (STACKGROWSDOWN)
  1695. mark(stackTop, stackBottom);
  1696. else
  1697. mark(stackBottom, stackTop);
  1698. }
  1699. }
  1700. // Scan roots[]
  1701. debug(COLLECT_PRINTF) printf("scan roots[]\n");
  1702. mark(roots, roots + nroots);
  1703. // Scan ranges[]
  1704. debug(COLLECT_PRINTF) printf("scan ranges[]\n");
  1705. //log++;
  1706. for (n = 0; n < nranges; n++)
  1707. {
  1708. debug(COLLECT_PRINTF) printf("\t%x .. %x\n", ranges[n].pbot, ranges[n].ptop);
  1709. mark(ranges[n].pbot, ranges[n].ptop);
  1710. }
  1711. //log--;
  1712. debug(COLLECT_PRINTF) printf("\tscan heap\n");
  1713. while (anychanges)
  1714. {
  1715. anychanges = 0;
  1716. for (n = 0; n < npools; n++)
  1717. {
  1718. uint *bbase;
  1719. uint *b;
  1720. uint *btop;
  1721. pool = pooltable[n];
  1722. bbase = pool.scan.base();
  1723. btop = bbase + pool.scan.nwords;
  1724. for (b = bbase; b < btop;)
  1725. { Bins bin;
  1726. uint pn;
  1727. uint u;
  1728. uint bitm;
  1729. byte *o;
  1730. bitm = *b;
  1731. if (!bitm)
  1732. { b++;
  1733. continue;
  1734. }
  1735. *b = 0;
  1736. o = pool.baseAddr + (b - bbase) * 32 * 16;
  1737. /* version (BigEndian)
  1738. bitm = bswap(bitm);
  1739. */
  1740. if (!(bitm & 0xFFFF))
  1741. {
  1742. bitm >>= 16;
  1743. o += 16 * 16;
  1744. }
  1745. for (; bitm; o += 16, bitm >>= 1)
  1746. {
  1747. if (!(bitm & 1))
  1748. continue;
  1749. pn = (o - pool.baseAddr) / PAGESIZE;
  1750. bin = cast(Bins)pool.pagetable[pn];
  1751. if (bin < B_PAGE)
  1752. {
  1753. mark(o, o + binsize[bin]);
  1754. }
  1755. else if (bin == B_PAGE || bin == B_PAGEPLUS)
  1756. {
  1757. if (bin == B_PAGEPLUS)
  1758. {
  1759. while (pool.pagetable[pn - 1] != B_PAGE)
  1760. pn--;
  1761. }
  1762. u = 1;
  1763. while (pn + u < pool.ncommitted && pool.pagetable[pn + u] == B_PAGEPLUS)
  1764. u++;
  1765. mark(o, o + u * PAGESIZE);
  1766. }
  1767. }
  1768. }
  1769. }
  1770. }
  1771. // Free up everything not marked
  1772. debug(COLLECT_PRINTF) printf("\tfree'ing\n");
  1773. size_t freedpages = 0;
  1774. size_t freed = 0;
  1775. for (n = 0; n < npools; n++)
  1776. { uint pn;
  1777. uint ncommitted;
  1778. uint *bbase;
  1779. pool = pooltable[n];
  1780. bbase = pool.mark.base();
  1781. ncommitted = pool.ncommitted;
  1782. for (pn = 0; pn < ncommitted; pn++, bbase += PAGESIZE / (32 * 16))
  1783. {
  1784. Bins bin = cast(Bins)pool.pagetable[pn];
  1785. if (bin < B_PAGE)
  1786. { byte *p;
  1787. byte *ptop;
  1788. uint biti;
  1789. uint bitstride;
  1790. uint size = binsize[bin];
  1791. p = pool.baseAddr + pn * PAGESIZE;
  1792. ptop = p + PAGESIZE;
  1793. biti = pn * (PAGESIZE/16);
  1794. bitstride = size / 16;
  1795. version(none) // BUG: doesn't work because freebits() must also be cleared
  1796. {
  1797. // If free'd entire page
  1798. if (bbase[0] == 0 && bbase[1] == 0 && bbase[2] == 0 && bbase[3] == 0 &&
  1799. bbase[4] == 0 && bbase[5] == 0 && bbase[6] == 0 && bbase[7] == 0)
  1800. {
  1801. for (; p < ptop; p += size, biti += bitstride)
  1802. {
  1803. if (pool.finals.nbits && pool.finals.testClear(biti))
  1804. rt_finalize(cast(List *)sentinel_add(p), false);
  1805. List *list = cast(List *)p;
  1806. //debug(PRINTF) printf("\tcollecting %x\n", list);
  1807. log_free(sentinel_add(list));
  1808. debug (MEMSTOMP) cstring.memset(p, 0xF3, size);
  1809. }
  1810. pool.pagetable[pn] = B_FREE;
  1811. freed += PAGESIZE;
  1812. //debug(PRINTF) printf("freeing entire page %d\n", pn);
  1813. continue;
  1814. }
  1815. }
  1816. for (; p < ptop; p += size, biti += bitstride)
  1817. {
  1818. if (!pool.mark.test(biti))
  1819. {
  1820. sentinel_Invariant(sentinel_add(p));
  1821. pool.freebits.set(biti);
  1822. pool.noscan.clear(biti);
  1823. if (pool.finals.nbits && pool.finals.testClear(biti))
  1824. rt_finalize(cast(List *)sentinel_add(p), false);
  1825. List *list = cast(List *)p;
  1826. debug(PRINTF) printf("\tcollecting %x\n", list);
  1827. log_free(sentinel_add(list));
  1828. debug (MEMSTOMP) cstring.memset(p, 0xF3, size);
  1829. freed += size;
  1830. }
  1831. }
  1832. }
  1833. else if (bin == B_PAGE)
  1834. { uint biti = pn * (PAGESIZE / 16);
  1835. if (!pool.mark.test(biti))
  1836. { byte *p = pool.baseAddr + pn * PAGESIZE;
  1837. sentinel_Invariant(sentinel_add(p));
  1838. pool.noscan.clear(biti);
  1839. if (pool.finals.nbits && pool.finals.testClear(biti))
  1840. rt_finalize(sentinel_add(p), false);
  1841. debug(COLLECT_PRINTF) printf("\tcollecting big %x\n", p);
  1842. log_free(sentinel_add(p));
  1843. pool.pagetable[pn] = B_FREE;
  1844. freedpages++;
  1845. debug (MEMSTOMP) cstring.memset(p, 0xF3, PAGESIZE);
  1846. while (pn + 1 < ncommitted && pool.pagetable[pn + 1] == B_PAGEPLUS)
  1847. {
  1848. pn++;
  1849. pool.pagetable[pn] = B_FREE;
  1850. freedpages++;
  1851. debug (MEMSTOMP)
  1852. { p += PAGESIZE;
  1853. cstring.memset(p, 0xF3, PAGESIZE);
  1854. }
  1855. }
  1856. }
  1857. }
  1858. }
  1859. }
  1860. // Zero buckets
  1861. bucket[] = null;
  1862. // Free complete pages, rebuild free list
  1863. debug(COLLECT_PRINTF) printf("\tfree complete pages\n");
  1864. size_t recoveredpages = 0;
  1865. for (n = 0; n < npools; n++)
  1866. { uint pn;
  1867. uint ncommitted;
  1868. pool = pooltable[n];
  1869. ncommitted = pool.ncommitted;
  1870. for (pn = 0; pn < ncommitted; pn++)
  1871. {
  1872. Bins bin = cast(Bins)pool.pagetable[pn];
  1873. uint biti;
  1874. uint u;
  1875. if (bin < B_PAGE)
  1876. {
  1877. uint size = binsize[bin];
  1878. uint bitstride = size / 16;
  1879. uint bitbase = pn * (PAGESIZE / 16);
  1880. uint bittop = bitbase + (PAGESIZE / 16);
  1881. byte *p;
  1882. biti = bitbase;
  1883. for (biti = bitbase; biti < bittop; biti += bitstride)
  1884. { if (!pool.freebits.test(biti))
  1885. goto Lnotfree;
  1886. }
  1887. pool.pagetable[pn] = B_FREE;
  1888. recoveredpages++;
  1889. continue;
  1890. Lnotfree:
  1891. p = pool.baseAddr + pn * PAGESIZE;
  1892. for (u = 0; u < PAGESIZE; u += size)
  1893. { biti = bitbase + u / 16;
  1894. if (pool.freebits.test(biti))
  1895. { List *list;
  1896. list = cast(List *)(p + u);
  1897. if (list.next != bucket[bin]) // avoid unnecessary writes
  1898. list.next = bucket[bin];
  1899. bucket[bin] = list;
  1900. }
  1901. }
  1902. }
  1903. }
  1904. }
  1905. debug(COLLECT_PRINTF) printf("recovered pages = %d\n", recoveredpages);
  1906. debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedpages, npools);
  1907. Thread.resumeAll();
  1908. return freedpages + recoveredpages;
  1909. }
  1910. /**
  1911. * Run finalizer on p when it is free'd.
  1912. */
  1913. void doFinalize(void *p)
  1914. {
  1915. Pool *pool = findPool(p);
  1916. assert(pool);
  1917. // Only allocate finals[] if we actually need it
  1918. if (!pool.finals.nbits)
  1919. pool.finals.alloc(pool.mark.nbits);
  1920. pool.finals.set((p - pool.baseAddr) / 16);
  1921. }
  1922. /**
  1923. * Indicate that block pointed to by p has possible pointers
  1924. * to GC allocated memory in it.
  1925. */
  1926. void HasPointers(void *p)
  1927. {
  1928. Pool *pool = findPool(p);
  1929. assert(pool);
  1930. pool.noscan.clear((p - pool.baseAddr) / 16);
  1931. }
  1932. /**
  1933. * Indicate that block pointed to by p has no possible pointers
  1934. * to GC allocated memory in it.
  1935. */
  1936. void HasNoPointers(void *p)
  1937. {
  1938. //printf("HasNoPointers(%p)\n", p);
  1939. Pool *pool = findPool(p);
  1940. assert(pool);
  1941. pool.noscan.set((p - pool.baseAddr) / 16);
  1942. }
  1943. /***** Leak Detector ******/
  1944. debug (LOGGING)
  1945. {
  1946. LogArray current;
  1947. LogArray prev;
  1948. void log_init()
  1949. {
  1950. //debug(PRINTF) printf("+log_init()\n");
  1951. current.reserve(1000);
  1952. prev.reserve(1000);
  1953. //debug(PRINTF) printf("-log_init()\n");
  1954. }
  1955. void log_malloc(void *p, size_t size)
  1956. {
  1957. //debug(PRINTF) printf("+log_malloc(p = %x, size = %d)\n", p, size);
  1958. Log log;
  1959. log.p = p;
  1960. log.size = size;
  1961. log.line = GC.line;
  1962. log.file = GC.file;
  1963. log.parent = null;
  1964. GC.line = 0;
  1965. GC.file = null;
  1966. current.push(log);
  1967. //debug(PRINTF) printf("-log_malloc()\n");
  1968. }
  1969. void log_free(void *p)
  1970. {
  1971. //debug(PRINTF) printf("+log_free(%x)\n", p);
  1972. size_t i;
  1973. i = current.find(p);
  1974. if (i == ~0u)
  1975. {
  1976. debug(PRINTF) printf("free'ing unallocated memory %x\n", p);
  1977. }
  1978. else
  1979. current.remove(i);
  1980. //debug(PRINTF) printf("-log_free()\n");
  1981. }
  1982. void log_collect()
  1983. {
  1984. //debug(PRINTF) printf("+log_collect()\n");
  1985. // Print everything in current that is not in prev
  1986. debug(PRINTF) printf("New pointers this cycle: --------------------------------\n");
  1987. size_t used = 0;
  1988. for (size_t i = 0; i < current.dim; i++)
  1989. {
  1990. size_t j;
  1991. j = prev.find(current.data[i].p);
  1992. if (j == ~0u)
  1993. current.data[i].print();
  1994. else
  1995. used++;
  1996. }
  1997. debug(PRINTF) printf("All roots this cycle: --------------------------------\n");
  1998. for (size_t i = 0; i < current.dim; i++)
  1999. {
  2000. void *p;
  2001. size_t j;
  2002. p = current.data[i].p;
  2003. if (!findPool(current.data[i].parent))
  2004. {
  2005. j = prev.find(current.data[i].p);
  2006. if (j == ~0u)
  2007. debug(PRINTF) printf("N");
  2008. else
  2009. debug(PRINTF) printf(" ");;
  2010. current.data[i].print();
  2011. }
  2012. }
  2013. debug(PRINTF) printf("Used = %d-------------------------------------------------\n", used);
  2014. prev.copy(&current);
  2015. debug(PRINTF) printf("-log_collect()\n");
  2016. }
  2017. void log_parent(void *p, void *parent)
  2018. {
  2019. //debug(PRINTF) printf("+log_parent()\n");
  2020. size_t i;
  2021. i = current.find(p);
  2022. if (i == ~0u)
  2023. {
  2024. debug(PRINTF) printf("parent'ing unallocated memory %x, parent = %x\n", p, parent);
  2025. Pool *pool;
  2026. pool = findPool(p);
  2027. assert(pool);
  2028. size_t offset = cast(uint)(p - pool.baseAddr);
  2029. size_t biti;
  2030. uint pn = offset / PAGESIZE;
  2031. Bins bin = cast(Bins)pool.pagetable[pn];
  2032. biti = (offset & notbinsize[bin]);
  2033. debug(PRINTF) printf("\tbin = %d, offset = x%x, biti = x%x\n", bin, offset, biti);
  2034. }
  2035. else
  2036. {
  2037. current.data[i].parent = parent;
  2038. }
  2039. //debug(PRINTF) printf("-log_parent()\n");
  2040. }
  2041. }
  2042. else
  2043. {
  2044. void log_init() { }
  2045. void log_malloc(void *p, size_t size) { }
  2046. void log_free(void *p) { }
  2047. void log_collect() { }
  2048. void log_parent(void *p, void *parent) { }
  2049. }
  2050. }
  2051. /* ============================ Pool =============================== */
  2052. struct Pool
  2053. {
  2054. byte* baseAddr;
  2055. byte* topAddr;
  2056. GCBits mark; // entries already scanned, or should not be scanned
  2057. GCBits scan; // entries that need to be scanned
  2058. GCBits freebits; // entries that are on the free list
  2059. GCBits finals; // entries that need finalizer run on them
  2060. GCBits noscan; // entries that should not be scanned
  2061. uint npages;
  2062. uint ncommitted; // ncommitted <= npages
  2063. ubyte* pagetable;
  2064. void initialize(uint npages)
  2065. {
  2066. size_t poolsize;
  2067. //debug(PRINTF) printf("Pool::Pool(%u)\n", npages);
  2068. poolsize = npages * PAGESIZE;
  2069. assert(poolsize >= POOLSIZE);
  2070. baseAddr = cast(byte *)os_mem_map(poolsize);
  2071. // Some of the code depends on page alignment of memory pools
  2072. assert((cast(ui

Large files files are truncated, but you can click here to view the full file