@@ -205,8 +205,9 @@ func (b gcBlock) free() {
205205
206206// objHeader is a structure appended to every heap object to hold metadata.
207207type objHeader struct {
208- // next is the next object to scan after this.
209- next * objHeader
208+ // next links the GC scan list. Manual allocations remain permanently marked
209+ // and use the otherwise invalid value 1 as an until-free marker.
210+ next uintptr
210211
211212 // layout holds the layout bitmap used to find pointers in the object.
212213 layout gcLayout
@@ -482,6 +483,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
482483 // Create the object header.
483484 size -= unsafe .Sizeof (objHeader {})
484485 header := (* objHeader )(unsafe .Add (pointer , size ))
486+ header .next = 0
485487 header .layout = parseGCLayout (layout )
486488
487489 // We've claimed this allocation, now we can unlock the heap.
@@ -500,42 +502,61 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
500502 return pointer
501503}
502504
503- func realloc (ptr unsafe.Pointer , size uintptr ) unsafe.Pointer {
504- if ptr == nil {
505- return alloc (size , gclayout .NoPtrs .AsPtr ())
505+ // allocManual allocates pointer-free memory that remains live until freeManual.
506+ func allocManual (size uintptr ) unsafe.Pointer {
507+ if size == 0 {
508+ return alloc_zero (size , gclayout .NoPtrs .AsPtr ())
506509 }
510+ ptr := alloc (size , gclayout .NoPtrs .AsPtr ())
507511
508- // Find the first block of the original allocation.
509- firstBlock := blockFromAddr (uintptr (ptr ))
510-
511- // Find the last block of the original allocation.
512- lastBlock := firstBlock .findHead ()
512+ gcLock .Lock ()
513+ head := blockFromAddr (uintptr (ptr )).findHead ()
514+ head .setState (blockStateMark )
515+ header := (* objHeader )(unsafe .Add (head .pointer (), bytesPerBlock - unsafe .Sizeof (objHeader {})))
516+ header .next = 1
517+ gcLock .Unlock ()
518+ return ptr
519+ }
513520
514- // Calculate the size of the original allocation body.
515- oldSize := uintptr (lastBlock - firstBlock )* bytesPerBlock + (bytesPerBlock - unsafe .Sizeof (objHeader {}))
521+ func free (ptr unsafe.Pointer ) {
522+ if ptr == nil {
523+ return
524+ }
516525
517- if size <= oldSize {
518- // The requested size is less than the old size.
519- // There are likely scenarios for this:
520- // - The caller intended to grow the allocation, but the original size
521- // was rounded up by alloc to a multiple of the block size.
522- // The rounded size is already sufficient.
523- // - The caller intended to shrink the allocation.
524- // We currently ignore this case.
525- // Either way, the current allocation can be left alone.
526- return ptr
526+ gcLock .Lock ()
527+ addr := uintptr (ptr )
528+ if ! isOnHeap (addr ) || (addr - heapStart )% bytesPerBlock != 0 {
529+ gcLock .Unlock ()
530+ runtimeFatal ("free: invalid pointer" )
527531 }
528532
529- // Create a new allocation and copy the old data.
530- newAlloc := alloc (size , gclayout .NoPtrs .AsPtr ())
531- memcpy (newAlloc , ptr , oldSize )
532- free (ptr )
533+ firstBlock := blockFromAddr (addr )
534+ state := firstBlock .state ()
535+ if state != blockStateTail && state != blockStateHead && state != blockStateMark {
536+ gcLock .Unlock ()
537+ runtimeFatal ("free: invalid pointer" )
538+ }
533539
534- return newAlloc
535- }
540+ allocationStart := firstBlock
541+ for allocationStart != 0 && (allocationStart - 1 ).state () == blockStateTail {
542+ allocationStart --
543+ }
544+ if allocationStart != firstBlock {
545+ gcLock .Unlock ()
546+ runtimeFatal ("free: invalid pointer" )
547+ }
536548
537- func free (ptr unsafe.Pointer ) {
538- // TODO: free blocks on request, when the compiler knows they're unused.
549+ lastBlock := firstBlock .findHead ()
550+ header := (* objHeader )(unsafe .Add (lastBlock .pointer (), bytesPerBlock - unsafe .Sizeof (objHeader {})))
551+ if header .next != 1 {
552+ gcLock .Unlock ()
553+ runtimeFatal ("free: invalid pointer" )
554+ }
555+ for block := firstBlock ; block <= lastBlock ; block ++ {
556+ block .free ()
557+ }
558+ insertFreeRange (firstBlock .pointer (), uintptr (lastBlock - firstBlock + 1 ))
559+ gcLock .Unlock ()
539560}
540561
541562// GC performs a garbage collection cycle.
@@ -666,7 +687,7 @@ func finishMark() {
666687 if obj == nil {
667688 return
668689 }
669- scanList = obj .next
690+ scanList = ( * objHeader )( unsafe . Pointer ( obj .next ))
670691
671692 // Check if the object may contain pointers.
672693 if obj .layout .pointerFree () {
@@ -724,7 +745,7 @@ func markRoot(addr, root uintptr) {
724745
725746 // Add the object to the scan list.
726747 header := (* objHeader )(unsafe .Add (head .pointer (), bytesPerBlock - unsafe .Sizeof (objHeader {})))
727- header .next = scanList
748+ header .next = uintptr ( unsafe . Pointer ( scanList ))
728749 scanList = header
729750}
730751
@@ -758,7 +779,10 @@ func sweep() uintptr {
758779
759780 // Unmark the next head.
760781 block --
761- block .unmark ()
782+ header := (* objHeader )(unsafe .Add (block .pointer (), bytesPerBlock - unsafe .Sizeof (objHeader {})))
783+ if header .next != 1 {
784+ block .unmark ()
785+ }
762786
763787 // Skip the tail.
764788 for block > 0 && (block - 1 ).state () == blockStateTail {
@@ -903,5 +927,19 @@ func SetFinalizer(obj interface{}, finalizer interface{}) {
903927 // A nil pointer has nothing to finalize.
904928 return
905929 }
930+
931+ gcLock .Lock ()
932+ addr := uintptr (objPtr )
933+ manual := false
934+ if isOnHeap (addr ) {
935+ head := blockFromAddr (addr ).findHead ()
936+ header := (* objHeader )(unsafe .Add (head .pointer (), bytesPerBlock - unsafe .Sizeof (objHeader {})))
937+ manual = header .next == 1
938+ }
939+ gcLock .Unlock ()
940+ if manual && finalizer != nil {
941+ runtimeFatal ("runtime.SetFinalizer: manual allocation" )
942+ }
943+
906944 registerFinalizer (uintptr (objPtr ), finalizer )
907945}
0 commit comments