donegroup

package module
v1.5.1 Latest Latest
Warning

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

Go to latest
Published: Apr 4, 2024 License: MIT Imports: 5 Imported by: 2

README

donegroup Go Reference CI Coverage Code to Test Ratio Test Execution Time

donegroup is a package that provides a graceful cleanup transaction to context.Context when the context is canceled ( done ).

errgroup.Group + <-ctx.Done() = donegroup

Usage

Use donegroup.WithCancel instead of context.WithCancel.

Then, it can wait for the cleanup processes associated with the context using donegroup.Wait.

// before
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// after
ctx, cancel := donegroup.WithCancel(context.Background())
defer func() {
	cancel()
	if err := donegroup.Wait(ctx); err != nil {
		log.Fatal(err)
	}
}()
Basic usage ( donegroup.Cleanup )
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/k1LoW/donegroup"
)

func main() {
	ctx, cancel := donegroup.WithCancel(context.Background())

	// Cleanup process 1 of some kind
	if err := donegroup.Cleanup(ctx, func(_ context.Context) error {
		fmt.Println("cleanup 1")
		return nil
	}); err != nil {
		log.Fatal(err)
	}

	// Cleanup process 2 of some kind
	if err := donegroup.Cleanup(ctx, func(_ context.Context) error {
		time.Sleep(1 * time.Second)
		fmt.Println("cleanup 2")
		return nil
	}); err != nil {
		log.Fatal(err)
	}

	defer func() {
		cancel()
		if err := donegroup.Wait(ctx); err != nil {
			log.Fatal(err)
		}
	}()

	// Main process of some kind
	fmt.Println("main")

	// Output:
	// main finish
	// cleanup 1
	// cleanup 2
}

dongroup.Cleanup is similar in usage to testing.T.Cleanup, but the order of execution is not guaranteed.

Wait for a specified duration ( donegroup.WaitWithTimeout )

Using donegroup.WaitWithTimeout, it is possible to set a timeout for the cleanup processes.

Note that each cleanup process must handle its own context argument.

ctx, cancel := WithCancel(context.Background())

// Cleanup process of some kind
if err := Cleanup(ctx, func(ctx context.Context) error {
	fmt.Println("cleanup start")
	for i := 0; i < 10; i++ {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
			time.Sleep(2 * time.Millisecond)
		}
	}
	fmt.Println("cleanup finish")
	return nil
}); err != nil {
	log.Fatal(err)
}

defer func() {
	cancel()
	timeout := 5 * time.Millisecond
	if err := WaitWithTimeout(ctx, timeout); err != nil {
		fmt.Println(err)
	}
}()

// Main process of some kind
fmt.Println("main start")

fmt.Println("main finish")

// Output:
// main start
// main finish
// cleanup start
// context deadline exceeded
donegroup.Awaiter

In addition to using donegroup.Cleanup to register a cleanup function after context cancellation, it is possible to use donegroup.Awaiter to make the execution of an arbitrary process wait.

ctx, cancel := donegroup.WithCancel(context.Background())

go func() {
	completed, err := donegroup.Awaiter(ctx)
	if err != nil {
		log.Fatal(err)
		return
	}
	time.Sleep(1000 * time.Millisecond)
	fmt.Println("do something")
	completed()
}()

// Main process of some kind
fmt.Println("main")
time.Sleep(10 * time.Millisecond)

cancel()
if err := donegroup.Wait(ctx); err != nil {
	log.Fatal(err)
}

fmt.Println("finish")

// Output:
// main
// do something
// finish

It is also possible to guarantee the execution of a function block using defer donegroup.Awaitable(ctx)().

go func() {
	defer donegroup.Awaitable(ctx)()
	time.Sleep(1000 * time.Millisecond)
	fmt.Println("do something")
	completed()
}()
donegroup.Go

donegroup.Go can execute arbitrary process asynchronously while still waiting for it to finish, similar to donegroup.Awaiter.

donegroup.Go(func() error {
	time.Sleep(1000 * time.Millisecond)
	fmt.Println("do something")
	return nil
}()
Syntax sugar for cancel() and donegroup.Wait ( donegroup.Cancel )

If cancel() and donegroup.Wait are to be executed at the same time, donegroup.Cancel can be used.

ctx, cancel := donegroup.WithCancel(context.Background())
defer func() {
	cancel()
	if err := donegroup.Wait(ctx); err != nil {
		log.Fatal(err)
	}
}()

and

ctx, _ := donegroup.WithCancel(context.Background())
defer func() {
	if err := donegroup.Cancel(ctx); err != nil {
		log.Fatal(err)
	}
}()

are equivalent.

Documentation

Overview

Example
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/k1LoW/donegroup"
)

func main() {
	ctx, cancel := donegroup.WithCancel(context.Background())

	// Cleanup process of some kind
	if err := donegroup.Cleanup(ctx, func(_ context.Context) error {
		time.Sleep(10 * time.Millisecond)
		fmt.Println("cleanup with sleep")
		return nil
	}); err != nil {
		log.Fatal(err)
	}

	// Cleanup process of some kind
	if err := donegroup.Cleanup(ctx, func(_ context.Context) error {
		fmt.Println("cleanup")
		return nil
	}); err != nil {
		log.Fatal(err)
	}

	defer func() {
		cancel()

		if err := donegroup.Wait(ctx); err != nil {
			log.Fatal(err)
		}
	}()

	// Main process of some kind
	fmt.Println("main start")

	fmt.Println("main finish")

}
Output:

main start
main finish
cleanup
cleanup with sleep

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrNotContainDoneGroup = errors.New("donegroup: context does not contain a doneGroup. Use donegroup.WithCancel to create a context with a doneGroup")

Functions

func Awaitable added in v1.3.0

func Awaitable(ctx context.Context) (completed func())

Awaitable returns a function that guarantees execution of the process until it is called. Note that if the timeout of WaitWithTimeout has passed (or the context of WaitWithContext has canceled), it will not wait.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/k1LoW/donegroup"
)

func main() {
	ctx, cancel := donegroup.WithCancel(context.Background())

	go func() {
		defer donegroup.Awaitable(ctx)()
		for {
			select {
			case <-ctx.Done():
				time.Sleep(100 * time.Millisecond)
				fmt.Println("cleanup")
				return
			case <-time.After(10 * time.Millisecond):
				fmt.Println("do something")
			}
		}
	}()

	// Main process of some kind
	fmt.Println("main")
	time.Sleep(35 * time.Millisecond)

	cancel()
	if err := donegroup.Wait(ctx); err != nil {
		log.Fatal(err)
	}

}
Output:

main
do something
do something
do something
cleanup

func AwaitableWithKey added in v1.3.0

func AwaitableWithKey(ctx context.Context, key any) (completed func())

AwaitableWithKey returns a function that guarantees execution of the process until it is called. Note that if the timeout of WaitWithTimeout has passed (or the context of WaitWithContext has canceled), it will not wait.

func Awaiter added in v1.1.0

func Awaiter(ctx context.Context) (completed func(), err error)

Awaiter returns a function that guarantees execution of the process until it is called. Note that if the timeout of WaitWithTimeout has passed (or the context of WaitWithContext has canceled), it will not wait.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/k1LoW/donegroup"
)

func main() {
	ctx, cancel := donegroup.WithCancel(context.Background())

	go func() {
		completed, err := donegroup.Awaiter(ctx)
		if err != nil {
			log.Fatal(err)
			return
		}
		<-ctx.Done()
		time.Sleep(100 * time.Millisecond)
		fmt.Println("do something")
		completed()
	}()

	// Main process of some kind
	fmt.Println("main")
	time.Sleep(10 * time.Millisecond)

	cancel()
	if err := donegroup.Wait(ctx); err != nil {
		log.Fatal(err)
	}

	fmt.Println("finish")

}
Output:

main
do something
finish

func AwaiterWithKey added in v1.1.0

func AwaiterWithKey(ctx context.Context, key any) (completed func(), err error)

AwaiterWithKey returns a function that guarantees execution of the process until it is called. Note that if the timeout of WaitWithTimeout has passed (or the context of WaitWithContext has canceled), it will not wait.

func Cancel added in v1.2.0

func Cancel(ctx context.Context) error

Cancel cancels the context. Then calls the function registered by Cleanup.

func CancelWithContext added in v1.2.0

func CancelWithContext(ctx, ctxw context.Context) error

CancelWithContext cancels the context. Then calls the function registered by Cleanup with context (ctxw).

func CancelWithContextAndKey added in v1.2.0

func CancelWithContextAndKey(ctx, ctxw context.Context, key any) error

CancelWithContextAndKey cancels the context. Then calls the function registered by Cleanup with context (ctxw).

func CancelWithKey added in v1.2.0

func CancelWithKey(ctx context.Context, key any) error

CancelWithKey cancels the context. Then calls the function registered by Cleanup.

func CancelWithTimeout added in v1.2.0

func CancelWithTimeout(ctx context.Context, timeout time.Duration) error

CancelWithTimeout cancels the context. Then calls the function registered by Cleanup with timeout.

func CancelWithTimeoutAndKey added in v1.2.0

func CancelWithTimeoutAndKey(ctx context.Context, timeout time.Duration, key any) error

CancelWithTimeoutAndKey cancels the context. Then calls the function registered by Cleanup with timeout.

func Cleanup added in v0.2.3

func Cleanup(ctx context.Context, f func(ctx context.Context) error) error

Cleanup registers a function to be called when the context is canceled.

func CleanupWithKey added in v0.2.3

func CleanupWithKey(ctx context.Context, key any, f func(ctx context.Context) error) error

CleanupWithKey Cleanup registers a function to be called when the context is canceled.

func Go added in v1.5.0

func Go(ctx context.Context, f func() error)

Go calls the function now asynchronously. If an error occurs, it is stored in the doneGroup. Note that if the timeout of WaitWithTimeout has passed (or the context of WaitWithContext has canceled), it will not wait.

func GoWithKey added in v1.5.0

func GoWithKey(ctx context.Context, key any, f func() error)

GoWithKey calls the function now asynchronously. If an error occurs, it is stored in the doneGroup. Note that if the timeout of WaitWithTimeout has passed (or the context of WaitWithContext has canceled), it will not wait.

func Wait

func Wait(ctx context.Context) error

Wait blocks until the context is canceled. Then calls the function registered by Cleanup.

func WaitWithContext

func WaitWithContext(ctx, ctxw context.Context) error

WaitWithContext blocks until the context (ctx) is canceled. Then calls the function registered by Cleanup with context (ctxw).

func WaitWithContextAndKey

func WaitWithContextAndKey(ctx, ctxw context.Context, key any) error

WaitWithContextAndKey blocks until the context is canceled. Then calls the function registered by Cleanup with context (ctxx).

func WaitWithKey

func WaitWithKey(ctx context.Context, key any) error

WaitWithKey blocks until the context is canceled. Then calls the function registered by Cleanup.

func WaitWithTimeout added in v0.2.2

func WaitWithTimeout(ctx context.Context, timeout time.Duration) error

WaitWithTimeout blocks until the context (ctx) is canceled. Then calls the function registered by Cleanup with timeout.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/k1LoW/donegroup"
)

func main() {
	ctx, cancel := donegroup.WithCancel(context.Background())

	// Cleanup process of some kind
	if err := donegroup.Cleanup(ctx, func(ctx context.Context) error {
		fmt.Println("cleanup start")
		for i := 0; i < 10; i++ {
			select {
			case <-ctx.Done():
				return ctx.Err()
			default:
				time.Sleep(2 * time.Millisecond)
			}
		}
		fmt.Println("cleanup finish")
		return nil
	}); err != nil {
		log.Fatal(err)
	}

	defer func() {
		cancel()
		timeout := 5 * time.Millisecond
		if err := donegroup.WaitWithTimeout(ctx, timeout); err != nil {
			fmt.Println(err)
		}
	}()

	// Main process of some kind
	fmt.Println("main start")

	fmt.Println("main finish")

}
Output:

main start
main finish
cleanup start
context deadline exceeded

func WaitWithTimeoutAndKey added in v0.2.2

func WaitWithTimeoutAndKey(ctx context.Context, timeout time.Duration, key any) error

WaitWithTimeoutAndKey blocks until the context is canceled. Then calls the function registered by Cleanup with timeout.

func WithCancel

func WithCancel(ctx context.Context) (context.Context, context.CancelFunc)

WithCancel returns a copy of parent with a new Done channel and a doneGroup.

func WithCancelWithKey

func WithCancelWithKey(ctx context.Context, key any) (context.Context, context.CancelFunc)

WithCancelWithKey returns a copy of parent with a new Done channel and a doneGroup.

Types

This section is empty.

Jump to

Keyboard shortcuts

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