syncutil

package module
Version: v0.0.0-...-228ac8e Latest Latest
Warning

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

Go to latest
Published: Feb 1, 2018 License: Apache-2.0 Imports: 3 Imported by: 127

README

GoDoc

This package contains code that supplements the sync package from the Go standard library. In particular:

  • Bundle, which makes it easy to write code that spawns multiple cancellation-aware workers that may fail.
  • InvariantMutex, which makes it possible to automatically check your invariants at lock and unlock time.

See the reference for more info.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EnableInvariantChecking

func EnableInvariantChecking()

Enable checking of invariants when locking and unlocking InvariantMutex.

func InvariantCheckingEnabled

func InvariantCheckingEnabled() bool

Has EnableInvariantChecking previously been called?

Types

type Bundle

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

A collection of concurrently-executing operations, each of which may fail.

Operations are defined by functions that accept a context and return an error. If any operation returns a non-nil error, all concurrent and future operations will have their contexts cancelled . When Join() is called on a bundle with one or more operations that returned an error, it always returns the first error (i.e. that which led to the cancellation of others).

Bundles can be used to set up pipelines of concurrent actors sending data to each other, conveniently cancelling the pipeline if anything fails. A typical use looks like the following:

 // Run a pipeline that consists of one goroutine listing object names,
 // while N goroutines concurrently delete the listed objects one by one.
 // If any listing or deletion operation fails, cancel the whole pipeline
 // and return the error.
 func deleteAllObjects(ctx context.Context, N int) error {
   bundle := syncutil.NewBundle(ctx)

   // List objects into a channel. Assuming that listObjects responds to
   // cancellation of its context, it will not get stuck blocking forever
   // on a write into objectNames if the deleters return early in error
   // before draining the channel.
   objectNames := make(chan string)
   bundle.Add(func(ctx context.Context) error {
     defer close(objectNames)
     return listObjects(ctx, objectNames)
   })

   // Run N deletion workers.
   for i := 0; i < N; i++ {
     bundle.Add(func(ctx context.Context) error {
       for name := range objectNames {
         if err := deleteObject(ctx, name); err != nil {
           return err
         }
       }
     })
   }

   // Wait for the whole pipeline to finish, and return its status.
   return bundle.Join()
}

Bundles must be created using NewBundle. Bundle methods must not be called concurrently.

func NewBundle

func NewBundle(parent context.Context) *Bundle

Create a bundle whose operations are fed a context inheriting from the given parent context, which must be non-nil. The bundle must eventually be joined with Join.

func (*Bundle) Add

func (b *Bundle) Add(f func(context.Context) error)

Add a new operation to the bundle. The operation will be invoked with a context that will be cancelled if any other operation fails or has already failed.

func (*Bundle) Join

func (b *Bundle) Join() error

Wait for all previously-added operations to complete. Return nil if all operations succeeded. Otherwise return the first error.

Add must not be called concurrently with or after Join.

type InvariantMutex

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

A sync.Locker that, when enabled, runs a check for registered invariants at times when invariants should hold. This can aid debugging subtle code by crashing early as soon as something unexpected happens.

Must be created with NewInvariantMutex. See that function for more details.

A typical use looks like this:

type myStruct struct {
  mu syncutil.InvariantMutex

  // INVARIANT: nextGeneration == currentGeneration + 1
  currentGeneration int // GUARDED_BY(mu)
  nextGeneration    int // GUARDED_BY(mu)
}

// The constructor function for myStruct sets up the mutex to
// call the checkInvariants method.
func newMyStruct() *myStruct {
  s := &myStruct{
    currentGeneration: 1,
    nextGeneration:    2,
  }

  s.mu = syncutil.NewInvariantMutex(s.checkInvariants)
  return s
}

func (s *myStruct) checkInvariants() {
  if s.nextGeneration != s.currentGeneration+1 {
    panic(
      fmt.Sprintf("%v != %v + 1", s.nextGeneration, s.currentGeneration))
  }
}

// When EnableInvariantChecking has been called, invariants will be
// checked at entry to and exit from this function.
func (s *myStruct) setGeneration(n int) {
  s.mu.Lock()
  defer s.mu.Unlock()

  s.currentGeneration = n
  s.nextGeneration = n + 1
}

func NewInvariantMutex

func NewInvariantMutex(check func()) (mu InvariantMutex)

Create a lock which, when EnableInvariantChecking has been called, will call the supplied function at moments when invariants protected by the lock should hold (e.g. just after acquiring the lock). The function should crash if an invariant is violated. It should not have side effects, as there are no guarantees that it will run.

The invariants must hold at the time that NewInvariantMutex is called.

func (*InvariantMutex) Lock

func (i *InvariantMutex) Lock()

func (*InvariantMutex) RLock

func (i *InvariantMutex) RLock()

func (*InvariantMutex) RUnlock

func (i *InvariantMutex) RUnlock()

func (*InvariantMutex) Unlock

func (i *InvariantMutex) Unlock()

Jump to

Keyboard shortcuts

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