github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/unsafekit/keep_alive.go (about)

     1  // Copyright 2020 Insolar Network Ltd.
     2  // All rights reserved.
     3  // This material is licensed under the Insolar License version 1.0,
     4  // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md.
     5  
     6  package unsafekit
     7  
     8  import (
     9  	"fmt"
    10  	"reflect"
    11  	"runtime"
    12  	"unsafe"
    13  
    14  	"github.com/insolar/vanilla/longbits"
    15  )
    16  
    17  func KeepAliveWhile(p unsafe.Pointer, fn func(unsafe.Pointer) uintptr) uintptr {
    18  	res := fn(p)
    19  	runtime.KeepAlive(p)
    20  	return res
    21  }
    22  
    23  const keepAlivePageLen = 4096/PtrSize - 4
    24  
    25  type keepAlivePage [keepAlivePageLen]unsafe.Pointer
    26  
    27  type KeepAliveList struct {
    28  	list          []unsafe.Pointer
    29  	lastPageCount uint16
    30  	listOfPages   bool
    31  }
    32  
    33  func (p *KeepAliveList) Reset() {
    34  	*p = KeepAliveList{}
    35  }
    36  
    37  func (p *KeepAliveList) Keep(v interface{}) {
    38  	vv := reflect.ValueOf(v)
    39  	switch vv.Kind() {
    40  	case reflect.Chan, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Slice:
    41  		p.keep(vv.Pointer())
    42  	case reflect.String:
    43  		p.keepStrData(vv.String())
    44  	default:
    45  		panic(fmt.Sprint("unsupported value", vv))
    46  	}
    47  	runtime.KeepAlive(v)
    48  }
    49  
    50  func (p *KeepAliveList) KeepDataOf(v longbits.ByteString) {
    51  	p.keepStrData(string(v))
    52  	runtime.KeepAlive(v)
    53  }
    54  
    55  func (p *KeepAliveList) KeepDataOfBytes(v []byte) {
    56  	pSlice := (*reflect.SliceHeader)(unsafe.Pointer(&v))
    57  	p.keep(pSlice.Data)
    58  	runtime.KeepAlive(v)
    59  }
    60  
    61  func (p *KeepAliveList) keepStrData(s string) {
    62  	pString := (*reflect.StringHeader)(unsafe.Pointer(&s))
    63  	p.keep(pString.Data)
    64  }
    65  
    66  func (p *KeepAliveList) keep(v uintptr) {
    67  	switch {
    68  	case p.listOfPages:
    69  		if p.lastPageCount == keepAlivePageLen {
    70  			break
    71  		}
    72  
    73  		switch {
    74  		case len(p.list) < 2:
    75  			panic("illegal state")
    76  		case p.lastPageCount > keepAlivePageLen:
    77  			panic("illegal state")
    78  		}
    79  		lastPage := (*keepAlivePage)(p.list[len(p.list)-1])
    80  		lastPage[p.lastPageCount] = unsafe.Pointer(v)
    81  		p.lastPageCount++
    82  		return
    83  
    84  	case p.lastPageCount != 0:
    85  		panic("illegal state")
    86  
    87  	case len(p.list) == keepAlivePageLen:
    88  		lastPage := new(keepAlivePage)
    89  		copy(lastPage[:], p.list)
    90  		p.list = make([]unsafe.Pointer, 1, 2)
    91  		p.list[0] = unsafe.Pointer(lastPage)
    92  
    93  	case len(p.list) > keepAlivePageLen:
    94  		panic("illegal state")
    95  
    96  	default:
    97  		p.list = append(p.list, unsafe.Pointer(v))
    98  		return
    99  	}
   100  
   101  	switch {
   102  	case p.lastPageCount != keepAlivePageLen:
   103  		panic("illegal state")
   104  	case len(p.list) < 1:
   105  		panic("illegal state")
   106  	}
   107  
   108  	lastPage := new(keepAlivePage)
   109  	p.list = append(p.list, (unsafe.Pointer)(lastPage))
   110  	lastPage[0] = unsafe.Pointer(p)
   111  	p.lastPageCount = 1
   112  }