goro

package
v1.19.0 Latest Latest
Warning

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

Go to latest
Published: Dec 1, 2022 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package goro provides utilities for spawning and subsequently managing the liftime(s) of one or more goroutines. This package relies heavily on the context package to provide consistent cancellation semantics for long-lived goroutines. The goal of this package is to provide a unified way to cancel and wait on running goroutines as is often seen in "service" or "daemon" types with Start()/Stop() lifecycle functions and to unify the multiplicity of approaches that have been adopted over time.

Note: If you're looking for a short-lived (e.g.  request-scoped) group of
transient goroutines, you probably want `errgroup.Group` from
https://golang.org/x/sync/errgroup.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Group added in v1.13.0

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

Group manages a set of long-running goroutines. Goroutines are spawned individually with Group.Go and after that interrupted and waited-on as a single unit. The expected use-case for this type is as a member field of a `common.Daemon` (or similar) type that spawns one or more goroutines in its Start() function and then stops those same goroutines in its Stop() function. The zero-value of this type is valid. A Group must not be copied after first use.

Example
// The MIT License
//
// Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
//
// Copyright (c) 2020 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package main

import (
	"context"
	"fmt"
	"time"

	"go.temporal.io/server/internal/goro"
)

type ExampleService struct {
	gorogrp goro.Group // goroutines managed in here
}

func (svc *ExampleService) Start() {
	// launch two background goroutines
	svc.gorogrp.Go(svc.backgroundLoop1)
	svc.gorogrp.Go(svc.backgroundLoop2)
}

func (svc *ExampleService) Stop() {
	// stop all goroutines in the goro.Group
	svc.gorogrp.Cancel() // interrupt the background goroutines
	svc.gorogrp.Wait()   // wait for the background goroutines to finish
}

func (svc *ExampleService) backgroundLoop1(ctx context.Context) error {
	fmt.Println("starting backgroundLoop1")
	defer fmt.Println("stopping backgroundLoop1")
	for {
		select {
		case <-time.After(1 * time.Minute):
			// do something every minute
		case <-ctx.Done():
			return nil
		}
	}
}

func (svc *ExampleService) backgroundLoop2(ctx context.Context) error {
	fmt.Println("starting backgroundLoop2")
	defer fmt.Println("stopping backgroundLoop2")
	for {
		select {
		case <-time.After(10 * time.Second):
			// do something every 10 seconds
		case <-ctx.Done():
			return nil
		}
	}
}

func main() {
	var svc ExampleService
	svc.Start()
	svc.Stop()

	// it is safe to call svc.Stop() multiple times
	svc.Stop()
	svc.Stop()
	svc.Stop()

}
Output:

starting backgroundLoop1
starting backgroundLoop2
stopping backgroundLoop1
stopping backgroundLoop2

func (*Group) Cancel added in v1.13.0

func (g *Group) Cancel()

Cancel cancels the `context.Context` that was passed to all goroutines spawned via `Go` on this `Group`.

func (*Group) Go added in v1.13.0

func (g *Group) Go(f func(ctx context.Context) error)

Go spawns a goroutine whose lifecycle will be managed via this Group object. All functions passed to Go on the same instance of Group will be given the same `context.Context` object, which will be used to indicate cancellation. If the supplied func does not abide by `ctx.Done()` of the provided `context.Context` then `Wait()` on this `Group` will hang until all functions exit on their own (possibly never).

func (*Group) Wait added in v1.13.0

func (g *Group) Wait()

Wait blocks waiting for all goroutines spawned via `Go` on this `Group` instance to complete. If `Go` has not been called then this function returns immediately.

type Handle

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

Handle is a threadsafe and multi-stop safe handle to a single running goroutine.

Example
package main

import (
	"context"
	"fmt"

	"go.temporal.io/server/internal/goro"
)

func main() {
	h := goro.NewHandle(context.Background()).Go(func(ctx context.Context) error {
		<-ctx.Done()
		fmt.Println("shutting down")
		return ctx.Err()
	})
	fmt.Println(h.Err())
	h.Cancel()
	<-h.Done()
	fmt.Println(h.Err())

}
Output:

<nil>
shutting down
context canceled

func NewHandle added in v1.17.3

func NewHandle(ctx context.Context) *Handle

NewHandle creates a *Handle that serves as a handle to a goroutine. The caller should call Go exactly once on the returned value. NewHandle and Go are separate function so that the *Handle can be stored into a field before the goroutine starts, which makes it possible for the goroutine to call Done() on itself (maybe indirectly) without a race condition.

func (*Handle) Cancel

func (h *Handle) Cancel()

Cancel requests that this goroutine stop by cancelling the associated context object. This function is threadsafe and idempotent. Note that this function _requests_ termination, it does not forcefully kill the goroutine.

func (*Handle) Done

func (h *Handle) Done() <-chan struct{}

Done exposes a channel that allows outside goroutines to block on this goroutine's completion. Whatever time passes between a call to Cancel() and the Done() channel closing is the time taken by the goroutine to shut itself down.

func (*Handle) Err

func (h *Handle) Err() error

Error observes the error returned by the func passed to Go (if any). There is never any error (i.e. this function returns nil) while the goroutine is running.

func (*Handle) Go added in v1.17.3

func (h *Handle) Go(f func(context.Context) error) *Handle

Go launches the supplied function in its own goroutine. Go should be called exactly once on each *Handle.

Jump to

Keyboard shortcuts

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