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