lockctx

package module
v0.0.0-...-226f85c Latest Latest
Warning

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

Go to latest
Published: Apr 12, 2025 License: MIT Imports: 4 Imported by: 34

README

Lockctx

Lockctx is a tool for organizing modules which use locks. It can be useful when:

  • the module uses a relatively large number of locks
  • the module is large
  • the module spans multiple layers, where locks are acquired in one layer and expected to have been acquired in another

Benefits

Lockctx is used in large modules where some functions acquire locks and some functions require locks.

  • Lock-requiring functions can enforce that a lock they require to have been acquired was acquired by the caller.
  • Lock-acquiring functions release all held locks together, so using defer statements is easy and safe
  • Policies can guarantee deadlock-free operation

Usage Rules

There are some usage requirements which must be satisfied for Lockctx to provide these benefits:

  • Lock-requiring functions must check their lockctx.Proof holds the expected lock and exit otherwise
  • lockctx.Context instances must not be shared between goroutines

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrPolicyViolation = errors.New("policy violation")

ErrPolicyViolation is returned if acquiring a lock causes a policy violation.

View Source
var NoPolicy statelessPolicy = func(holding []string, next string) bool {
	return true
}

NoPolicy enforces no constraints on lock ordering.

View Source
var StringOrderPolicy statelessPolicy = func(holding []string, next string) bool {
	if len(holding) == 0 {
		return true
	}
	last := holding[len(holding)-1]

	return last < next
}

StringOrderPolicy enforces that locks are acquired in lexicographic sort order. This Policy guarantees deadlock-free operation.

Functions

func IsUnknownLockError

func IsUnknownLockError(err error) bool

Types

type Context

type Context interface {
	Proof

	// AcquireLock acquires the lock with the given ID, unless doing so violates the configured Policy.
	// This function will block if the lock is held by another goroutine.
	//
	// Returns ErrPolicyViolation if acquiring the lock would violate the configured Policy.
	// Returns UnknownLockError if no lock with the given ID exists.
	// Panics if Release has ever been called on this Context.
	AcquireLock(lockID string) error

	// Release releases all currently held locks and permanently marks this Context as "used".
	// This method is non-blocking.
	//
	// Panics if Release has ever been called on this Context.
	Release()
}

Context represents a goroutine's access to one or more locks managed by a Manager. It provides methods for acquiring and releasing locks and checking whether a lock is held. A new Context must be created every time a goroutine first acquires a lock. A Context is not safe for concurrent access by multiple goroutines.

type DAGPolicyBuilder

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

DAGPolicyBuilder is used to construct a DAG policy. A DAG policy uses a directed acyclic graph, where graph nodes are lock IDs, to define when locks may be acquired. If an edge exists from A->B, then if I have most recently acquired lock A, I am allowed to acquire lock B next. Edges are added at construction time with Add. When all edges have been defined, the Policy can be created with Build. A DAG Policy returned from Build guarantees deadlock-free operation.

func NewDAGPolicyBuilder

func NewDAGPolicyBuilder() DAGPolicyBuilder

NewDAGPolicyBuilder returns a DAGPolicyBuilder with an empty graph.

func (DAGPolicyBuilder) Add

func (b DAGPolicyBuilder) Add(lock1, lock2 string) DAGPolicyBuilder

Add defines the Policy by adding a lock acquisition allowance (an edge in the graph). The resulting Policy will allow threads to acquire lock2 if they have just acquired lock1.

func (DAGPolicyBuilder) Build

func (b DAGPolicyBuilder) Build() Policy

Build validates that the constructed graph is acyclic. If the constructed graph is cyclic, this function will panic. DAGPolicyBuilder (and policies in general) are intended to be called at startup with statically defined parameters, hence the use of panic here. If the constructed graph is acyclic, a dagPolicy using the constructed graph is returned.

type Manager

type Manager interface {
	// NewContext returns a new Context which is able to acquire locks managed by this Manager.
	NewContext() Context
}

Manager controls access to a set of locks. The set of locks and Policy (if any) is defined at construction time and is constant for the lifecycle of the Manager.

func NewManager

func NewManager(lockIDs []string, policy Policy) Manager

type Policy

type Policy interface {
	// CanAcquire returns true if a goroutine already holding the given locks is
	// allowed to also acquire the next lock.
	//
	// Implementations must be safe for concurrent use by multiple goroutines.
	// Implementations must be non-blocking.
	CanAcquire(holding []string, next string) bool
}

Policy defines whether a goroutine is allowed acquire a new lock based on locks it already holds. Policies exist to prevent deadlock by defining a canonical ordering for the set of locks in a Manager.

type Proof

type Proof interface {
	// HoldsLock returns true if this goroutine currently holds the lock with the given ID.
	// This method is non-blocking.
	//
	// Panics if no lock with the given ID exists.
	HoldsLock(lockID string) bool
}

Proof provides a read-only interface to a Context. A low-level function which must be executed while holding a certain lock, but which is not itself responsible for acquiring that lock, can accept a Proof argument. It can then validate that the caller has acquired the necessary lock.

type UnknownLockError

type UnknownLockError struct {
	LockID string
}

UnknownLockError is returned if an unknown lock is acquired.

func NewUnknownLockError

func NewUnknownLockError(lockID string) UnknownLockError

func (UnknownLockError) Error

func (err UnknownLockError) Error() string

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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