waitgroup

package module
Version: v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Dec 1, 2020 License: Apache-2.0 Imports: 3 Imported by: 0

README

WaitGroup

GoDoc Go ReportCard

A simple wrapper around sync.WaitGroup with support for specifying a timeout to return in bounded time, e.g. during shutdown.

For convenience two additional functions are provided in case existing sync.WaitGroup or errgroup.Group should be used directly instead of wrapping.

Why would I want this?

In case you use sync.WaitGroup ("wg") to manage goroutines you might run into an issue where wg.Wait() could block very long, err... forever, if one or more goroutines do not finish their work in time/livelock, e.g. due to a missing wg.Done(). ¯\_(ツ)_/¯

How to use

Example with waitgroup.WaitGroup{}

package main

import (
	"fmt"
	"os"
	"time"

	"github.com/embano1/waitgroup"
)

func main() {
	var wg waitgroup.WaitGroup

	wg.Add(1)
	go func() {
		defer wg.Done()
		fmt.Println("I'm slow")
		time.Sleep(time.Second * 5)
	}()

	if err := wg.WaitTimeout(time.Second); err != nil {
		fmt.Printf("an error occurred: %v\n", err)
		os.Exit(1)
	}
}

Run this program:

go run cmd/main.go
I'm slow
an error occurred: timed out
exit status 1

Example with waitgroup.Await()

package main

import (
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/embano1/waitgroup"
)

func main() {
	var wg sync.WaitGroup

	wg.Add(1)
	go func() {
		defer wg.Done()
		time.Sleep(time.Second * 5)
	}()

	if err := waitgroup.Await(&wg, time.Second); err != nil {
		fmt.Printf("an error occurred: %v\n", err)
		os.Exit(1)
	}
}

Run this program:

go run cmd/main.go
I'm slow
an error occurred: timed out
exit status 1

See GoDoc for more API details and more examples.

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrTimeout = errors.New("timed out")

ErrTimeout is returned when the timeout in WaitTimeout is exceeded

Functions

func Await

func Await(wf Waiter, timeout time.Duration) error

Await is a convenience function that can be used instead of WaitGroup provided by this package. Await blocks until Waiter returns or the specified timeout is exceeded. In case of timeout exceeded the error ErrTimeout is returned and the internally spawned goroutine might leak if Waiter never returns.

It is safe to call Await concurrently and multiple times but doing so might leak additional goroutines as described above.

Example
package main

import (
	"fmt"
	"sync"
	"time"

	"github.com/embano1/waitgroup"
)

func main() {
	var wg sync.WaitGroup

	wg.Add(1)
	go func() {
		defer wg.Done()
		// long running computation
		time.Sleep(time.Second)
	}()

	err := waitgroup.Await(&wg, time.Millisecond*100)
	if err != nil {
		fmt.Println(err.Error())
	}
}
Output:

timed out

func AwaitWithError

func AwaitWithError(we WaitErrorer, timeout time.Duration) error

AwaitWithError is a convenience function that can be used instead of WaitGroup provided by this package. AwaitWithError blocks until WaitErrorer returns or the specified timeout is exceeded. Any error from WaitErrorer will be returned unless the timeout has been exceeded before. In case of timeout exceeded the error ErrTimeout is returned and the internally spawned goroutine might leak if WaitErrorer never returns.

It is safe to call AwaitWithError concurrently and multiple times but doing so might leak additional goroutines as described above.

Example (Error)
package main

import (
	"errors"
	"fmt"
	"time"

	"github.com/embano1/waitgroup"
	"golang.org/x/sync/errgroup"
)

func main() {
	var eg errgroup.Group

	eg.Go(func() error {
		return errors.New("did not work")
	})

	err := waitgroup.AwaitWithError(&eg, time.Millisecond*100)
	if err != nil {
		fmt.Println(err.Error())
	}
}
Output:

did not work
Example (Timeout)
package main

import (
	"fmt"
	"time"

	"github.com/embano1/waitgroup"
	"golang.org/x/sync/errgroup"
)

func main() {
	var eg errgroup.Group

	eg.Go(func() error {
		// long running computation
		time.Sleep(time.Second)
		return nil
	})

	err := waitgroup.AwaitWithError(&eg, time.Millisecond*100)
	if err != nil {
		fmt.Println(err.Error())
	}
}
Output:

timed out

Types

type WaitErrorer

type WaitErrorer interface {
	Wait() error
}

WaitErrorer is the interface blocking on Wait() and returning any error that occurred from Wait(). errgroup.Group implements this interface.

type WaitGroup

type WaitGroup struct {
	sync.WaitGroup
}

WaitGroup wraps sync.WaitGroup and adds a method WaitTimeout to abort waiting for long-running, blocked or leaked goroutines blocking Wait() from the underlying WaitGroup. A caller might use this functionality to terminate a program in a bounded time.

func (*WaitGroup) WaitTimeout

func (wg *WaitGroup) WaitTimeout(timeout time.Duration) error

WaitTimeout blocks until the WaitGroup counter is zero or when timeout is exceeded. It spawns an internal goroutine. In case of timeout exceeded the error ErrTimeout is returned and the internally spawned goroutine might leak if the underlying WaitGroup never returns.

It is safe to call WaitTimeout concurrently but doing so might leak additional goroutines as described above.

Example
package main

import (
	"fmt"
	"time"

	"github.com/embano1/waitgroup"
)

func main() {
	var wg waitgroup.WaitGroup

	wg.Add(1)
	go func() {
		defer wg.Done()
		// long running computation
		time.Sleep(time.Second)
	}()

	err := wg.WaitTimeout(time.Millisecond * 100)
	if err != nil {
		fmt.Println(err.Error())
	}
}
Output:

timed out

type Waiter

type Waiter interface {
	Wait()
}

Waiter is the interface blocking on Wait(). sync.WaitGroup implements this interface.

Source Files

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
t or T : Toggle theme light dark auto