Skip to content

Instantly share code, notes, and snippets.

@theruziev
Last active January 7, 2024 21:12
Show Gist options
  • Save theruziev/7c3f614f538189aef1a8f641ed485e76 to your computer and use it in GitHub Desktop.
Save theruziev/7c3f614f538189aef1a8f641ed485e76 to your computer and use it in GitHub Desktop.
Closer in go. I wrote a blog post about this code snippet. https://theruziev.com/blog/post/closer-go
package closer
import (
"context"
"errors"
"sync"
)
type closer func(ctx context.Context) error
// Closer is a helper to close multiple closers.
type Closer struct {
closers []closer
mu sync.Mutex
isDone chan struct{}
isClosed bool
once sync.Once
}
// NewCloser creates a new Closer.
func NewCloser() *Closer {
return &Closer{
closers: make([]closer, 0),
isDone: make(chan struct{}),
isClosed: false,
}
}
// Add adds a closer to the Closer.
func (c *Closer) Add(cl closer) {
c.mu.Lock()
defer c.mu.Unlock()
if c.isClosed {
return
}
c.closers = append(c.closers, cl)
}
// Done returns a channel that's closed when Close is called.
func (c *Closer) Done() chan struct{} {
return c.isDone
}
// Close closes all closers.
func (c *Closer) Close(ctx context.Context) error {
c.mu.Lock()
defer c.mu.Unlock()
if c.isClosed {
return nil
}
c.isClosed = true
defer func() {
c.once.Do(func() {
close(c.isDone)
})
}()
var resultErr []error
for i := len(c.closers) - 1; i >= 0; i-- {
fn := c.closers[i]
if err := fn(ctx); err != nil {
resultErr = append(resultErr, err)
}
}
return errors.Join(resultErr...)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment