latch

package module
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: May 26, 2023 License: Apache-2.0 Imports: 3 Imported by: 3

README

A Counting Latch

Golang codecov PkgGoDev

This package contains a notification-based, counter latch. This is conceptually similar to a sync.WaitGroup, except that it does not require foreknowledge of the total number of tasks that will be tracked.

The API is stable and semantically versioned. The implementation depends only on the standard library.

Documentation

Overview

Package latch provides a notification-based counter latch.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Counter

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

Counter is conceptually similar to a sync.WaitGroup, except that it does not require foreknowledge of how many tasks there will be, and it uses a notification-style API. That it, it can count both up and down.

It is always valid to add or remove holds on the Counter, regardless of their ordering with Wait. For example, a Counter can be used to track the completion of tasks that spawn an unknown number of sub-tasks.

The Wait methods on a Counter will trigger when the count is exactly zero.

Counter additionally implements sync.Locker. When the latch is in a locked state, no changes to the Counter's count may be made.

A Counter should not be copied.

Example (Hierarchy)

This example shows how a Counter can be used in a manner similar to a sync.WaitGroup, but where the total number of sub-processes is unknown to the parent process.

// The "work" will be to increment this value; it's not relevant to
// the use of the latch.
var dummyWork int32

// Latches must be constructed, the zero value isn't useful.
latch := New()

// Spawn a number of child goroutines.
for i := 0; i < 10; i++ {
	latch.Hold()
	go func() {
		defer latch.Release()
		// Do work.
		atomic.AddInt32(&dummyWork, 1)

		// Spawn grand-children goroutines.
		for j := 0; j < 10; j++ {
			latch.Hold()
			go func() {
				defer latch.Release()
				// Do more work.
				atomic.AddInt32(&dummyWork, 1)
			}()
		}
	}()
}

// The top level code can wait for the unknown number of child
// routines to finish.  Since the API is channel-based, it can be
// combined with select statements for conditional flow control.
select {
case <-latch.Wait():
	fmt.Printf("%d processes ran, with %d holds pending",
		atomic.LoadInt32(&dummyWork), latch.Count())
case <-time.After(time.Second):
	fmt.Println("Timed out")
}
Output:

110 processes ran, with 0 holds pending

func New

func New(options ...*Option) *Counter

New constructs a Counter using the provided options.

func (*Counter) Apply

func (l *Counter) Apply(delta int)

Apply changes the hold-count on the latch.

This method will panic if a negative delta exceeds the number of holds on the Counter.

func (*Counter) Count

func (l *Counter) Count() int64

Count returns an estimate of the number of pending holds.

This method is not subject to locking and is safe to call at any time. This makes it suitable as a source for metrics collection.

func (*Counter) Hold

func (l *Counter) Hold()

Hold increments the use-count by 1.

It is a shortcut for Apply(1).

func (*Counter) Lock

func (l *Counter) Lock()

Lock will lock the underlying sync.Locker used by the Counter.

This has the effect of blocking all other uses of the Counter until a call to Unlock is made.

func (*Counter) Release

func (l *Counter) Release()

Release decrements the use-count by 1.

It is a shortcut for Apply(-1).

func (*Counter) Unlock

func (l *Counter) Unlock()

Unlock will unlock the underlying sync.Locker used by the Counter.

func (*Counter) Wait

func (l *Counter) Wait() <-chan WaitStatus

Wait returns a channel that will emit a single value once the wait condition has been (instantaneously) satisfied.

Consider using WaitLock if it is necessary to prevent additional holds from being obtained until a call to Unlock is made.

func (*Counter) WaitHold

func (l *Counter) WaitHold(holds int) <-chan WaitStatus

WaitHold returns a channel that will emit a single value once the wait condition has been (instantaneously) satisfied and the requested number of holds has been added.

This method will panic if holds is negative.

func (*Counter) WaitLock

func (l *Counter) WaitLock() <-chan WaitStatus

WaitLock returns a channel that will emit a single value once the wait condition has been (instantaneously) satisfied and the Counter has been left in a locked state.

Callers to this method must ensure that Unlock is called after receiving the notification.

type Option

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

Option allows the behavior of a Counter to be tuned.

func WithLocker

func WithLocker(l sync.Locker) *Option

WithLocker constructs a Counter that is externally synchronized using the given sync.Locker.

type WaitStatus

type WaitStatus int

WaitStatus is returned from the Wait functions.

func (WaitStatus) Delayed

func (s WaitStatus) Delayed() bool

Delayed returns true if the call to Wait was delayed by existing holds.

func (WaitStatus) Locked

func (s WaitStatus) Locked() bool

Locked returns true if the call to wait left the Counter in a locked state.

Jump to

Keyboard shortcuts

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