closer

package module
v4.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 27, 2026 License: MIT Imports: 8 Imported by: 1

README

Closer - A simple, thread-safe closer

GoDoc Go Report Card coverage license

This package aims to provide a simple and performance oriented mechanism to manage the graceful and reliable shutdown of an application, or parts of it.

It can also be a handy alternative to the context package, though it does not solve the problem that common go libraries only accept context as a valid cancellation method. Therefore, you are only able to cancel "in-between" slow operations.

go get github.com/desertbit/closer/v4
Examples

Check out the sample program for a good overview of this package's functionality.

Closing

Let us assume you want a server that should close its connection once it gets closed. We close the connection in the onClose() method of the server's closer and demonstrate that it does not matter how often you call Close(), the connection is closed exactly once.

type Server struct {
    closer.Closer // Embedded
    conn net.Conn
}

func New() *Server {
    // ...
    s := &Server {
        Closer: closer.New(),
        conn: conn,
    }
    closer.Hook(s, func(h closer.H) {
        h.OnCloseWithErr(s.onClose)
    })
    return s
}

func (s *server) onClose() error {
    return s.conn.Close()
}

func main() {
    s := New()
    // ...

    // The s.onClose function will be called only once.
    s.Close()
    s.Close()
}
OneWay

Now we want an application that (among other things) connects as a client to a remote server. In case the connection is interrupted, the app should continue to run and not fail. But if the app itself closes, of course we want to take down the client connection as well.

type App struct {
    closer.Closer
}

func NewApp() *App {
    return &App{
        Closer: closer.New()
    }
}

type Client struct {
    closer.Closer
    conn net.Conn
}

func NewClient(cl closer.Closer, conn net.Conn) (c *Client) {
    closer.Hook(s, func(h closer.H) {
        c = &Client{
            Closer: cl,
            conn: conn,
        }
        h.OnCloseWithErr(func() error {
            return c.conn.Close()
        })
    })
    return c
}

func main() {
    a := NewApp()
    // Close c, when a closes, but do not close a, when c closes.
    c := NewClient(closer.OneWay(a))
    
    c.Close()
    // App still alive.
}
TwoWay

Of course, there is the opposite to the OneWay closer that closes its parent as well. If we take the example from before, we can simply exchange the closer that is passed to the client.

//...

func main() {
    a := NewApp()
    // Close c, when a closes, and close a, when c closes.
    c := NewClient(closer.TwoWay(a))
    
    c.Close()
    // App has been closed.
}

Documentation

Overview

Package closer offers a simple, thread-safe closer.

It allows to build up a tree of closing relationships, where you typically start with a root closer that branches into different children and children's children. When a parent closer spawns a child closer, the child either has a one-way or two-way connection to its parent. One-way children are closed when their parent closes. In addition, two-way children also close their parent, if they are closed themselves.

A closer is also useful to ensure that certain dependencies, such as network connections, are reliably taken down, once the closer closes. In addition, a closer can be concurrently closed many times, without closing more than once, but still returning the errors to every caller.

This allows to represent complex closing relationships and helps avoiding leaking goroutines, gracefully shutting down, etc.

Index

Constants

This section is empty.

Variables

View Source
var ErrClosed = errors.New("closed")

ErrClosed is a generic error that indicates a resource has been closed.

Functions

func Block

func Block(cl Closer, f func() error) error

Block ensures that during the function execution, the closer will not reach the closed state. This is handled with an internal wait group. This call will return ErrClosed, if the closer is already closed. This method can be used to free C memory during OnClose and ensures, that pointers are not used after beeing freed.

func CloseOnInterrupt added in v4.0.2

func CloseOnInterrupt(cl Closer)

CloseOnInterrupt closes the closer when an interrupt or termination signal (SIGINT, SIGTERM) is received. This method does not block.

func Context

func Context(cl Closer) context.Context

Context returns a context.Context, which is done as soon as the closer is closing. The retuned error will be ErrClosed.

func ContextWithCancel

func ContextWithCancel(cl Closer) (context.Context, context.CancelFunc)

Context returns a context.Context, which is cancelled as soon as the closer is closing. The returned cancel func should be called as soon as the context is no longer needed, to free resources.

func Hook

func Hook(cl Closer, f func(H))

Hook can be used to register closer hooks. Do not use the passed hook H after the function goes out-of-scope. Hook gaurenetees, that the newly registered hooks within this context will be called after the function scope. This also applies, if the closer is already closed. Use this call to register all hooks within a constructor function.

func HookWithErr

func HookWithErr(cl Closer, f func(H) error) error

HookWithErr same as Hook, but returns the inner error. If the closer is already closed, then the inner function will not run and ErrClosed will be returned.

func OnContextDoneClose

func OnContextDoneClose(ctx context.Context, cl Closer)

OnContextDoneClose closes the closer if the context is done.

func Routine

func Routine(cl Closer, f func() error)

Routine starts a closer goroutine: - increment the internal wait group. - start a new goroutine - wait for the routine function to return - handle the error, decrement the wait group and close the closer

func RoutineWithCloser

func RoutineWithCloser(cl Closer, f func(Closer) error)

func Wait

func Wait(cl Closer, ctx context.Context) error

Wait waits for the closer to close and returns the CloserError if present. Use the context to cancel the blocking wait.

func WaitChan

func WaitChan(cl Closer, ctx context.Context) <-chan error

WaitChan extends Wait by returning an error channel. If the context is canceled, the context error will be send over the channel.

Types

type Closer

type Closer interface {
	// Close closes this closer in a thread-safe manner.
	//
	// Implements the io.Closer interface.
	//
	// This method always returns the close error,
	// regardless of how often it gets called.
	//
	// The closing order looks like this:
	// 1: the closing chan is closed.
	// 2: the OnClosing funcs are executed.
	// 3: each of the closer's children is closed.
	// 4: it waits for the wait group.
	// 5: the OnClose funcs are executed.
	// 6: the closed chan is closed.
	// 7: the parent is closed, if it has one.
	//
	// Close blocks, until step 6 of the closing order
	// has been finished. A potential parent gets
	// closed concurrently in a new goroutine.
	//
	// The returned error contains the joined errors of all closers that were part of
	// the blocking closing order of this closer.
	// This means that two-way closers do not report their parents' errors.
	Close() error

	// CloseErr returns the joined error of this closer once it has fully closed.
	// If there was no error or the closer is not yet closed, nil is returned.
	CloseErr() error

	// AsyncClose closes the this closer without blocking.
	// Use this within Block() calls or Routines().
	AsyncClose()

	// AsyncCloseWithErr appends the given error to its joined error and calls AsyncClose.
	// If the closer is already closed, the error will be ignored. During the closing state errors can be appended.
	AsyncCloseWithErr(error)

	// ClosingChan returns a channel, which is closed as
	// soon as the closer is about to close.
	// Remains closed, once ClosedChan() has also been closed.
	// See Close() for the position in the closing order.
	ClosingChan() <-chan struct{}

	// ClosedChan returns a channel, which is closed as
	// soon as the closer is completely closed.
	// See Close() for the position in the closing order.
	ClosedChan() <-chan struct{}

	// IsClosing returns a boolean indicating
	// whether this instance is about to close.
	// Also returns true, if IsClosed() returns true.
	IsClosing() bool

	// IsClosed returns a boolean indicating
	// whether this instance has been closed completely.
	IsClosed() bool
}

A Closer is a thread-safe helper for common close actions.

func New

func New() Closer

New creates a new closer without any parent relation.

func OneWay

func OneWay(p Closer) Closer

OneWay creates a new child closer that has a one-way relationship with the parent closer. This means that the child is closed whenever the parent closes, but not vice versa. See Close() for the position in the closing order.

func TwoWay

func TwoWay(p Closer) Closer

TwoWay creates a new child closer that has a two-way relationship with the parent closer. This means that the child is closed whenever the parent closes and vice versa. See Close() for the position in the closing order.

type H

type H interface {
	// OnClose same as OnCloseWithErr but without error.
	OnClose(f ...func())

	// OnClosing same as OnClosingWithErr but without error.
	OnClosing(f ...func())

	// OnCloseWithErr adds the given functions to the closer.
	// Their errors are joined with the closer's other errors.
	// Close functions are called in LIFO order.
	// See Close() for their position in the closing order.
	OnCloseWithErr(f ...func() error)

	// OnClosingWithErr adds the given functions to the closer.
	// Their errors are joined with the closer's other errors.
	// Closing functions are called in LIFO order.
	// It is guaranteed that all closing funcs within this hook scope are executed before
	// any close funcs.
	// See Close() for their position in the closing order.
	OnClosingWithErr(f ...func() error)
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL