-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathclose-channel.go
74 lines (65 loc) · 2.03 KB
/
close-channel.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
/*
© 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
ISC License
*/
package parl
import (
"github.com/haraldrudell/parl/perrors"
"github.com/haraldrudell/parl/pruntime"
)
const (
CloseChannelDrain = true
)
// CloseChannel closes a channel
// - CloseChannel is thread-safe, deferrable and panic-free,
// handles closed-channel panic, nil-channel case and
// has channel drain feature
// - isNilChannel returns true if ch is nil.
// closing a nil channel would cause panic.
// - isCloseOfClosedChannel is true if close paniced due to
// the channel already closed.
// A channel transferring data cannot be inspected for being
// closed
// - if errp is non-nil, panic values updates it using errors.AppendError.
// - if doDrain is [parl.CloseChannelDrain], 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.
func CloseChannel[T any](ch chan T, errp *error, drainChannel ...bool) (
isNilChannel, isCloseOfClosedChannel bool, n int, err error,
) {
// nil channel case
if isNilChannel = ch == nil; isNilChannel {
return // closing of nil channel return
}
// channel drain feature
if len(drainChannel) > 0 && drainChannel[0] {
for {
select {
// read non-blocking from the channel
// - ok true: received item, channel is not closed
// - ok false: channel is closed
case _, ok := <-ch:
if ok {
// the channel is not closed
n++
continue // read next item
}
default: // channel is open but has no items
}
break // closed or no items
}
}
// close channel
if Closer(ch, &err); err == nil {
return // close successful return
}
// handle close error
isCloseOfClosedChannel = pruntime.IsCloseOfClosedChannel(err)
if errp != nil {
*errp = perrors.AppendError(*errp, err)
}
return
}