You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			84 lines
		
	
	
		
			1.4 KiB
		
	
	
	
		
			Go
		
	
		
		
			
		
	
	
			84 lines
		
	
	
		
			1.4 KiB
		
	
	
	
		
			Go
		
	
| 
											2 years ago
										 | package mpsc | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"sync/atomic" | ||
|  | 	"unsafe" | ||
|  | ) | ||
|  | 
 | ||
|  | type ( | ||
|  | 	Queue[T any] struct { | ||
|  | 		head   unsafe.Pointer | ||
|  | 		tail   unsafe.Pointer | ||
|  | 		length int64 | ||
|  | 	} | ||
|  | 	Node[T any] struct { | ||
|  | 		value T | ||
|  | 		next  unsafe.Pointer | ||
|  | 	} | ||
|  | ) | ||
|  | 
 | ||
|  | func New[T any]() *Queue[T] { | ||
|  | 	n := unsafe.Pointer(new(Node[T])) | ||
|  | 	return &Queue[T]{ | ||
|  | 		head: n, | ||
|  | 		tail: n, | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (q *Queue[T]) Push(v T) { | ||
|  | 	n := &Node[T]{value: v} | ||
|  | 	for { | ||
|  | 		tail := load[T](&q.tail) | ||
|  | 		next := load[T](&tail.next) | ||
|  | 		if tail == load[T](&q.tail) { | ||
|  | 			atomic.AddInt64(&q.length, 1) | ||
|  | 			if next == nil { | ||
|  | 				if cas(&tail.next, next, n) { | ||
|  | 					cas(&q.tail, tail, n) | ||
|  | 					return | ||
|  | 				} | ||
|  | 			} else { | ||
|  | 				cas(&q.tail, tail, next) | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (q *Queue[T]) Pop() (v T) { | ||
|  | 	for { | ||
|  | 		head := load[T](&q.head) | ||
|  | 		tail := load[T](&q.tail) | ||
|  | 		next := load[T](&head.next) | ||
|  | 		if head == load[T](&q.head) { | ||
|  | 			if head == tail { | ||
|  | 				if next == nil { | ||
|  | 					return | ||
|  | 				} | ||
|  | 				cas(&q.tail, tail, next) | ||
|  | 			} else { | ||
|  | 				v = next.value | ||
|  | 				atomic.AddInt64(&q.length, -1) | ||
|  | 				if cas(&q.head, head, next) { | ||
|  | 					return | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | func (q *Queue[T]) Length() int64 { | ||
|  | 	return q.length | ||
|  | } | ||
|  | 
 | ||
|  | func (q *Queue[T]) Empty() bool { | ||
|  | 	return q.length == 0 | ||
|  | } | ||
|  | 
 | ||
|  | func load[T any](p *unsafe.Pointer) *Node[T] { | ||
|  | 	return (*Node[T])(atomic.LoadPointer(p)) | ||
|  | } | ||
|  | 
 | ||
|  | func cas[T any](p *unsafe.Pointer, old, new *Node[T]) bool { | ||
|  | 	return atomic.CompareAndSwapPointer(p, | ||
|  | 		unsafe.Pointer(old), unsafe.Pointer(new)) | ||
|  | } |