shot

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2025 License: MIT Imports: 5 Imported by: 0

README

shot - manage instance execution

Go Reference

This package provides concurrency primitives for managing the execution of instances.

Types

  • One - ensures that only one instance runs at a time and runs only once
  • Many - ensures that only one instance runs at a time
  • Xor - ensures only one instance runs at a time, but allows replacing the running instance

Functions

  • Go - runs a function in a goroutine with a managed context
  • GoCtx - runs a function in a goroutine with a managed context derived from the parent context ctx

Documentation

Overview

Package shot provides concurrency primitives for managing the execution of instances.

Index

Examples

Constants

This section is empty.

Variables

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

Functions

This section is empty.

Types

type G

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

G allows to manage execution of a goroutine and get the error returned from the goroutine.

func Go

func Go(f func(ctx context.Context) error) *G

Go runs a function in a goroutine with a managed context.

Returns G, which can be used to manage the goroutine execution and get the error returned from the goroutine.

func GoCtx

func GoCtx(ctx context.Context, f func(ctx context.Context) error) *G

GoCtx runs a function in a goroutine with a managed context derived from the parent context ctx.

Returns G, which can be used to manage the goroutine execution and get the error returned from the goroutine.

func (*G) Close

func (g *G) Close(ctx context.Context) error

Close stops the goroutine and waits for it to exit with the given context.

Context passed to this method can be canceled to pass control back to the caller if waiting for the goroutine to exit takes too much time. Context cancellation only affects the wait time, not the goroutine itself.

func (*G) Done

func (g *G) Done() <-chan struct{}

Done returns a channel that is closed when the goroutine has exited.

func (*G) Err

func (g *G) Err() error

Err returns the error returned from the goroutine. If goroutine hasn't yet exited, returns nil. If goroutine exited without error, also returns nil.

type Many

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

Many ensures that only one instance runs at a time.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"codeberg.org/go-toolbox/shot"
)

type FibMany struct {
	values [2]int
	idx    int
	output chan int
	state  shot.Many
}

func NewFibMany(ctx context.Context) *FibMany {
	return &FibMany{
		values: [2]int{-1, 1},
		idx:    0,
		output: make(chan int, 1),
		state:  shot.NewMany(ctx),
	}
}

func (e *FibMany) Run() error {
	stop, err := e.state.Start()
	if err != nil {
		return err
	}
	defer stop()

	ticker := time.NewTicker(10 * time.Millisecond)
	defer ticker.Stop()

	for {
		select {
		case <-e.state.Context().Done():
			return e.state.Context().Err()
		case <-ticker.C:
			sum := e.values[0] + e.values[1]
			e.values[e.idx] = sum
			e.idx = (e.idx + 1) % 2
			e.output <- sum
			if sum == 13 || sum == 89 {
				return nil
			}
			if sum == 144 {
				ticker.Stop()
			}
		}
	}
}

func (e *FibMany) Close() error {
	if err := e.state.Close(context.Background()); err != nil {
		return err
	}
	close(e.output)
	return nil
}

func (e *FibMany) Output() <-chan int {
	return e.output
}

func (e *FibMany) Done() <-chan struct{} {
	return e.state.Done()
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	fib := NewFibMany(ctx)
	go fib.Run()
	defer fib.Close()

	for {
		select {
		case <-ctx.Done():
			return
		case <-fib.Done():
			go fib.Run()
		case msg := <-fib.Output():
			fmt.Print(msg, " ")
		}
	}

}
Output:

0 1 1 2 3 5 8 13 21 34 55 89 144

func NewMany

func NewMany(parent context.Context) Many

NewMany creates Many with the given parent context.

func (*Many) Close

func (m *Many) Close(ctx context.Context) error

Close shuts down the instance and cancels its context.

If not started, prevents instance from being started at all. If running, waits for completion with the given context.

Context passed to this method can be canceled to pass control back to the caller if waiting for completion takes too much time. Context cancellation only affects the wait time, not the instance itself.

func (*Many) Context

func (m *Many) Context() context.Context

Context returns the instance's context.

The context is cancelled when:

  • The instance exits normally
  • Close is called
  • The parent context is cancelled

This context must be used to exit when it has been cancelled.

func (*Many) Done

func (m *Many) Done() <-chan struct{}

Done returns a channel that is closed when the instance instance exits or is closed.

func (*Many) Start

func (m *Many) Start() (stop func(), err error)

Start starts the instance and returns a stop function. Call stop to signal completion and allow restarting. The stop function must be called. Returns an error if already running or closed.

func (*Many) State

func (m *Many) State() State

State returns the current state of the instance.

type One

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

One ensures that only one instance runs at a time and runs only once.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"codeberg.org/go-toolbox/shot"
)

type FibOne struct {
	output chan int
	state  shot.One
}

func NewFibOne(ctx context.Context) *FibOne {
	return &FibOne{
		output: make(chan int, 1),
		state:  shot.NewOne(ctx),
	}
}

func (e *FibOne) Run() error {
	stop, err := e.state.Start()
	if err != nil {
		return err
	}
	defer stop()

	ticker := time.NewTicker(10 * time.Millisecond)
	defer ticker.Stop()

	values := [2]int{-1, 1}
	idx := 0

	for {
		select {
		case <-e.state.Context().Done():
			return e.state.Context().Err()
		case <-ticker.C:
			sum := values[0] + values[1]
			values[idx] = sum
			idx = (idx + 1) % 2
			e.output <- sum
			if sum == 144 {
				ticker.Stop()
			}
		}
	}
}

func (e *FibOne) Close() error {
	if err := e.state.Close(context.Background()); err != nil {
		return err
	}
	return nil
}

func (e *FibOne) Output() <-chan int {
	return e.output
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	fib := NewFibOne(ctx)
	go fib.Run()
	defer fib.Close()

	for {
		select {
		case <-ctx.Done():
			return
		case msg := <-fib.Output():
			fmt.Print(msg, " ")
		}
	}

}
Output:

0 1 1 2 3 5 8 13 21 34 55 89 144

func NewOne

func NewOne(parent context.Context) One

NewOne creates One with the given parent context.

func (*One) Close

func (s *One) Close(ctx context.Context) error

Close shuts down the instance and cancels its context.

If not started, prevents instance from being started at all. If running, waits for completion with the given context.

Context passed to this method can be canceled to pass control back to the caller if waiting for completion takes too much time. Context cancellation only affects the wait time, not the instance itself.

func (*One) Context

func (s *One) Context() context.Context

Context returns the instance's context.

The context is cancelled when:

  • The instance exits normally
  • Close is called
  • The parent context is cancelled

This context must be used to exit when it has been cancelled.

func (*One) Done

func (s *One) Done() <-chan struct{}

Done returns a channel that is closed when the instance instance exits or is closed.

func (*One) Start

func (s *One) Start() (stop func(), err error)

Start starts the instance and returns a stop function. The stop function signals completion and must be called. Returns an error if already running or closed.

func (*One) State

func (s *One) State() State

State returns the current state of the instance.

type State

type State int32

State represents the state of an instance.

const (
	// Instance initialized but not yet running.
	StateCreated State = iota
	// Instance is running.
	StateRunning
	// Instance completed and can be restarted.
	StateStopped
	// Instance exited and cannot be reused.
	StateClosed
)

func (State) String

func (s State) String() string

type Xor

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

Xor ensures only one instance runs at a time, but allows replacing the running instance.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"codeberg.org/go-toolbox/shot"
)

type FibXor struct {
	output chan int
	state  shot.Xor
}

func NewFibXor(ctx context.Context) *FibXor {
	return &FibXor{
		output: make(chan int, 1),
		state:  shot.NewXor(ctx),
	}
}

func (e *FibXor) Run(values [2]int, idx int) error {
	stop, err := e.state.Start(context.Background())
	if err != nil {
		return err
	}
	defer stop()

	ticker := time.NewTicker(10 * time.Millisecond)
	defer ticker.Stop()

	for {
		select {
		case <-e.state.Context().Done():
			return e.state.Context().Err()
		case <-ticker.C:
			sum := values[0] + values[1]
			values[idx] = sum
			idx = (idx + 1) % 2
			e.output <- sum
			if sum == 377 {
				ticker.Stop()
			}
		}
	}
}

func (e *FibXor) Close() error {
	if err := e.state.Close(context.Background()); err != nil {
		return err
	}
	close(e.output)
	return nil
}

func (e *FibXor) Output() <-chan int {
	return e.output
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	fib := NewFibXor(ctx)
	go fib.Run([2]int{-1, 1}, 0)
	defer fib.Close()

	for {
		select {
		case <-ctx.Done():
			return
		case msg := <-fib.Output():
			if msg == 13 {
				go fib.Run([2]int{13, 21}, 0)
			}
			if msg == 89 {
				go fib.Run([2]int{89, 144}, 0)
			}
			fmt.Print(msg, " ")
		}
	}

}
Output:

0 1 1 2 3 5 8 13 34 55 89 233 377

func NewXor

func NewXor(parent context.Context) Xor

NewXor creates Xor with the given parent context.

func (*Xor) Close

func (x *Xor) Close(ctx context.Context) error

Close shuts down the instance and cancels its context.

If not started, prevents instance from being started at all. If running, waits for completion with the given context.

Context passed to this method can be canceled to pass control back to the caller if waiting for completion takes too much time. Context cancellation only affects the wait time, not the instance itself.

func (*Xor) Context

func (x *Xor) Context() context.Context

Context returns the instance's context.

The context is cancelled when:

  • The instance exits normally
  • Close is called
  • Stop is called
  • The parent context is cancelled

This context must be used to exit when it has been cancelled.

func (*Xor) Done

func (x *Xor) Done() <-chan struct{}

Done returns a channel that is closed when the instance instance exits or is closed.

func (*Xor) Start

func (x *Xor) Start(ctx context.Context) (stop func(), err error)

Start starts or replaces the running instance and returns a stop function. Call stop to signal completion and allow restarting. The stop function must be called.

Context passed to this method can be canceled to pass control back to the caller if waiting for stopping the running instance takes too much time. In such case, the instance will eventually stop, but will not start.

Returns ErrClosed if already closed.

func (*Xor) State

func (x *Xor) State() State

State returns the current state of the instance.

func (*Xor) Stop

func (x *Xor) Stop(ctx context.Context) error

Stop shuts down the instance, allowing future restarts, and cancels its context.

If running, waits for completion with the given context.

Context passed to this method can be canceled to pass control back to the caller if waiting for completion takes too much time. Context cancellation only affects the wait time, not the instance itself.

Returns ErrClosed if already closed.

Jump to

Keyboard shortcuts

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