-
Notifications
You must be signed in to change notification settings - Fork 1
/
closer.go
94 lines (83 loc) · 2.47 KB
/
closer.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
/*
© 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
ISC License
*/
package parl
import (
"io"
"github.com/haraldrudell/parl/perrors"
"github.com/haraldrudell/parl/pruntime"
)
const (
CloseChannelDrain = true
)
// Closer is a deferrable function that closes a channel.
// Closer handles panics.
// if errp is non-nil, panic values updates it using errors.AppendError.
func Closer[T any](ch chan T, errp *error) {
defer Recover(Annotation(), errp, NoOnError)
close(ch)
}
// CloserSend is a deferrable function that closes a send-channel.
// CloserSend handles panics.
// if errp is non-nil, panic values updates it using errors.AppendError.
func CloserSend[T any](ch chan<- T, errp *error) {
defer Recover(Annotation(), errp, NoOnError)
close(ch)
}
// Close is a deferrable function that closes an io.Closer object.
// Close handles panics.
// if errp is non-nil, panic values updates it using errors.AppendError.
func Close(closable io.Closer, errp *error) {
defer Recover(Annotation(), errp, NoOnError)
if e := closable.Close(); e != nil {
*errp = perrors.AppendError(*errp, e)
}
}
// CloseChannel closes a channel recovering panics
// - deferrable
// - if errp is non-nil, panic values updates it using errors.AppendError.
// - if doDrain is CloseChannelDrain or true, the channel is drained first.
// Note: closing a channel while a thread is blocked in channel send is
// a data race.
// If a thread is continuously sending items and doDrain is true,
// CloseChannel will block indefinitely.
// - n returns the number of drained items.
// - isNilChannel returns true if ch is nil.
// No close will be attempted for a nil channel, it would panic.
func CloseChannel[T any](ch chan T, errp *error, drainChannel ...bool) (
isNilChannel, isCloseOfClosedChannel bool, n int, err error,
) {
if isNilChannel = ch == nil; isNilChannel {
return // closing of nil channel return
}
var doDrain bool
if len(drainChannel) > 0 {
doDrain = drainChannel[0]
}
if doDrain {
var hasItems = true
for hasItems {
select {
// read non-blocking from the channel
case _, ok := <-ch:
if ok {
// the channel is not closed
n++
continue // read next item
}
default:
}
hasItems = false
}
}
Closer(ch, &err)
if err == nil {
return // close successful
}
isCloseOfClosedChannel = pruntime.IsCloseOfClosedChannel(err)
if errp != nil {
*errp = perrors.AppendError(*errp, err)
}
return
}