File tree Expand file tree Collapse file tree 2 files changed +143
-0
lines changed
Expand file tree Collapse file tree 2 files changed +143
-0
lines changed Original file line number Diff line number Diff line change 1+ package lockfree
2+
3+ import (
4+ "sync/atomic"
5+ "unsafe"
6+ )
7+
8+ // Stack implements lock-free freelist based stack.
9+ type Stack struct {
10+ top unsafe.Pointer
11+ len uint64
12+ }
13+
14+ // NewStack creates a new lock-free queue.
15+ func NewStack () * Stack {
16+ return & Stack {}
17+ }
18+
19+ // Pop pops value from the top of the stack.
20+ func (s * Stack ) Pop () interface {} {
21+ var top , next unsafe.Pointer
22+ var item * stackitem
23+ for {
24+ top = atomic .LoadPointer (& s .top )
25+ if top == nil {
26+ return nil
27+ }
28+ item = (* stackitem )(top )
29+ next = atomic .LoadPointer (& item .next )
30+ if atomic .CompareAndSwapPointer (& s .top , top , next ) {
31+ atomic .AddUint64 (& s .len , ^ uint64 (0 ))
32+ return item .v
33+ }
34+ }
35+ }
36+
37+ // Push pushes a value on top of the stack.
38+ func (s * Stack ) Push (v interface {}) {
39+ item := stackitem {v : v }
40+ var top unsafe.Pointer
41+ for {
42+ top = atomic .LoadPointer (& s .top )
43+ item .next = top
44+ if atomic .CompareAndSwapPointer (& s .top , top , unsafe .Pointer (& item )) {
45+ atomic .AddUint64 (& s .len , 1 )
46+ return
47+ }
48+ }
49+ }
50+
51+ type stackitem struct {
52+ next unsafe.Pointer
53+ v interface {}
54+ }
Original file line number Diff line number Diff line change 1+ package lockfree_test
2+
3+ import (
4+ "fmt"
5+ "math/rand"
6+ "sync"
7+ "sync/atomic"
8+ "testing"
9+
10+ "github.com/changkun/lockfree"
11+ )
12+
13+ func TestStackPopEmpty (t * testing.T ) {
14+ s := lockfree .NewStack ()
15+ if s .Pop () != nil {
16+ t .Fatal ("pop empty stack returns non-nil" )
17+ }
18+ }
19+
20+ func ExampleStack () {
21+ s := lockfree .NewStack ()
22+
23+ s .Push (1 )
24+ s .Push (2 )
25+ s .Push (3 )
26+
27+ fmt .Println (s .Pop ())
28+ fmt .Println (s .Pop ())
29+ fmt .Println (s .Pop ())
30+
31+ // Output:
32+ // 3
33+ // 2
34+ // 1
35+ }
36+
37+ type stackInterface interface {
38+ Push (interface {})
39+ Pop () interface {}
40+ }
41+
42+ type mutexStack struct {
43+ v []interface {}
44+ mu sync.Mutex
45+ }
46+
47+ func newMutexStack () * mutexStack {
48+ return & mutexStack {v : make ([]interface {}, 0 )}
49+ }
50+
51+ func (s * mutexStack ) Push (v interface {}) {
52+ s .mu .Lock ()
53+ s .v = append (s .v , v )
54+ s .mu .Unlock ()
55+ }
56+
57+ func (s * mutexStack ) Pop () interface {} {
58+ s .mu .Lock ()
59+ v := s .v [len (s .v )]
60+ s .v = s .v [:len (s .v )- 1 ]
61+ s .mu .Unlock ()
62+ return v
63+ }
64+
65+ func BenchmarkStack (b * testing.B ) {
66+ length := 1 << 12
67+ inputs := make ([]int , length )
68+ for i := 0 ; i < length ; i ++ {
69+ inputs = append (inputs , rand .Int ())
70+ }
71+ s , ms := lockfree .NewStack (), newMutexStack ()
72+ b .ResetTimer ()
73+ for _ , s := range [... ]stackInterface {s , ms } {
74+ b .Run (fmt .Sprintf ("%T" , s ), func (b * testing.B ) {
75+ var c int64
76+ b .RunParallel (func (pb * testing.PB ) {
77+ for pb .Next () {
78+ i := int (atomic .AddInt64 (& c , 1 )- 1 ) % length
79+ v := inputs [i ]
80+ if v >= 0 {
81+ s .Push (v )
82+ } else {
83+ s .Pop ()
84+ }
85+ }
86+ })
87+ })
88+ }
89+ }
You can’t perform that action at this time.
0 commit comments