github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/third_party/gofrontend/libgo/runtime/mheap.c (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Page heap. 6 // 7 // See malloc.h for overview. 8 // 9 // When a MSpan is in the heap free list, state == MSpanFree 10 // and heapmap(s->start) == span, heapmap(s->start+s->npages-1) == span. 11 // 12 // When a MSpan is allocated, state == MSpanInUse 13 // and heapmap(i) == span for all s->start <= i < s->start+s->npages. 14 15 #include "runtime.h" 16 #include "arch.h" 17 #include "malloc.h" 18 19 static MSpan *MHeap_AllocLocked(MHeap*, uintptr, int32); 20 static bool MHeap_Grow(MHeap*, uintptr); 21 static void MHeap_FreeLocked(MHeap*, MSpan*); 22 static MSpan *MHeap_AllocLarge(MHeap*, uintptr); 23 static MSpan *BestFit(MSpan*, uintptr, MSpan*); 24 25 static void 26 RecordSpan(void *vh, byte *p) 27 { 28 MHeap *h; 29 MSpan *s; 30 MSpan **all; 31 uint32 cap; 32 33 h = vh; 34 s = (MSpan*)p; 35 if(h->nspan >= h->nspancap) { 36 cap = 64*1024/sizeof(all[0]); 37 if(cap < h->nspancap*3/2) 38 cap = h->nspancap*3/2; 39 all = (MSpan**)runtime_SysAlloc(cap*sizeof(all[0]), &mstats.other_sys); 40 if(all == nil) 41 runtime_throw("runtime: cannot allocate memory"); 42 if(h->allspans) { 43 runtime_memmove(all, h->allspans, h->nspancap*sizeof(all[0])); 44 // Don't free the old array if it's referenced by sweep. 45 // See the comment in mgc0.c. 46 if(h->allspans != runtime_mheap.sweepspans) 47 runtime_SysFree(h->allspans, h->nspancap*sizeof(all[0]), &mstats.other_sys); 48 } 49 h->allspans = all; 50 h->nspancap = cap; 51 } 52 h->allspans[h->nspan++] = s; 53 } 54 55 // Initialize the heap; fetch memory using alloc. 56 void 57 runtime_MHeap_Init(MHeap *h) 58 { 59 uint32 i; 60 61 runtime_FixAlloc_Init(&h->spanalloc, sizeof(MSpan), RecordSpan, h, &mstats.mspan_sys); 62 runtime_FixAlloc_Init(&h->cachealloc, sizeof(MCache), nil, nil, &mstats.mcache_sys); 63 runtime_FixAlloc_Init(&h->specialfinalizeralloc, sizeof(SpecialFinalizer), nil, nil, &mstats.other_sys); 64 runtime_FixAlloc_Init(&h->specialprofilealloc, sizeof(SpecialProfile), nil, nil, &mstats.other_sys); 65 // h->mapcache needs no init 66 for(i=0; i<nelem(h->free); i++) { 67 runtime_MSpanList_Init(&h->free[i]); 68 runtime_MSpanList_Init(&h->busy[i]); 69 } 70 runtime_MSpanList_Init(&h->freelarge); 71 runtime_MSpanList_Init(&h->busylarge); 72 for(i=0; i<nelem(h->central); i++) 73 runtime_MCentral_Init(&h->central[i].mcentral, i); 74 } 75 76 void 77 runtime_MHeap_MapSpans(MHeap *h) 78 { 79 uintptr pagesize; 80 uintptr n; 81 82 // Map spans array, PageSize at a time. 83 n = (uintptr)h->arena_used; 84 n -= (uintptr)h->arena_start; 85 n = n / PageSize * sizeof(h->spans[0]); 86 n = ROUND(n, PageSize); 87 pagesize = getpagesize(); 88 n = ROUND(n, pagesize); 89 if(h->spans_mapped >= n) 90 return; 91 runtime_SysMap((byte*)h->spans + h->spans_mapped, n - h->spans_mapped, h->arena_reserved, &mstats.other_sys); 92 h->spans_mapped = n; 93 } 94 95 // Sweeps spans in list until reclaims at least npages into heap. 96 // Returns the actual number of pages reclaimed. 97 static uintptr 98 MHeap_ReclaimList(MHeap *h, MSpan *list, uintptr npages) 99 { 100 MSpan *s; 101 uintptr n; 102 uint32 sg; 103 104 n = 0; 105 sg = runtime_mheap.sweepgen; 106 retry: 107 for(s = list->next; s != list; s = s->next) { 108 if(s->sweepgen == sg-2 && runtime_cas(&s->sweepgen, sg-2, sg-1)) { 109 runtime_MSpanList_Remove(s); 110 // swept spans are at the end of the list 111 runtime_MSpanList_InsertBack(list, s); 112 runtime_unlock(&h->lock); 113 n += runtime_MSpan_Sweep(s); 114 runtime_lock(&h->lock); 115 if(n >= npages) 116 return n; 117 // the span could have been moved elsewhere 118 goto retry; 119 } 120 if(s->sweepgen == sg-1) { 121 // the span is being sweept by background sweeper, skip 122 continue; 123 } 124 // already swept empty span, 125 // all subsequent ones must also be either swept or in process of sweeping 126 break; 127 } 128 return n; 129 } 130 131 // Sweeps and reclaims at least npage pages into heap. 132 // Called before allocating npage pages. 133 static void 134 MHeap_Reclaim(MHeap *h, uintptr npage) 135 { 136 uintptr reclaimed, n; 137 138 // First try to sweep busy spans with large objects of size >= npage, 139 // this has good chances of reclaiming the necessary space. 140 for(n=npage; n < nelem(h->busy); n++) { 141 if(MHeap_ReclaimList(h, &h->busy[n], npage)) 142 return; // Bingo! 143 } 144 145 // Then -- even larger objects. 146 if(MHeap_ReclaimList(h, &h->busylarge, npage)) 147 return; // Bingo! 148 149 // Now try smaller objects. 150 // One such object is not enough, so we need to reclaim several of them. 151 reclaimed = 0; 152 for(n=0; n < npage && n < nelem(h->busy); n++) { 153 reclaimed += MHeap_ReclaimList(h, &h->busy[n], npage-reclaimed); 154 if(reclaimed >= npage) 155 return; 156 } 157 158 // Now sweep everything that is not yet swept. 159 runtime_unlock(&h->lock); 160 for(;;) { 161 n = runtime_sweepone(); 162 if(n == (uintptr)-1) // all spans are swept 163 break; 164 reclaimed += n; 165 if(reclaimed >= npage) 166 break; 167 } 168 runtime_lock(&h->lock); 169 } 170 171 // Allocate a new span of npage pages from the heap 172 // and record its size class in the HeapMap and HeapMapCache. 173 MSpan* 174 runtime_MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, bool large, bool needzero) 175 { 176 MSpan *s; 177 178 runtime_lock(&h->lock); 179 mstats.heap_alloc += runtime_m()->mcache->local_cachealloc; 180 runtime_m()->mcache->local_cachealloc = 0; 181 s = MHeap_AllocLocked(h, npage, sizeclass); 182 if(s != nil) { 183 mstats.heap_inuse += npage<<PageShift; 184 if(large) { 185 mstats.heap_objects++; 186 mstats.heap_alloc += npage<<PageShift; 187 // Swept spans are at the end of lists. 188 if(s->npages < nelem(h->free)) 189 runtime_MSpanList_InsertBack(&h->busy[s->npages], s); 190 else 191 runtime_MSpanList_InsertBack(&h->busylarge, s); 192 } 193 } 194 runtime_unlock(&h->lock); 195 if(s != nil) { 196 if(needzero && s->needzero) 197 runtime_memclr((byte*)(s->start<<PageShift), s->npages<<PageShift); 198 s->needzero = 0; 199 } 200 return s; 201 } 202 203 static MSpan* 204 MHeap_AllocLocked(MHeap *h, uintptr npage, int32 sizeclass) 205 { 206 uintptr n; 207 MSpan *s, *t; 208 PageID p; 209 210 // To prevent excessive heap growth, before allocating n pages 211 // we need to sweep and reclaim at least n pages. 212 if(!h->sweepdone) 213 MHeap_Reclaim(h, npage); 214 215 // Try in fixed-size lists up to max. 216 for(n=npage; n < nelem(h->free); n++) { 217 if(!runtime_MSpanList_IsEmpty(&h->free[n])) { 218 s = h->free[n].next; 219 goto HaveSpan; 220 } 221 } 222 223 // Best fit in list of large spans. 224 if((s = MHeap_AllocLarge(h, npage)) == nil) { 225 if(!MHeap_Grow(h, npage)) 226 return nil; 227 if((s = MHeap_AllocLarge(h, npage)) == nil) 228 return nil; 229 } 230 231 HaveSpan: 232 // Mark span in use. 233 if(s->state != MSpanFree) 234 runtime_throw("MHeap_AllocLocked - MSpan not free"); 235 if(s->npages < npage) 236 runtime_throw("MHeap_AllocLocked - bad npages"); 237 runtime_MSpanList_Remove(s); 238 runtime_atomicstore(&s->sweepgen, h->sweepgen); 239 s->state = MSpanInUse; 240 mstats.heap_idle -= s->npages<<PageShift; 241 mstats.heap_released -= s->npreleased<<PageShift; 242 if(s->npreleased > 0) 243 runtime_SysUsed((void*)(s->start<<PageShift), s->npages<<PageShift); 244 s->npreleased = 0; 245 246 if(s->npages > npage) { 247 // Trim extra and put it back in the heap. 248 t = runtime_FixAlloc_Alloc(&h->spanalloc); 249 runtime_MSpan_Init(t, s->start + npage, s->npages - npage); 250 s->npages = npage; 251 p = t->start; 252 p -= ((uintptr)h->arena_start>>PageShift); 253 if(p > 0) 254 h->spans[p-1] = s; 255 h->spans[p] = t; 256 h->spans[p+t->npages-1] = t; 257 t->needzero = s->needzero; 258 runtime_atomicstore(&t->sweepgen, h->sweepgen); 259 t->state = MSpanInUse; 260 MHeap_FreeLocked(h, t); 261 t->unusedsince = s->unusedsince; // preserve age 262 } 263 s->unusedsince = 0; 264 265 // Record span info, because gc needs to be 266 // able to map interior pointer to containing span. 267 s->sizeclass = sizeclass; 268 s->elemsize = (sizeclass==0 ? s->npages<<PageShift : (uintptr)runtime_class_to_size[sizeclass]); 269 s->types.compression = MTypes_Empty; 270 p = s->start; 271 p -= ((uintptr)h->arena_start>>PageShift); 272 for(n=0; n<npage; n++) 273 h->spans[p+n] = s; 274 return s; 275 } 276 277 // Allocate a span of exactly npage pages from the list of large spans. 278 static MSpan* 279 MHeap_AllocLarge(MHeap *h, uintptr npage) 280 { 281 return BestFit(&h->freelarge, npage, nil); 282 } 283 284 // Search list for smallest span with >= npage pages. 285 // If there are multiple smallest spans, take the one 286 // with the earliest starting address. 287 static MSpan* 288 BestFit(MSpan *list, uintptr npage, MSpan *best) 289 { 290 MSpan *s; 291 292 for(s=list->next; s != list; s=s->next) { 293 if(s->npages < npage) 294 continue; 295 if(best == nil 296 || s->npages < best->npages 297 || (s->npages == best->npages && s->start < best->start)) 298 best = s; 299 } 300 return best; 301 } 302 303 // Try to add at least npage pages of memory to the heap, 304 // returning whether it worked. 305 static bool 306 MHeap_Grow(MHeap *h, uintptr npage) 307 { 308 uintptr ask; 309 void *v; 310 MSpan *s; 311 PageID p; 312 313 // Ask for a big chunk, to reduce the number of mappings 314 // the operating system needs to track; also amortizes 315 // the overhead of an operating system mapping. 316 // Allocate a multiple of 64kB (16 pages). 317 npage = (npage+15)&~15; 318 ask = npage<<PageShift; 319 if(ask < HeapAllocChunk) 320 ask = HeapAllocChunk; 321 322 v = runtime_MHeap_SysAlloc(h, ask); 323 if(v == nil) { 324 if(ask > (npage<<PageShift)) { 325 ask = npage<<PageShift; 326 v = runtime_MHeap_SysAlloc(h, ask); 327 } 328 if(v == nil) { 329 runtime_printf("runtime: out of memory: cannot allocate %D-byte block (%D in use)\n", (uint64)ask, mstats.heap_sys); 330 return false; 331 } 332 } 333 334 // Create a fake "in use" span and free it, so that the 335 // right coalescing happens. 336 s = runtime_FixAlloc_Alloc(&h->spanalloc); 337 runtime_MSpan_Init(s, (uintptr)v>>PageShift, ask>>PageShift); 338 p = s->start; 339 p -= ((uintptr)h->arena_start>>PageShift); 340 h->spans[p] = s; 341 h->spans[p + s->npages - 1] = s; 342 runtime_atomicstore(&s->sweepgen, h->sweepgen); 343 s->state = MSpanInUse; 344 MHeap_FreeLocked(h, s); 345 return true; 346 } 347 348 // Look up the span at the given address. 349 // Address is guaranteed to be in map 350 // and is guaranteed to be start or end of span. 351 MSpan* 352 runtime_MHeap_Lookup(MHeap *h, void *v) 353 { 354 uintptr p; 355 356 p = (uintptr)v; 357 p -= (uintptr)h->arena_start; 358 return h->spans[p >> PageShift]; 359 } 360 361 // Look up the span at the given address. 362 // Address is *not* guaranteed to be in map 363 // and may be anywhere in the span. 364 // Map entries for the middle of a span are only 365 // valid for allocated spans. Free spans may have 366 // other garbage in their middles, so we have to 367 // check for that. 368 MSpan* 369 runtime_MHeap_LookupMaybe(MHeap *h, void *v) 370 { 371 MSpan *s; 372 PageID p, q; 373 374 if((byte*)v < h->arena_start || (byte*)v >= h->arena_used) 375 return nil; 376 p = (uintptr)v>>PageShift; 377 q = p; 378 q -= (uintptr)h->arena_start >> PageShift; 379 s = h->spans[q]; 380 if(s == nil || p < s->start || (byte*)v >= s->limit || s->state != MSpanInUse) 381 return nil; 382 return s; 383 } 384 385 // Free the span back into the heap. 386 void 387 runtime_MHeap_Free(MHeap *h, MSpan *s, int32 acct) 388 { 389 runtime_lock(&h->lock); 390 mstats.heap_alloc += runtime_m()->mcache->local_cachealloc; 391 runtime_m()->mcache->local_cachealloc = 0; 392 mstats.heap_inuse -= s->npages<<PageShift; 393 if(acct) { 394 mstats.heap_alloc -= s->npages<<PageShift; 395 mstats.heap_objects--; 396 } 397 MHeap_FreeLocked(h, s); 398 runtime_unlock(&h->lock); 399 } 400 401 static void 402 MHeap_FreeLocked(MHeap *h, MSpan *s) 403 { 404 MSpan *t; 405 PageID p; 406 407 s->types.compression = MTypes_Empty; 408 409 if(s->state != MSpanInUse || s->ref != 0 || s->sweepgen != h->sweepgen) { 410 runtime_printf("MHeap_FreeLocked - span %p ptr %p state %d ref %d sweepgen %d/%d\n", 411 s, s->start<<PageShift, s->state, s->ref, s->sweepgen, h->sweepgen); 412 runtime_throw("MHeap_FreeLocked - invalid free"); 413 } 414 mstats.heap_idle += s->npages<<PageShift; 415 s->state = MSpanFree; 416 runtime_MSpanList_Remove(s); 417 // Stamp newly unused spans. The scavenger will use that 418 // info to potentially give back some pages to the OS. 419 s->unusedsince = runtime_nanotime(); 420 s->npreleased = 0; 421 422 // Coalesce with earlier, later spans. 423 p = s->start; 424 p -= (uintptr)h->arena_start >> PageShift; 425 if(p > 0 && (t = h->spans[p-1]) != nil && t->state != MSpanInUse) { 426 s->start = t->start; 427 s->npages += t->npages; 428 s->npreleased = t->npreleased; // absorb released pages 429 s->needzero |= t->needzero; 430 p -= t->npages; 431 h->spans[p] = s; 432 runtime_MSpanList_Remove(t); 433 t->state = MSpanDead; 434 runtime_FixAlloc_Free(&h->spanalloc, t); 435 } 436 if((p+s->npages)*sizeof(h->spans[0]) < h->spans_mapped && (t = h->spans[p+s->npages]) != nil && t->state != MSpanInUse) { 437 s->npages += t->npages; 438 s->npreleased += t->npreleased; 439 s->needzero |= t->needzero; 440 h->spans[p + s->npages - 1] = s; 441 runtime_MSpanList_Remove(t); 442 t->state = MSpanDead; 443 runtime_FixAlloc_Free(&h->spanalloc, t); 444 } 445 446 // Insert s into appropriate list. 447 if(s->npages < nelem(h->free)) 448 runtime_MSpanList_Insert(&h->free[s->npages], s); 449 else 450 runtime_MSpanList_Insert(&h->freelarge, s); 451 } 452 453 static void 454 forcegchelper(void *vnote) 455 { 456 Note *note = (Note*)vnote; 457 458 runtime_gc(1); 459 runtime_notewakeup(note); 460 } 461 462 static uintptr 463 scavengelist(MSpan *list, uint64 now, uint64 limit) 464 { 465 uintptr released, sumreleased, start, end, pagesize; 466 MSpan *s; 467 468 if(runtime_MSpanList_IsEmpty(list)) 469 return 0; 470 471 sumreleased = 0; 472 for(s=list->next; s != list; s=s->next) { 473 if((now - s->unusedsince) > limit && s->npreleased != s->npages) { 474 released = (s->npages - s->npreleased) << PageShift; 475 mstats.heap_released += released; 476 sumreleased += released; 477 s->npreleased = s->npages; 478 479 start = s->start << PageShift; 480 end = start + (s->npages << PageShift); 481 482 // Round start up and end down to ensure we 483 // are acting on entire pages. 484 pagesize = getpagesize(); 485 start = ROUND(start, pagesize); 486 end &= ~(pagesize - 1); 487 if(end > start) 488 runtime_SysUnused((void*)start, end - start); 489 } 490 } 491 return sumreleased; 492 } 493 494 static void 495 scavenge(int32 k, uint64 now, uint64 limit) 496 { 497 uint32 i; 498 uintptr sumreleased; 499 MHeap *h; 500 501 h = &runtime_mheap; 502 sumreleased = 0; 503 for(i=0; i < nelem(h->free); i++) 504 sumreleased += scavengelist(&h->free[i], now, limit); 505 sumreleased += scavengelist(&h->freelarge, now, limit); 506 507 if(runtime_debug.gctrace > 0) { 508 if(sumreleased > 0) 509 runtime_printf("scvg%d: %D MB released\n", k, (uint64)sumreleased>>20); 510 runtime_printf("scvg%d: inuse: %D, idle: %D, sys: %D, released: %D, consumed: %D (MB)\n", 511 k, mstats.heap_inuse>>20, mstats.heap_idle>>20, mstats.heap_sys>>20, 512 mstats.heap_released>>20, (mstats.heap_sys - mstats.heap_released)>>20); 513 } 514 } 515 516 // Release (part of) unused memory to OS. 517 // Goroutine created at startup. 518 // Loop forever. 519 void 520 runtime_MHeap_Scavenger(void* dummy) 521 { 522 G *g; 523 MHeap *h; 524 uint64 tick, now, forcegc, limit; 525 int64 unixnow; 526 uint32 k; 527 Note note, *notep; 528 529 USED(dummy); 530 531 g = runtime_g(); 532 g->issystem = true; 533 g->isbackground = true; 534 535 // If we go two minutes without a garbage collection, force one to run. 536 forcegc = 2*60*1e9; 537 // If a span goes unused for 5 minutes after a garbage collection, 538 // we hand it back to the operating system. 539 limit = 5*60*1e9; 540 // Make wake-up period small enough for the sampling to be correct. 541 if(forcegc < limit) 542 tick = forcegc/2; 543 else 544 tick = limit/2; 545 546 h = &runtime_mheap; 547 for(k=0;; k++) { 548 runtime_noteclear(¬e); 549 runtime_notetsleepg(¬e, tick); 550 551 runtime_lock(&h->lock); 552 unixnow = runtime_unixnanotime(); 553 if(unixnow - mstats.last_gc > forcegc) { 554 runtime_unlock(&h->lock); 555 // The scavenger can not block other goroutines, 556 // otherwise deadlock detector can fire spuriously. 557 // GC blocks other goroutines via the runtime_worldsema. 558 runtime_noteclear(¬e); 559 notep = ¬e; 560 __go_go(forcegchelper, (void*)notep); 561 runtime_notetsleepg(¬e, -1); 562 if(runtime_debug.gctrace > 0) 563 runtime_printf("scvg%d: GC forced\n", k); 564 runtime_lock(&h->lock); 565 } 566 now = runtime_nanotime(); 567 scavenge(k, now, limit); 568 runtime_unlock(&h->lock); 569 } 570 } 571 572 void runtime_debug_freeOSMemory(void) __asm__("runtime_debug.freeOSMemory"); 573 574 void 575 runtime_debug_freeOSMemory(void) 576 { 577 runtime_gc(2); // force GC and do eager sweep 578 runtime_lock(&runtime_mheap.lock); 579 scavenge(-1, ~(uintptr)0, 0); 580 runtime_unlock(&runtime_mheap.lock); 581 } 582 583 // Initialize a new span with the given start and npages. 584 void 585 runtime_MSpan_Init(MSpan *span, PageID start, uintptr npages) 586 { 587 span->next = nil; 588 span->prev = nil; 589 span->start = start; 590 span->npages = npages; 591 span->freelist = nil; 592 span->ref = 0; 593 span->sizeclass = 0; 594 span->incache = false; 595 span->elemsize = 0; 596 span->state = MSpanDead; 597 span->unusedsince = 0; 598 span->npreleased = 0; 599 span->types.compression = MTypes_Empty; 600 span->specialLock.key = 0; 601 span->specials = nil; 602 span->needzero = 0; 603 span->freebuf = nil; 604 } 605 606 // Initialize an empty doubly-linked list. 607 void 608 runtime_MSpanList_Init(MSpan *list) 609 { 610 list->state = MSpanListHead; 611 list->next = list; 612 list->prev = list; 613 } 614 615 void 616 runtime_MSpanList_Remove(MSpan *span) 617 { 618 if(span->prev == nil && span->next == nil) 619 return; 620 span->prev->next = span->next; 621 span->next->prev = span->prev; 622 span->prev = nil; 623 span->next = nil; 624 } 625 626 bool 627 runtime_MSpanList_IsEmpty(MSpan *list) 628 { 629 return list->next == list; 630 } 631 632 void 633 runtime_MSpanList_Insert(MSpan *list, MSpan *span) 634 { 635 if(span->next != nil || span->prev != nil) { 636 runtime_printf("failed MSpanList_Insert %p %p %p\n", span, span->next, span->prev); 637 runtime_throw("MSpanList_Insert"); 638 } 639 span->next = list->next; 640 span->prev = list; 641 span->next->prev = span; 642 span->prev->next = span; 643 } 644 645 void 646 runtime_MSpanList_InsertBack(MSpan *list, MSpan *span) 647 { 648 if(span->next != nil || span->prev != nil) { 649 runtime_printf("failed MSpanList_Insert %p %p %p\n", span, span->next, span->prev); 650 runtime_throw("MSpanList_Insert"); 651 } 652 span->next = list; 653 span->prev = list->prev; 654 span->next->prev = span; 655 span->prev->next = span; 656 } 657 658 // Adds the special record s to the list of special records for 659 // the object p. All fields of s should be filled in except for 660 // offset & next, which this routine will fill in. 661 // Returns true if the special was successfully added, false otherwise. 662 // (The add will fail only if a record with the same p and s->kind 663 // already exists.) 664 static bool 665 addspecial(void *p, Special *s) 666 { 667 MSpan *span; 668 Special **t, *x; 669 uintptr offset; 670 byte kind; 671 672 span = runtime_MHeap_LookupMaybe(&runtime_mheap, p); 673 if(span == nil) 674 runtime_throw("addspecial on invalid pointer"); 675 676 // Ensure that the span is swept. 677 // GC accesses specials list w/o locks. And it's just much safer. 678 runtime_m()->locks++; 679 runtime_MSpan_EnsureSwept(span); 680 681 offset = (uintptr)p - (span->start << PageShift); 682 kind = s->kind; 683 684 runtime_lock(&span->specialLock); 685 686 // Find splice point, check for existing record. 687 t = &span->specials; 688 while((x = *t) != nil) { 689 if(offset == x->offset && kind == x->kind) { 690 runtime_unlock(&span->specialLock); 691 runtime_m()->locks--; 692 return false; // already exists 693 } 694 if(offset < x->offset || (offset == x->offset && kind < x->kind)) 695 break; 696 t = &x->next; 697 } 698 // Splice in record, fill in offset. 699 s->offset = offset; 700 s->next = x; 701 *t = s; 702 runtime_unlock(&span->specialLock); 703 runtime_m()->locks--; 704 return true; 705 } 706 707 // Removes the Special record of the given kind for the object p. 708 // Returns the record if the record existed, nil otherwise. 709 // The caller must FixAlloc_Free the result. 710 static Special* 711 removespecial(void *p, byte kind) 712 { 713 MSpan *span; 714 Special *s, **t; 715 uintptr offset; 716 717 span = runtime_MHeap_LookupMaybe(&runtime_mheap, p); 718 if(span == nil) 719 runtime_throw("removespecial on invalid pointer"); 720 721 // Ensure that the span is swept. 722 // GC accesses specials list w/o locks. And it's just much safer. 723 runtime_m()->locks++; 724 runtime_MSpan_EnsureSwept(span); 725 726 offset = (uintptr)p - (span->start << PageShift); 727 728 runtime_lock(&span->specialLock); 729 t = &span->specials; 730 while((s = *t) != nil) { 731 // This function is used for finalizers only, so we don't check for 732 // "interior" specials (p must be exactly equal to s->offset). 733 if(offset == s->offset && kind == s->kind) { 734 *t = s->next; 735 runtime_unlock(&span->specialLock); 736 runtime_m()->locks--; 737 return s; 738 } 739 t = &s->next; 740 } 741 runtime_unlock(&span->specialLock); 742 runtime_m()->locks--; 743 return nil; 744 } 745 746 // Adds a finalizer to the object p. Returns true if it succeeded. 747 bool 748 runtime_addfinalizer(void *p, FuncVal *f, const FuncType *ft, const PtrType *ot) 749 { 750 SpecialFinalizer *s; 751 752 runtime_lock(&runtime_mheap.speciallock); 753 s = runtime_FixAlloc_Alloc(&runtime_mheap.specialfinalizeralloc); 754 runtime_unlock(&runtime_mheap.speciallock); 755 s->special.kind = KindSpecialFinalizer; 756 s->fn = f; 757 s->ft = ft; 758 s->ot = ot; 759 if(addspecial(p, &s->special)) 760 return true; 761 762 // There was an old finalizer 763 runtime_lock(&runtime_mheap.speciallock); 764 runtime_FixAlloc_Free(&runtime_mheap.specialfinalizeralloc, s); 765 runtime_unlock(&runtime_mheap.speciallock); 766 return false; 767 } 768 769 // Removes the finalizer (if any) from the object p. 770 void 771 runtime_removefinalizer(void *p) 772 { 773 SpecialFinalizer *s; 774 775 s = (SpecialFinalizer*)removespecial(p, KindSpecialFinalizer); 776 if(s == nil) 777 return; // there wasn't a finalizer to remove 778 runtime_lock(&runtime_mheap.speciallock); 779 runtime_FixAlloc_Free(&runtime_mheap.specialfinalizeralloc, s); 780 runtime_unlock(&runtime_mheap.speciallock); 781 } 782 783 // Set the heap profile bucket associated with addr to b. 784 void 785 runtime_setprofilebucket(void *p, Bucket *b) 786 { 787 SpecialProfile *s; 788 789 runtime_lock(&runtime_mheap.speciallock); 790 s = runtime_FixAlloc_Alloc(&runtime_mheap.specialprofilealloc); 791 runtime_unlock(&runtime_mheap.speciallock); 792 s->special.kind = KindSpecialProfile; 793 s->b = b; 794 if(!addspecial(p, &s->special)) 795 runtime_throw("setprofilebucket: profile already set"); 796 } 797 798 // Do whatever cleanup needs to be done to deallocate s. It has 799 // already been unlinked from the MSpan specials list. 800 // Returns true if we should keep working on deallocating p. 801 bool 802 runtime_freespecial(Special *s, void *p, uintptr size, bool freed) 803 { 804 SpecialFinalizer *sf; 805 SpecialProfile *sp; 806 807 switch(s->kind) { 808 case KindSpecialFinalizer: 809 sf = (SpecialFinalizer*)s; 810 runtime_queuefinalizer(p, sf->fn, sf->ft, sf->ot); 811 runtime_lock(&runtime_mheap.speciallock); 812 runtime_FixAlloc_Free(&runtime_mheap.specialfinalizeralloc, sf); 813 runtime_unlock(&runtime_mheap.speciallock); 814 return false; // don't free p until finalizer is done 815 case KindSpecialProfile: 816 sp = (SpecialProfile*)s; 817 runtime_MProf_Free(sp->b, size, freed); 818 runtime_lock(&runtime_mheap.speciallock); 819 runtime_FixAlloc_Free(&runtime_mheap.specialprofilealloc, sp); 820 runtime_unlock(&runtime_mheap.speciallock); 821 return true; 822 default: 823 runtime_throw("bad special kind"); 824 return true; 825 } 826 } 827 828 // Free all special records for p. 829 void 830 runtime_freeallspecials(MSpan *span, void *p, uintptr size) 831 { 832 Special *s, **t, *list; 833 uintptr offset; 834 835 if(span->sweepgen != runtime_mheap.sweepgen) 836 runtime_throw("runtime: freeallspecials: unswept span"); 837 // first, collect all specials into the list; then, free them 838 // this is required to not cause deadlock between span->specialLock and proflock 839 list = nil; 840 offset = (uintptr)p - (span->start << PageShift); 841 runtime_lock(&span->specialLock); 842 t = &span->specials; 843 while((s = *t) != nil) { 844 if(offset + size <= s->offset) 845 break; 846 if(offset <= s->offset) { 847 *t = s->next; 848 s->next = list; 849 list = s; 850 } else 851 t = &s->next; 852 } 853 runtime_unlock(&span->specialLock); 854 855 while(list != nil) { 856 s = list; 857 list = s->next; 858 if(!runtime_freespecial(s, p, size, true)) 859 runtime_throw("can't explicitly free an object with a finalizer"); 860 } 861 } 862 863 // Split an allocated span into two equal parts. 864 void 865 runtime_MHeap_SplitSpan(MHeap *h, MSpan *s) 866 { 867 MSpan *t; 868 MCentral *c; 869 uintptr i; 870 uintptr npages; 871 PageID p; 872 873 if(s->state != MSpanInUse) 874 runtime_throw("MHeap_SplitSpan on a free span"); 875 if(s->sizeclass != 0 && s->ref != 1) 876 runtime_throw("MHeap_SplitSpan doesn't have an allocated object"); 877 npages = s->npages; 878 879 // remove the span from whatever list it is in now 880 if(s->sizeclass > 0) { 881 // must be in h->central[x].empty 882 c = &h->central[s->sizeclass].mcentral; 883 runtime_lock(&c->lock); 884 runtime_MSpanList_Remove(s); 885 runtime_unlock(&c->lock); 886 runtime_lock(&h->lock); 887 } else { 888 // must be in h->busy/busylarge 889 runtime_lock(&h->lock); 890 runtime_MSpanList_Remove(s); 891 } 892 // heap is locked now 893 894 if(npages == 1) { 895 // convert span of 1 PageSize object to a span of 2 PageSize/2 objects. 896 s->ref = 2; 897 s->sizeclass = runtime_SizeToClass(PageSize/2); 898 s->elemsize = PageSize/2; 899 } else { 900 // convert span of n>1 pages into two spans of n/2 pages each. 901 if((s->npages & 1) != 0) 902 runtime_throw("MHeap_SplitSpan on an odd size span"); 903 904 // compute position in h->spans 905 p = s->start; 906 p -= (uintptr)h->arena_start >> PageShift; 907 908 // Allocate a new span for the first half. 909 t = runtime_FixAlloc_Alloc(&h->spanalloc); 910 runtime_MSpan_Init(t, s->start, npages/2); 911 t->limit = (byte*)((t->start + npages/2) << PageShift); 912 t->state = MSpanInUse; 913 t->elemsize = npages << (PageShift - 1); 914 t->sweepgen = s->sweepgen; 915 if(t->elemsize <= MaxSmallSize) { 916 t->sizeclass = runtime_SizeToClass(t->elemsize); 917 t->ref = 1; 918 } 919 920 // the old span holds the second half. 921 s->start += npages/2; 922 s->npages = npages/2; 923 s->elemsize = npages << (PageShift - 1); 924 if(s->elemsize <= MaxSmallSize) { 925 s->sizeclass = runtime_SizeToClass(s->elemsize); 926 s->ref = 1; 927 } 928 929 // update span lookup table 930 for(i = p; i < p + npages/2; i++) 931 h->spans[i] = t; 932 } 933 934 // place the span into a new list 935 if(s->sizeclass > 0) { 936 runtime_unlock(&h->lock); 937 c = &h->central[s->sizeclass].mcentral; 938 runtime_lock(&c->lock); 939 // swept spans are at the end of the list 940 runtime_MSpanList_InsertBack(&c->empty, s); 941 runtime_unlock(&c->lock); 942 } else { 943 // Swept spans are at the end of lists. 944 if(s->npages < nelem(h->free)) 945 runtime_MSpanList_InsertBack(&h->busy[s->npages], s); 946 else 947 runtime_MSpanList_InsertBack(&h->busylarge, s); 948 runtime_unlock(&h->lock); 949 } 950 }