From 3ed81051e814e688f7b4ae1bbc7c4d4c3fbd7d0f Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Wed, 8 Aug 2018 12:26:01 -0400 Subject: Add an SHM-backed Lock Manager implementation Signed-off-by: Matthew Heon --- libpod/lock/lock.go | 8 +-- libpod/lock/shm_lock.go | 8 +-- libpod/lock/shm_lock_manager.go | 109 ++++++++++++++++++++++++++++++++++++++++ libpod/lock/shm_lock_test.go | 5 +- 4 files changed, 119 insertions(+), 11 deletions(-) create mode 100644 libpod/lock/shm_lock_manager.go (limited to 'libpod') diff --git a/libpod/lock/lock.go b/libpod/lock/lock.go index 6999e1118..6d17828f6 100644 --- a/libpod/lock/lock.go +++ b/libpod/lock/lock.go @@ -1,7 +1,7 @@ package lock -// LockManager provides an interface for allocating multiprocess locks. -// Locks returned by LockManager MUST be multiprocess - allocating a lock in +// Manager provides an interface for allocating multiprocess locks. +// Locks returned by Manager MUST be multiprocess - allocating a lock in // process A and retrieving that lock's ID in process B must return handles for // the same lock, and locking the lock in A should exclude B from the lock until // it is unlocked in A. @@ -13,7 +13,7 @@ package lock // AllocateLock() must fail once all available locks have been allocated. // Locks are returned to use by calls to Free(), and can subsequently be // reallocated. -type LockManager interface { +type Manager interface { // AllocateLock returns an unallocated lock. // It is guaranteed that the same lock will not be returned again by // AllocateLock until the returned lock has Free() called on it. @@ -35,7 +35,7 @@ type LockManager interface { type Locker interface { // ID retrieves the lock's ID. // ID is guaranteed to uniquely identify the lock within the - // LockManager - that is, calling RetrieveLock with this ID will return + // Manager - that is, calling RetrieveLock with this ID will return // another instance of the same lock. ID() string // Lock locks the lock. diff --git a/libpod/lock/shm_lock.go b/libpod/lock/shm_lock.go index 4d7c26aa2..a8a969479 100644 --- a/libpod/lock/shm_lock.go +++ b/libpod/lock/shm_lock.go @@ -27,13 +27,13 @@ type SHMLocks struct { // semaphores, and returns a struct that can be used to operate on those locks. // numLocks must be a multiple of the lock bitmap size (by default, 32). func CreateSHMLock(numLocks uint32) (*SHMLocks, error) { - if numLocks % bitmapSize != 0 || numLocks == 0 { + if numLocks%bitmapSize != 0 || numLocks == 0 { return nil, errors.Wrapf(syscall.EINVAL, "number of locks must be a multiple of %d", C.bitmap_size_c) } locks := new(SHMLocks) - var errCode C.int = 0 + var errCode C.int lockStruct := C.setup_lock_shm(C.uint32_t(numLocks), &errCode) if lockStruct == nil { // We got a null pointer, so something errored @@ -52,13 +52,13 @@ func CreateSHMLock(numLocks uint32) (*SHMLocks, error) { // segment was created with and be a multiple of the lock bitmap size (default // 32). func OpenSHMLock(numLocks uint32) (*SHMLocks, error) { - if numLocks % bitmapSize != 0 || numLocks == 0 { + if numLocks%bitmapSize != 0 || numLocks == 0 { return nil, errors.Wrapf(syscall.EINVAL, "number of locks must be a multiple of %d", C.bitmap_size_c) } locks := new(SHMLocks) - var errCode C.int = 0 + var errCode C.int lockStruct := C.open_lock_shm(C.uint32_t(numLocks), &errCode) if lockStruct == nil { // We got a null pointer, so something errored diff --git a/libpod/lock/shm_lock_manager.go b/libpod/lock/shm_lock_manager.go new file mode 100644 index 000000000..1fc2b106e --- /dev/null +++ b/libpod/lock/shm_lock_manager.go @@ -0,0 +1,109 @@ +package lock + +import ( + "fmt" + "math" + "strconv" + "syscall" + + "github.com/pkg/errors" +) + +// SHMLockManager manages shared memory locks. +type SHMLockManager struct { + locks *SHMLocks +} + +// NewSHMLockManager makes a new SHMLockManager with the given number of locks. +func NewSHMLockManager(numLocks uint32) (Manager, error) { + locks, err := CreateSHMLock(numLocks) + if err != nil { + return nil, err + } + + manager := new(SHMLockManager) + manager.locks = locks + + return manager, nil +} + +// OpenSHMLockManager opens an existing SHMLockManager with the given number of +// locks. +func OpenSHMLockManager(numLocks uint32) (LockManager, error) { + locks, err := OpenSHMLock(numLocks) + if err != nil { + return nil, err + } + + manager := new(SHMLockManager) + manager.locks = locks + + return manager, nil +} + +// AllocateLock allocates a new lock from the manager. +func (m *SHMLockManager) AllocateLock() (Locker, error) { + semIndex, err := m.locks.AllocateSemaphore() + if err != nil { + return nil, err + } + + lock := new(SHMLock) + lock.lockID = semIndex + lock.manager = m + + return lock, nil +} + +// RetrieveLock retrieves a lock from the manager given its ID. +func (m *SHMLockManager) RetrieveLock(id string) (Locker, error) { + intID, err := strconv.ParseInt(id, 16, 64) + if err != nil { + return errors.Wrapf(err, "given ID %q is not a valid SHMLockManager ID - cannot be parsed as int", id) + } + + if intID < 0 { + return errors.Wrapf(syscall.EINVAL, "given ID %q is not a valid SHMLockManager ID - must be positive", id) + } + + if intID > math.MaxUint32 { + return errors.Wrapf(syscall.EINVAL, "given ID %q is not a valid SHMLockManager ID - too large", id) + } + + var u32ID uint32 = uint32(intID) + if u32ID >= m.locks.maxLocks { + return errors.Wrapf(syscall.EINVAL, "given ID %q is not a valid SHMLockManager ID - too large to fit", id) + } + + lock := new(SHMLock) + lock.lockID = u32ID + lock.manager = m + + return lock, nil +} + +// SHMLock is an individual shared memory lock. +type SHMLock struct { + lockID uint32 + manager *SHMLockManager +} + +// ID returns the ID of the lock. +func (l *SHMLock) ID() string { + return fmt.Sprintf("%x", l.lockID) +} + +// Lock acquires the lock. +func (l *SHMLock) Lock() error { + return l.manager.locks.LockSemaphore(l.lockID) +} + +// Unlock releases the lock. +func (l *SHMLock) Unlock() error { + return l.manager.locks.UnlockSemaphore(l.lockID) +} + +// Free releases the lock, allowing it to be reused. +func (l *SHMLock) Free() error { + return l.manager.locks.DeallocateSemaphore(l.lockID) +} diff --git a/libpod/lock/shm_lock_test.go b/libpod/lock/shm_lock_test.go index 6d4525f6a..4903d3a50 100644 --- a/libpod/lock/shm_lock_test.go +++ b/libpod/lock/shm_lock_test.go @@ -4,8 +4,8 @@ import ( "fmt" "os" "syscall" - "time" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -41,7 +41,6 @@ func TestMain(m *testing.M) { os.Exit(exitCode) } - func runLockTest(t *testing.T, testFunc func(*testing.T, *SHMLocks)) { locks, err := OpenSHMLock(numLocks) if err != nil { @@ -66,7 +65,7 @@ func runLockTest(t *testing.T, testFunc func(*testing.T, *SHMLocks)) { } }() - success := t.Run("locks", func (t *testing.T) { + success := t.Run("locks", func(t *testing.T) { testFunc(t, locks) }) if !success { -- cgit v1.2.3-54-g00ecf