github.com/haraldrudell/parl@v0.4.176/close-channel.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package parl 7 8 import ( 9 "github.com/haraldrudell/parl/perrors" 10 "github.com/haraldrudell/parl/pruntime" 11 ) 12 13 const ( 14 CloseChannelDrain = true 15 ) 16 17 // CloseChannel closes a channel 18 // - CloseChannel is thread-safe, deferrable and panic-free, 19 // handles closed-channel panic, nil-channel case and 20 // has channel drain feature 21 // - isNilChannel returns true if ch is nil. 22 // closing a nil channel would cause panic. 23 // - isCloseOfClosedChannel is true if close paniced due to 24 // the channel already closed. 25 // A channel transferring data cannot be inspected for being 26 // closed 27 // - if errp is non-nil, panic values updates it using errors.AppendError. 28 // - if doDrain is [parl.CloseChannelDrain], the channel is drained first. 29 // Note: closing a channel while a thread is blocked in channel send is 30 // a data race. 31 // If a thread is continuously sending items and doDrain is true, 32 // CloseChannel will block indefinitely. 33 // - n returns the number of drained items. 34 func CloseChannel[T any](ch chan T, errp *error, drainChannel ...bool) ( 35 isNilChannel, isCloseOfClosedChannel bool, n int, err error, 36 ) { 37 38 // nil channel case 39 if isNilChannel = ch == nil; isNilChannel { 40 return // closing of nil channel return 41 } 42 43 // channel drain feature 44 if len(drainChannel) > 0 && drainChannel[0] { 45 for { 46 select { 47 // read non-blocking from the channel 48 // - ok true: received item, channel is not closed 49 // - ok false: channel is closed 50 case _, ok := <-ch: 51 if ok { 52 // the channel is not closed 53 n++ 54 continue // read next item 55 } 56 default: // channel is open but has no items 57 } 58 break // closed or no items 59 } 60 } 61 62 // close channel 63 if Closer(ch, &err); err == nil { 64 return // close successful return 65 } 66 67 // handle close error 68 isCloseOfClosedChannel = pruntime.IsCloseOfClosedChannel(err) 69 if errp != nil { 70 *errp = perrors.AppendError(*errp, err) 71 } 72 73 return 74 }