@ -21,6 +21,7 @@ import (
"errors"
pathutil "path"
"runtime"
"sort"
"strings"
"sync"
@ -51,16 +52,10 @@ func newNSLock(isDistXL bool) *nsLockMap {
if isDistXL {
return & nsMutex
}
nsMutex . lockMap = make ( map [ nsParam ] * nsLock )
nsMutex . lockMap = make ( map [ string ] * nsLock )
return & nsMutex
}
// nsParam - carries name space resource.
type nsParam struct {
volume string
path string
}
// nsLock - provides primitives for locking critical namespace regions.
type nsLock struct {
* lsync . LRWMutex
@ -72,23 +67,24 @@ type nsLock struct {
type nsLockMap struct {
// Indicates if namespace is part of a distributed setup.
isDistXL bool
lockMap map [ nsParam ] * nsLock
lockMap map [ string ] * nsLock
lockMapMutex sync . RWMutex
}
// Lock the namespace resource.
func ( n * nsLockMap ) lock ( ctx context . Context , volume , path string , lockSource , opsID string , readLock bool , timeout time . Duration ) ( locked bool ) {
func ( n * nsLockMap ) lock ( ctx context . Context , volume string , path string , lockSource , opsID string , readLock bool , timeout time . Duration ) ( locked bool ) {
var nsLk * nsLock
resource := pathJoin ( volume , path )
n . lockMapMutex . Lock ( )
param := nsParam { volume , path }
nsLk , found := n . lockMap [ param ]
nsLk , found := n . lockMap [ resource ]
if ! found {
n . lockMap [ param ] = & nsLock {
nsLk = & nsLock {
LRWMutex : lsync . NewLRWMutex ( ctx ) ,
ref : 1 ,
}
nsLk = n . lockMap [ param ]
n . lockMap [ resource ] = nsLk
} else {
// Update ref count here to avoid multiple races.
nsLk . ref ++
@ -109,7 +105,7 @@ func (n *nsLockMap) lock(ctx context.Context, volume, path string, lockSource, o
nsLk . ref --
if nsLk . ref == 0 {
// Remove from the map if there are no more references.
delete ( n . lockMap , param )
delete ( n . lockMap , resource )
}
n . lockMapMutex . Unlock ( )
}
@ -117,10 +113,10 @@ func (n *nsLockMap) lock(ctx context.Context, volume, path string, lockSource, o
}
// Unlock the namespace resource.
func ( n * nsLockMap ) unlock ( volume , path string , readLock bool ) {
param := nsParam { volume , path }
func ( n * nsLockMap ) unlock ( volume string , path string , readLock bool ) {
resource := pathJoin ( volume , path )
n . lockMapMutex . RLock ( )
nsLk , found := n . lockMap [ param ]
nsLk , found := n . lockMap [ resource ]
n . lockMapMutex . RUnlock ( )
if ! found {
return
@ -137,45 +133,16 @@ func (n *nsLockMap) unlock(volume, path string, readLock bool) {
nsLk . ref --
if nsLk . ref == 0 {
// Remove from the map if there are no more references.
delete ( n . lockMap , param )
delete ( n . lockMap , resource )
}
}
n . lockMapMutex . Unlock ( )
}
// Lock - locks the given resource for writes, using a previously
// allocated name space lock or initializing a new one.
func ( n * nsLockMap ) Lock ( volume , path , opsID string , timeout time . Duration ) ( locked bool ) {
readLock := false // This is a write lock.
lockSource := getSource ( ) // Useful for debugging
return n . lock ( context . Background ( ) , volume , path , lockSource , opsID , readLock , timeout )
}
// Unlock - unlocks any previously acquired write locks.
func ( n * nsLockMap ) Unlock ( volume , path , opsID string ) {
readLock := false
n . unlock ( volume , path , readLock )
}
// RLock - locks any previously acquired read locks.
func ( n * nsLockMap ) RLock ( volume , path , opsID string , timeout time . Duration ) ( locked bool ) {
readLock := true
lockSource := getSource ( ) // Useful for debugging
return n . lock ( context . Background ( ) , volume , path , lockSource , opsID , readLock , timeout )
}
// RUnlock - unlocks any previously acquired read locks.
func ( n * nsLockMap ) RUnlock ( volume , path , opsID string ) {
readLock := true
n . unlock ( volume , path , readLock )
}
// dsync's distributed lock instance.
type distLockInstance struct {
rwMutex * dsync . DRWMutex
volume , path , opsID string
rwMutex * dsync . DRWMutex
opsID string
}
// Lock - block until write lock is taken or timeout has occurred.
@ -185,7 +152,7 @@ func (di *distLockInstance) GetLock(timeout *dynamicTimeout) (timedOutErr error)
if ! di . rwMutex . GetLock ( di . opsID , lockSource , timeout . Timeout ( ) ) {
timeout . LogFailure ( )
return OperationTimedOut { Path : di . path }
return OperationTimedOut { }
}
timeout . LogSuccess ( UTCNow ( ) . Sub ( start ) )
return nil
@ -202,7 +169,7 @@ func (di *distLockInstance) GetRLock(timeout *dynamicTimeout) (timedOutErr error
start := UTCNow ( )
if ! di . rwMutex . GetRLock ( di . opsID , lockSource , timeout . Timeout ( ) ) {
timeout . LogFailure ( )
return OperationTimedOut { Path : di . path }
return OperationTimedOut { }
}
timeout . LogSuccess ( UTCNow ( ) . Sub ( start ) )
return nil
@ -215,22 +182,26 @@ func (di *distLockInstance) RUnlock() {
// localLockInstance - frontend/top-level interface for namespace locks.
type localLockInstance struct {
ctx context . Context
ns * nsLockMap
volume , path , opsID string
ctx context . Context
ns * nsLockMap
volume string
paths [ ] string
opsID string
}
// NewNSLock - returns a lock instance for a given volume and
// path. The returned lockInstance object encapsulates the nsLockMap,
// volume, path and operation ID.
func ( n * nsLockMap ) NewNSLock ( ctx context . Context , lockersFn func ( ) [ ] dsync . NetLocker , volume , path string ) RWLocker {
func ( n * nsLockMap ) NewNSLock ( ctx context . Context , lockersFn func ( ) [ ] dsync . NetLocker , volume string , paths ... string ) RWLocker {
opsID := mustGetUUID ( )
if n . isDistXL {
return & distLockInstance { dsync . NewDRWMutex ( ctx , pathJoin ( volume , path ) , & dsync . Dsync {
drwmutex := dsync . NewDRWMutex ( ctx , & dsync . Dsync {
GetLockersFn : lockersFn ,
} ) , volume , path , opsID }
} , pathsJoinPrefix ( volume , paths ... ) ... )
return & distLockInstance { drwmutex , opsID }
}
return & localLockInstance { ctx , n , volume , path , opsID }
sort . Strings ( paths )
return & localLockInstance { ctx , n , volume , paths , opsID }
}
// Lock - block until write lock is taken or timeout has occurred.
@ -238,9 +209,16 @@ func (li *localLockInstance) GetLock(timeout *dynamicTimeout) (timedOutErr error
lockSource := getSource ( )
start := UTCNow ( )
readLock := false
if ! li . ns . lock ( li . ctx , li . volume , li . path , lockSource , li . opsID , readLock , timeout . Timeout ( ) ) {
timeout . LogFailure ( )
return OperationTimedOut { Path : li . path }
var success [ ] int
for i , path := range li . paths {
if ! li . ns . lock ( li . ctx , li . volume , path , lockSource , li . opsID , readLock , timeout . Timeout ( ) ) {
timeout . LogFailure ( )
for _ , sint := range success {
li . ns . unlock ( li . volume , li . paths [ sint ] , readLock )
}
return OperationTimedOut { }
}
success = append ( success , i )
}
timeout . LogSuccess ( UTCNow ( ) . Sub ( start ) )
return
@ -249,7 +227,9 @@ func (li *localLockInstance) GetLock(timeout *dynamicTimeout) (timedOutErr error
// Unlock - block until write lock is released.
func ( li * localLockInstance ) Unlock ( ) {
readLock := false
li . ns . unlock ( li . volume , li . path , readLock )
for _ , path := range li . paths {
li . ns . unlock ( li . volume , path , readLock )
}
}
// RLock - block until read lock is taken or timeout has occurred.
@ -257,9 +237,16 @@ func (li *localLockInstance) GetRLock(timeout *dynamicTimeout) (timedOutErr erro
lockSource := getSource ( )
start := UTCNow ( )
readLock := true
if ! li . ns . lock ( li . ctx , li . volume , li . path , lockSource , li . opsID , readLock , timeout . Timeout ( ) ) {
timeout . LogFailure ( )
return OperationTimedOut { Path : li . path }
var success [ ] int
for i , path := range li . paths {
if ! li . ns . lock ( li . ctx , li . volume , path , lockSource , li . opsID , readLock , timeout . Timeout ( ) ) {
timeout . LogFailure ( )
for _ , sint := range success {
li . ns . unlock ( li . volume , li . paths [ sint ] , readLock )
}
return OperationTimedOut { }
}
success = append ( success , i )
}
timeout . LogSuccess ( UTCNow ( ) . Sub ( start ) )
return
@ -268,7 +255,9 @@ func (li *localLockInstance) GetRLock(timeout *dynamicTimeout) (timedOutErr erro
// RUnlock - block until read lock is released.
func ( li * localLockInstance ) RUnlock ( ) {
readLock := true
li . ns . unlock ( li . volume , li . path , readLock )
for _ , path := range li . paths {
li . ns . unlock ( li . volume , path , readLock )
}
}
func getSource ( ) string {