scope

package module
v0.0.0-...-1e230fe Latest Latest
Warning

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

Go to latest
Published: Mar 14, 2021 License: MIT Imports: 3 Imported by: 1

README

scope

A small library with context helpers for golang.

Consists of 2 main components:

  • Closer
  • Group

Closer

Closer tries to loosely fill in gap between new context-aware world and old world of context-less network primitives with blocking operations, whose unblocking and cancellation logic is entirely different. Cancelling such blocking operations is often achieved by Close()-ing corresponding primitives in another goroutine. ContextCloser runs separate goroutine, which closes all io.Closer-s passed to Closer constructor func, when its bind context's Done() channel is closed. This separate goroutine is finished either after context is expired and io.Closer-s are closed or when Cancel() or Close() method of Closer is called, if it happens earlier. Closer must be constructed with scope.Closer or scope.CloserWithErrorGroup func.

To finish Closer's goroutine before context is expired call either Close() or Cancel(), but not both! Close() closes all the io.Closers passed to ContextCloser constructor func regardless of context and finishes goroutine. Cancel() just finishes goroutine and doesn't close anything. Both Close() and Cancel() close inner channel without any check, so calling them both or calling any of them more than once leads to panic! Nevertheless, it is perfectly safe to call either of them once regardless of whether context was already expired or not.

It is convenient to construct Closer and call either Close() or Cancel() at once in defer statement. If io.Closer (e.g. a net.Conn) should be bound not only to context, but also to current func scope, then use Close():

defer scope.Closer(ctx, conn).Close()

If io.Closer (e.g. a net.Conn) should be bound to context only while current goroutine is in the current func (rarer case, that might break structured concurrency principle), then use Cancel():

defer scope.Closer(ctx, conn).Cancel()

Group

Group is basically a convenient wrapper for errgroup.Group and optional Closer-s. Group must be constructed with scope.Group func. Any number of Closer-s can be added to it using Add[After]Closer[Cancelling]() functions.

Another way to look at Group is as at context.Context with its errgroup.Group, where new child goroutines could be run in structured way.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ContextCloser

type ContextCloser struct {
	// contains filtered or unexported fields
}

ContextCloser tries to loosely fill in gap between new context-aware world and old world of context-less network primitives with blocking operations, whose unblocking and cancellation logic is entirely different. Cancelling such blocking operations is often achieved by Close()-ing corresponding primitives in another goroutine. ContextCloser runs separate goroutine, which closes all io.Closer-s passed to ContextCloser constructor func, when its bind context's Done() channel is closed. This separate goroutine is finished either after context is expired and io.Closer-s are closed or when ContextCloser.Cancel() or ContextCloser.Close() is called, if it happens earlier. ContextCloser must be constructed with scope.Closer or scope.CloserWithErrorGroup func.

To finish ContextCloser's goroutine before context is expired call either Close() or Cancel(), but not both! Close() closes all the io.Closer passed to ContextCloser constructor func regardless of context and finishes goroutine. Cancel() just finishes goroutine and doesn't close anything. Both Close() and Cancel() close inner channel without any check, so calling them both or calling any of them more then once leads to panic! Nevertheless, it is perfectly safe to call either of them once regardless of whether context was already expired or not.

It is convenient to construct ContextCloser and call either Close() or Cancel() at once in defer statement. If io.Closer (e.g. a net.Conn) should be bound not only to context, but also to current func scope, then use Close():

defer scope.Closer(ctx, conn).Close()

If io.Closer (e.g. a net.Conn) should be bound to context only while current goroutine is in the current func (rarer case, that might break structured concurrency principle), then use Cancel():

defer scope.Closer(ctx, conn).Cancel()

func Closer

func Closer(ctx context.Context, closers ...io.Closer) *ContextCloser

Creates new ContextCloser, that binds all the given closers to given ctx with separate goroutine.

func CloserWithErrorGroup

func CloserWithErrorGroup(ctx context.Context, g *errgroup.Group, closer ...io.Closer) *ContextCloser

Creates new ContextCloser, that binds all the given closers to given ctx with new goroutine. New goroutine is added to given errgroup g. Note, that given errgroup g will be able to finish only if either given ctx will expire or some other goroutine, added to the errgroup will finish with error, or produced ContextCloser will be cancelled or closed explicitly, because ContextCloser's goroutine will be blocked forever in other cases. So, use with caution!

func (*ContextCloser) Cancel

func (ac *ContextCloser) Cancel()

Cancels ContextCloser's binding and finishes it, if ctx.Done() channel was not closed yet (if it was, then does nothing). Panics, if called more then once, or called after Close().

func (*ContextCloser) Close

func (ac *ContextCloser) Close()

Closes all io.Closer-s of ContextCloser and finishes its goroutine, if ctx.Done() channel was not closed yet (if it was, then does nothing). Panics, if called more then once, or called after Cancel()

type ContextGroup

type ContextGroup struct {
	// contains filtered or unexported fields
}

Another way to look at ContextGroup is as at context.Context with its errgroup.Group, where new child goroutines could be run in structured way.

func Group

Creates ContextGroup, wrapping new errgroup.Group

func (*ContextGroup) AddAfterCloser

func (g *ContextGroup) AddAfterCloser(closers ...io.Closer) *ContextGroup

Adds new ContextCloser, that binds all given closers to group context. ContextCloser will be automatically closed when any Wait*() func is called AFTER waiting for inner errgroup.

func (*ContextGroup) AddAfterCloserCancelling

func (g *ContextGroup) AddAfterCloserCancelling(closers ...io.Closer) *ContextGroup

Adds new ContextCloser, that binds all given closers to group context. ContextCloser will be automatically cancelled when any Wait*() func is called AFTER waiting for inner errgroup.

func (*ContextGroup) AddCloser

func (g *ContextGroup) AddCloser(closers ...io.Closer) *ContextGroup

Adds new ContextCloser, that binds all given closers to group context. ContextCloser will be automatically closed when any Wait*() func is called BEFORE waiting for inner errgroup.

func (*ContextGroup) AddCloserCancelling

func (g *ContextGroup) AddCloserCancelling(closers ...io.Closer) *ContextGroup

Adds new ContextCloser, that binds all given closers to group context. ContextCloser will be automatically cancelled when any Wait*() func is called BEFORE waiting for inner errgroup.

func (*ContextGroup) Cancel

func (g *ContextGroup) Cancel()

Cancels group's context

func (*ContextGroup) Ctx

func (g *ContextGroup) Ctx() context.Context

Returns group's context

func (*ContextGroup) Go

func (g *ContextGroup) Go(f func() error)

Adds new goroutine to the group. Simply calls Go() func of inner errgroup

func (*ContextGroup) GoNoError

func (g *ContextGroup) GoNoError(f func())

Adds new goroutine, that can't return error to the group. Convenient wrapper for returning nil after f to fulfill errgroup's requirements

func (*ContextGroup) Wait

func (g *ContextGroup) Wait() error

Adds new goroutine, that can't return error to the group. Convenient wrapper for returning nil after f to fulfill errgroup's requirements.

func (*ContextGroup) WaitAndSetErrorIfNotYet

func (g *ContextGroup) WaitAndSetErrorIfNotYet(err *error)

Wait()-s, and sets resulting error to its argument if it points to nil or ignores resulting error otherwise. Convenient wrapper for usage in defer statements and setting func result error (as named return value), if it wasn't already set earlier, e.g. with common return statement, or with assignment in func code

func (*ContextGroup) WaitQuietly

func (g *ContextGroup) WaitQuietly()

Wait()-s, ignoring any errors. Convenient wrapper for usage in defer statements and avoid linter warnings on unhandled error

Jump to

Keyboard shortcuts

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