-
Notifications
You must be signed in to change notification settings - Fork 0
/
reaper.go
125 lines (113 loc) · 3.35 KB
/
reaper.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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package kdone
import "github.com/go-kata/kerror"
// Reaper represents a mechanism of deferred call of destructors.
type Reaper struct {
// destructors specifies destructors the responsibility of calling which was assumed by this reaper.
destructors []Destructor
// released specifies whether was this reaper released
// from the responsibility for calling destructors.
released bool
// finalized specifies whether did this reapers call destructors.
finalized bool
}
// NewReaper returns a new reaper.
func NewReaper() *Reaper {
return &Reaper{}
}
// Assume passes the responsibility for calling the given destructor to this reaper.
func (r *Reaper) Assume(dtor Destructor) error {
if r == nil {
return kerror.New(kerror.ENil, "nil reaper cannot assume responsibility for calling destructor")
}
if r.released {
return kerror.New(kerror.EIllegal,
"reaper was already released from responsibility for calling destructors")
}
if r.finalized {
return kerror.New(kerror.EIllegal, "reaper has already called destructors")
}
if dtor == nil {
return kerror.New(kerror.EInvalid, "reaper cannot assume responsibility for calling nil destructor")
}
r.destructors = append(r.destructors, dtor)
return nil
}
// MustAssume is a variant of the Assume that panics on error.
func (r *Reaper) MustAssume(dtor Destructor) {
if err := r.Assume(dtor); err != nil {
panic(err)
}
}
// Release releases this reaper from the responsibility for calling destructors
// and pass it to caller by return a composite destructor that calls destructors
// in the backward order.
func (r *Reaper) Release() (Destructor, error) {
if r == nil {
return Noop, nil
}
if r.released {
return nil, kerror.New(kerror.EIllegal,
"reaper was already released from responsibility for calling destructors")
}
if r.finalized {
return nil, kerror.New(kerror.EIllegal, "reaper has already called destructors")
}
r.released = true
destructors := r.destructors
return DestructorFunc(func() error {
return reap(destructors...)
}), nil
}
// MustRelease is a variant of the Release that panics on error.
func (r *Reaper) MustRelease() Destructor {
dtor, err := r.Release()
if err != nil {
panic(err)
}
return dtor
}
// Released returns boolean specifies whether was this reaper released
// from the responsibility for calling destructors.
func (r *Reaper) Released() bool {
if r == nil {
return false
}
return r.released
}
// Finalize calls destructors in the backward order
// if this reaper was not released from this responsibility yet.
func (r *Reaper) Finalize() error {
if r == nil || r.released {
return nil
}
if r.finalized {
return kerror.New(kerror.EIllegal, "reaper has already called destructors")
}
defer func() {
r.finalized = true
}()
return reap(r.destructors...)
}
// MustFinalize is a variant of the Finalize that panics on error.
func (r *Reaper) MustFinalize() {
if err := r.Finalize(); err != nil {
panic(err)
}
}
// Finalized returns boolean specifies whether did this reapers call destructors.
func (r *Reaper) Finalized() bool {
if r == nil {
return false
}
return r.finalized
}
// reap calls given destructors in the backward order.
func reap(destructors ...Destructor) error {
coerr := kerror.NewCollector()
for i := len(destructors) - 1; i >= 0; i-- {
coerr.Collect(kerror.Try(func() error {
return destructors[i].Destroy()
}))
}
return coerr.Error()
}