lockable

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jul 5, 2023 License: MIT Imports: 1 Imported by: 3

README


go-lockable

key based lock


About The Project

go-lockable provides a simple implementation for acquiring locks by key.

This can be useful when multiple goroutines need to manipulate a map atomically using async calls without blocking access to all keys or when access to certain ressource needs to be partially locked.

This pkg does not use any 3rd party dependencies, only std packages.

Getting Started

Installation

go get github.com/MysteriousPotato/go-lockable

Usage

package main


import (
	"github.com/MysteriousPotato/go-lockable"
)

func main() {
	// Using lockable like a mutex:
    lock := lockable.New[string]()
	lock.LockKey("potato")
    defer lock.UnlockKey("potato")
    
    // Using go-lockable built-in Map type:
    lockableMap := lockable.NewMap[string, int]()
    lockableMap.LockKey("potato")
    defer lockableMap.UnlockKey("potato")
    
    // Do async stuff....
    
    lockableMap.Store("potato", 10)
}

For more detailed examples, please refer to the Documentation_

Roadmap

See the open issues for a full list of proposed features (and known issues).

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".

License

Distributed under the MIT License. See LICENSE for more information.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Lockable

type Lockable[T comparable] struct {
	// contains filtered or unexported fields
}

Lockable can be used to add lock-by-key support to any struct.

The zero value is not ready for use. Refer to New for creating ready-to-use instance.

Lockable has an interface that's deliberately similar to sync.RWMutex.

For exemple, instead of using:

func main() {
	// Read/write lock
	myMutex.Lock()
	defer myMutex.Unlock()

	// Read lock
	myMutex.RLock()
	defer myMutex.RUnlock()
}

You would use it like so:

func main() {
	// Read/write lock
	myLockable.LockKey(arbitraryKey)
	defer myLockable.Unlock(arbitraryKey)

	// Read lock
	myLockable.RLock(arbitraryKey)
	defer myLockable.RUnlock(arbitraryKey)
}

You can use it to add lock-by-key support to an already existing struct.

Ex.:

func main() {
		type ArbitraryType struct {
			lockable.Lockable[string]
			// properties...
		}

		arbitrary := ArbitraryType{
			Lockable: lockable.New[string](),
		}

		// You can now use it the way to acquire locks
		arbitrary.LockKey("potato")
		defer arbitrary.UnlockKey("potato")

		// Do stuff...
}

Lockable.LockKeyDuring and Lockable.RLockKeyDuring can be used to automatically manage lock acquisition/release.

Ex.:

func main() {
	type ArbitraryType struct {
		lockable.Lockable[string]
		// properties...
	}

	arbitrary := ArbitraryType{
		Lockable: lockable.New[string](),
	}

	_, err := arbitrary.LockKeyDuring("potato", func() (any, error) {
		// Do stuff...
	})
}

You can use Lockable.IsLocked to check if a lock is currently being held without locking the key. Ex.:

func main() {
	type ArbitraryType struct {
		lockable.Lockable[string]
		// properties...
	}

	arbitrary := ArbitraryType{
		Lockable: lockable.New[string](),
	}

	if arbitrary.IsLocked("potato") {
	 	//.. do stuff.
	}
}

func New added in v0.2.0

func New[T comparable]() Lockable[T]

New creates a ready-to-use Lockable instance.

Refer to Lockable for usage

func (Lockable[T]) IsLocked added in v0.2.0

func (l Lockable[T]) IsLocked(key T) bool

IsLocked is used to determine whether a key has been locked without locking the key.

func (Lockable[T]) LockKey

func (l Lockable[T]) LockKey(key T)

LockKey method is used to acquire read/write locks.

Use Lockable.RLockKey for read locks.

func (Lockable[T]) LockKeyDuring

func (l Lockable[T]) LockKeyDuring(key T, fn func() (any, error)) (any, error)

LockKeyDuring will automatically acquire a read/write lock before executing fn and release it once done.

func (Lockable[T]) RLockKey

func (l Lockable[T]) RLockKey(key T)

RLockKey method is used to acquire read locks.

Use Lockable.RLockKey for read/write locks.

func (Lockable[T]) RLockKeyDuring

func (l Lockable[T]) RLockKeyDuring(key T, fn func() (any, error)) (any, error)

RLockKeyDuring before executing fn and release it once done.

func (Lockable[T]) RUnlockKey

func (l Lockable[T]) RUnlockKey(key T)

RUnlockKey method is used to release read/write locks.

Can safely be called multiple times on the same key.

func (Lockable[T]) UnlockKey

func (l Lockable[T]) UnlockKey(key T)

UnlockKey method is used to release read/write locks.

Can safely be called multiple times on the same key.

type Map

type Map[T comparable, V any] struct {
	Lockable[T]
	// contains filtered or unexported fields
}

Map implements a map that can acquire key-specific locks.

The zero value is not ready for use. Refer to NewMap to create a ready-to-use instance.

Map has an interface that's deliberately similar to sync.Map, but uses a combination of RWMutex/map to simulate it.

MutexMap can be used instead if you want to use sync.Map internally. Refer to sync.Map's document for use cases.

Ex. usage:

func main() {
	lockableMap := lockable.NewMap[string, int]()

	// This will only lock access the "potato" key
	// Keys do not need to exist prior to acquiring the key lock
	lockableMap.LockKey("potato")
	defer lockableMap.UnlockKey("potato")

	// Do async stuff....

	lockableMap.Store("potato", 10)
}

Refer to Lockable for more detailed exemples of locking.

func NewMap

func NewMap[T comparable, V any]() Map[T, V]

NewMap creates a ready-to-use Map instance.

Refer to Map for usage.

func (Map[T, V]) Delete

func (m Map[T, V]) Delete(key T)

Delete effectively serves the same purpose as sync.Map.Delete

func (Map[T, V]) Load

func (m Map[T, V]) Load(key T) (V, bool)

Load effectively serves the same purpose as sync.Map.Load

func (Map[T, V]) Range

func (m Map[T, V]) Range(fn func(key T, value V) bool)

Range effectively serves the same purpose as sync.Map.Range

func (Map[T, V]) Store

func (m Map[T, V]) Store(key T, value V)

Store effectively serves the same purpose as sync.Map.Store

type MutexMap

type MutexMap[T comparable] struct {
	sync.Map
	Lockable[T]
}

MutexMap acts the same as Map except it uses a sync.Map.

The zero value is not ready for use. Refer to NewMutexMap to create a ready-to-use instance.

Ex. usage:
func main() {
	lockableMap := lockable.NewMutexMap[string]()

	// This will only lock access the "potato" key
	// Keys do not need to exist prior to acquiring the key lock
	lockableMap.LockKey("potato")
	defer lockableMap.UnlockKey("potato")

	// Do async stuff....

	lockableMap.Store("potato", 10)
}

Refer to Lockable for more detailed exemples of locking

func NewMutexMap

func NewMutexMap[T comparable]() MutexMap[T]

NewMutexMap creates a ready-to-use MutexMap instance.

Refer to MutexMap for usage.

Jump to

Keyboard shortcuts

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