github.com/haraldrudell/parl@v0.4.176/pio/context-closer.go (about)

     1  /*
     2  © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package pio
     7  
     8  import (
     9  	"io"
    10  	"sync/atomic"
    11  
    12  	"github.com/haraldrudell/parl"
    13  )
    14  
    15  // ContextCloser is an idempotent io.Closer
    16  //   - implements [io.Closer]
    17  type ContextCloser struct {
    18  	closer   io.Closer
    19  	isClosed atomic.Bool
    20  }
    21  
    22  // NewContextCloser returns a an idempotent [io.Closer]
    23  //   - closer may be nil
    24  //   - panic-free idempotent observable
    25  func NewContextCloser(closer io.Closer) (contextCloser *ContextCloser) {
    26  	return &ContextCloser{closer: closer}
    27  }
    28  
    29  // Close closes the io.Closer
    30  //   - if Close has already been invoked, noop, no error
    31  //   - if io.Closer is nil, noop, no error
    32  //   - panic-free idempotent
    33  func (c *ContextCloser) Close() (err error) {
    34  	if c.isClosed.Load() {
    35  		return // Close already invoked
    36  	} else if !c.isClosed.CompareAndSwap(false, true) {
    37  		return // another thread already closed
    38  	} else if c.closer == nil {
    39  		return // closer is nil
    40  	}
    41  	parl.Close(c.closer, &err)
    42  	return
    43  }
    44  
    45  // IsCloseable indicates whether an [io.Closer] is present that can be closed
    46  func (c *ContextCloser) IsCloseable() (isCloseable bool) {
    47  	return c.closer != nil
    48  }