-
Notifications
You must be signed in to change notification settings - Fork 3
/
fuse.go
84 lines (76 loc) · 1.48 KB
/
fuse.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package core
import (
"sync"
"sync/atomic"
)
const (
fuseNotInitialized = iota
fuseReady
fuseBroken
)
// Fuse is a thread-safe one-way switch, used for permanent state changes.
// Implementation partially borrowed from sync.Once
type Fuse struct {
state uint32
m sync.Mutex
c chan struct{}
}
// IsBroken returns true if the fuse has been broken
func (f *Fuse) IsBroken() bool {
switch atomic.LoadUint32(&f.state) {
case fuseNotInitialized:
return false
case fuseBroken:
return true
default:
select {
case <-f.c:
atomic.StoreUint32(&f.state, fuseBroken)
return true
default:
return false
}
}
}
func (f *Fuse) init() {
if atomic.LoadUint32(&f.state) != fuseNotInitialized {
return
}
f.m.Lock()
defer f.m.Unlock()
if atomic.LoadUint32(&f.state) != fuseNotInitialized {
return
}
f.c = make(chan struct{})
atomic.StoreUint32(&f.state, fuseReady)
}
// Watch returns a channel which will close once the fuse is broken
func (f *Fuse) Watch() <-chan struct{} {
f.init()
return f.c
}
// Break breaks the fuse
func (f *Fuse) Break() {
f.Once(nil)
}
// Once runs the callback and breaks the fuse
func (f *Fuse) Once(do func()) {
if atomic.LoadUint32(&f.state) == fuseBroken {
return
}
f.m.Lock()
defer f.m.Unlock()
switch atomic.LoadUint32(&f.state) {
case fuseBroken:
return
case fuseNotInitialized:
f.c = make(chan struct{})
fallthrough
default:
defer atomic.StoreUint32(&f.state, fuseBroken)
if do != nil {
do()
}
close(f.c)
}
}