github.com/leslie-fei/fastcache@v0.0.0-20240520092641-b7a9eb05711f/allocator.go (about)

     1  package fastcache
     2  
     3  import (
     4  	"errors"
     5  	"unsafe"
     6  )
     7  
     8  var sizeOfShardMemory = unsafe.Sizeof(shardMemory{})
     9  var ErrAllocSizeTooLarge = errors.New("alloc size too large")
    10  
    11  type Allocator interface {
    12  	Alloc(size uint64) (ptr unsafe.Pointer, offset uint64, err error)
    13  	Base() uintptr
    14  	Locker() Locker
    15  }
    16  
    17  // globalAllocator 全局的内存分配, 所有的内存分配最终都是通过他分配出去
    18  type globalAllocator struct {
    19  	mem      Memory
    20  	metadata *Metadata
    21  }
    22  
    23  func (g *globalAllocator) Alloc(size uint64) (ptr unsafe.Pointer, offset uint64, err error) {
    24  	if size > g.FreeMemory() {
    25  		err = ErrNoSpace
    26  		return
    27  	}
    28  	ptr = g.mem.PtrOffset(g.metadata.Used)
    29  	offset = g.metadata.Used
    30  	g.metadata.Used += size
    31  	return
    32  }
    33  
    34  func (g *globalAllocator) FreeMemory() uint64 {
    35  	return g.metadata.TotalSize - g.metadata.Used
    36  }
    37  
    38  func (g *globalAllocator) Base() uintptr {
    39  	return uintptr(g.mem.Ptr())
    40  }
    41  
    42  func (g *globalAllocator) Locker() Locker {
    43  	return g.metadata.GlobalLocker
    44  }
    45  
    46  type shardMemory struct {
    47  	offset uint64
    48  	size   uint64
    49  	used   uint64
    50  	next   uint64
    51  }
    52  
    53  func (s *shardMemory) Reset() {
    54  	s.size = 0
    55  	s.offset = 0
    56  	s.used = 0
    57  	s.next = 0
    58  }
    59  
    60  func (s *shardMemory) FreeMemory() uint64 {
    61  	return s.size - s.used
    62  }
    63  
    64  // shardAllocator 归属于shard的内存分配, 他们都先从 globalAllocator 中分配出内存, 给shard独享
    65  type shardAllocator struct {
    66  	global         *globalAllocator
    67  	first          uint64
    68  	shardMemoryLen uint32
    69  	growSize       uint64
    70  }
    71  
    72  func (s *shardAllocator) Alloc(size uint64) (ptr unsafe.Pointer, offset uint64, err error) {
    73  	if size > s.growSize-uint64(sizeOfShardMemory) {
    74  		err = ErrAllocSizeTooLarge
    75  		return
    76  	}
    77  	mem := s.findShardMemory(size)
    78  	if mem == nil {
    79  		// 当shardMemory没有可以分配出size大小的块, 就需要去globalAllocator中申请
    80  		globalLocker := s.global.Locker()
    81  		globalLocker.Lock()
    82  		ptr, offset, err = s.global.Alloc(s.growSize)
    83  		globalLocker.Unlock()
    84  		if err != nil {
    85  			return
    86  		}
    87  		newMem := (*shardMemory)(ptr)
    88  		newMem.Reset()
    89  		newMem.offset = offset
    90  		newMem.size = s.growSize - uint64(sizeOfShardMemory)
    91  		if s.first == 0 {
    92  			s.first = newMem.offset
    93  		} else {
    94  			next := s.first
    95  			s.first = newMem.offset
    96  			newMem.next = next
    97  		}
    98  		mem = newMem
    99  	}
   100  
   101  	offset = mem.offset + mem.used
   102  	ptr = unsafe.Pointer(s.Base() + uintptr(offset))
   103  	mem.used += size
   104  	return
   105  }
   106  
   107  func (s *shardAllocator) Base() uintptr {
   108  	return s.global.Base()
   109  }
   110  
   111  func (s *shardAllocator) Locker() Locker {
   112  	return nopLocker
   113  }
   114  
   115  // findShardMemory 找到一个可用的shardMemory
   116  func (s *shardAllocator) findShardMemory(size uint64) *shardMemory {
   117  	offset := s.first
   118  	for i := 0; i < int(s.shardMemoryLen); i++ {
   119  		mem := (*shardMemory)(unsafe.Pointer(s.Base() + uintptr(offset)))
   120  		if mem.FreeMemory() >= size {
   121  			return mem
   122  		}
   123  		offset = mem.next
   124  	}
   125  	return nil
   126  }