mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 11:52:24 -05:00 
			
		
		
		
	
		
			
	
	
		
			100 lines
		
	
	
	
		
			1.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			100 lines
		
	
	
	
		
			1.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | package mempool | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"unsafe" | ||
|  | ) | ||
|  | 
 | ||
|  | const DefaultDirtyFactor = 128 | ||
|  | 
 | ||
|  | // Pool provides a type-safe form | ||
|  | // of UnsafePool using generics. | ||
|  | // | ||
|  | // Note it is NOT safe for concurrent | ||
|  | // use, you must protect it yourself! | ||
|  | type Pool[T any] struct { | ||
|  | 
 | ||
|  | 	// New is an optionally provided | ||
|  | 	// allocator used when no value | ||
|  | 	// is available for use in pool. | ||
|  | 	New func() T | ||
|  | 
 | ||
|  | 	// Reset is an optionally provided | ||
|  | 	// value resetting function called | ||
|  | 	// on passed value to Put(). | ||
|  | 	Reset func(T) | ||
|  | 
 | ||
|  | 	UnsafePool | ||
|  | } | ||
|  | 
 | ||
|  | func (p *Pool[T]) Get() T { | ||
|  | 	if ptr := p.UnsafePool.Get(); ptr != nil { | ||
|  | 		return *(*T)(ptr) | ||
|  | 	} else if p.New != nil { | ||
|  | 		return p.New() | ||
|  | 	} | ||
|  | 	var z T | ||
|  | 	return z | ||
|  | } | ||
|  | 
 | ||
|  | func (p *Pool[T]) Put(t T) { | ||
|  | 	if p.Reset != nil { | ||
|  | 		p.Reset(t) | ||
|  | 	} | ||
|  | 	ptr := unsafe.Pointer(&t) | ||
|  | 	p.UnsafePool.Put(ptr) | ||
|  | } | ||
|  | 
 | ||
|  | // UnsafePool provides an incredibly | ||
|  | // simple memory pool implementation | ||
|  | // that stores ptrs to memory values, | ||
|  | // and regularly flushes internal pool | ||
|  | // structures according to DirtyFactor. | ||
|  | // | ||
|  | // Note it is NOT safe for concurrent | ||
|  | // use, you must protect it yourself! | ||
|  | type UnsafePool struct { | ||
|  | 
 | ||
|  | 	// DirtyFactor determines the max | ||
|  | 	// number of $dirty count before | ||
|  | 	// pool is garbage collected. Where: | ||
|  | 	// $dirty = len(current) - len(victim) | ||
|  | 	DirtyFactor int | ||
|  | 
 | ||
|  | 	current []unsafe.Pointer | ||
|  | 	victim  []unsafe.Pointer | ||
|  | } | ||
|  | 
 | ||
|  | func (p *UnsafePool) Get() unsafe.Pointer { | ||
|  | 	// First try current list. | ||
|  | 	if len(p.current) > 0 { | ||
|  | 		ptr := p.current[len(p.current)-1] | ||
|  | 		p.current = p.current[:len(p.current)-1] | ||
|  | 		return ptr | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Fallback to victim. | ||
|  | 	if len(p.victim) > 0 { | ||
|  | 		ptr := p.victim[len(p.victim)-1] | ||
|  | 		p.victim = p.victim[:len(p.victim)-1] | ||
|  | 		return ptr | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | func (p *UnsafePool) Put(ptr unsafe.Pointer) { | ||
|  | 	p.current = append(p.current, ptr) | ||
|  | 
 | ||
|  | 	// Get dirty factor. | ||
|  | 	df := p.DirtyFactor | ||
|  | 	if df == 0 { | ||
|  | 		df = DefaultDirtyFactor | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if len(p.current)-len(p.victim) > df { | ||
|  | 		// Garbage collection! | ||
|  | 		p.victim = p.current | ||
|  | 		p.current = nil | ||
|  | 	} | ||
|  | } |