mirror of
https://github.com/containers/podman.git
synced 2026-03-13 08:01:19 +08:00
Add an SHM-backed Lock Manager implementation
Signed-off-by: Matthew Heon <matthew.heon@gmail.com>
This commit is contained in:
committed by
Matthew Heon
parent
185136cf0e
commit
3ed81051e8
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
109
libpod/lock/shm_lock_manager.go
Normal file
109
libpod/lock/shm_lock_manager.go
Normal file
@@ -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)
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user