summaryrefslogtreecommitdiff
path: root/libpod/lock
diff options
context:
space:
mode:
authorMatthew Heon <matthew.heon@gmail.com>2018-08-08 12:26:01 -0400
committerMatthew Heon <matthew.heon@pm.me>2019-01-04 09:45:59 -0500
commit3ed81051e814e688f7b4ae1bbc7c4d4c3fbd7d0f (patch)
tree8b63f3390d82228b451e7832dd42d5f4d329cedf /libpod/lock
parent185136cf0edee5288576b541517e0e994f6ee18d (diff)
downloadpodman-3ed81051e814e688f7b4ae1bbc7c4d4c3fbd7d0f.tar.gz
podman-3ed81051e814e688f7b4ae1bbc7c4d4c3fbd7d0f.tar.bz2
podman-3ed81051e814e688f7b4ae1bbc7c4d4c3fbd7d0f.zip
Add an SHM-backed Lock Manager implementation
Signed-off-by: Matthew Heon <matthew.heon@gmail.com>
Diffstat (limited to 'libpod/lock')
-rw-r--r--libpod/lock/lock.go8
-rw-r--r--libpod/lock/shm_lock.go8
-rw-r--r--libpod/lock/shm_lock_manager.go109
-rw-r--r--libpod/lock/shm_lock_test.go5
4 files changed, 119 insertions, 11 deletions
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 {