github.com/GoWebProd/gip@v0.0.0-20230623090727-b60d41d5d320/stack/stack.go (about)

     1  package stack
     2  
     3  import (
     4  	"runtime"
     5  	"sync/atomic"
     6  	"unsafe"
     7  
     8  	"github.com/GoWebProd/gip/allocator"
     9  )
    10  
    11  type node[T any] struct {
    12  	next uintptr
    13  	data T
    14  }
    15  
    16  type Stack[T any] uintptr
    17  
    18  func (head *Stack[T]) Init() {
    19  	runtime.SetFinalizer(head, func(head *Stack[T]) {
    20  		for {
    21  			_, ok := head.Pop()
    22  			if !ok {
    23  				break
    24  			}
    25  		}
    26  	})
    27  }
    28  
    29  func (head *Stack[T]) Push(v T) {
    30  	node := allocator.AllocObject[node[T]]()
    31  	node.data = v
    32  	new := uintptr(unsafe.Pointer(node))
    33  
    34  	for {
    35  		old := atomic.LoadUintptr((*uintptr)(head))
    36  		node.next = old
    37  
    38  		if atomic.CompareAndSwapUintptr((*uintptr)(head), old, new) {
    39  			break
    40  		}
    41  	}
    42  }
    43  
    44  func (head *Stack[T]) Pop() (T, bool) {
    45  	var v T
    46  
    47  	for {
    48  		old := atomic.LoadUintptr((*uintptr)(head))
    49  		if old == 0 {
    50  			return v, false
    51  		}
    52  
    53  		node := (*node[T])(unsafe.Pointer(old))
    54  		next := atomic.LoadUintptr(&node.next)
    55  
    56  		if atomic.CompareAndSwapUintptr((*uintptr)(head), old, next) {
    57  			allocator.FreeObject(node)
    58  
    59  			return node.data, true
    60  		}
    61  	}
    62  }