@ -49,6 +49,7 @@ type DRWMutex struct {
writeLocks [ ] string // Array of nodes that granted a write lock
writeLocks [ ] string // Array of nodes that granted a write lock
readersLocks [ ] [ ] string // Array of array of nodes that granted reader locks
readersLocks [ ] [ ] string // Array of array of nodes that granted reader locks
m sync . Mutex // Mutex to prevent multiple simultaneous locks from this node
m sync . Mutex // Mutex to prevent multiple simultaneous locks from this node
clnt * Dsync
}
}
// Granted - represents a structure of a granted lock.
// Granted - represents a structure of a granted lock.
@ -66,10 +67,11 @@ func isLocked(uid string) bool {
}
}
// NewDRWMutex - initializes a new dsync RW mutex.
// NewDRWMutex - initializes a new dsync RW mutex.
func NewDRWMutex ( name string ) * DRWMutex {
func NewDRWMutex ( name string , clnt * Dsync ) * DRWMutex {
return & DRWMutex {
return & DRWMutex {
Name : name ,
Name : name ,
writeLocks : make ( [ ] string , dnodeCount ) ,
writeLocks : make ( [ ] string , clnt . dNodeCount ) ,
clnt : clnt ,
}
}
}
}
@ -128,10 +130,10 @@ func (dm *DRWMutex) lockBlocking(timeout time.Duration, isReadLock bool) (locked
// Use incremental back-off algorithm for repeated attempts to acquire the lock
// Use incremental back-off algorithm for repeated attempts to acquire the lock
for range newRetryTimerSimple ( doneCh ) {
for range newRetryTimerSimple ( doneCh ) {
// Create temp array on stack.
// Create temp array on stack.
locks := make ( [ ] string , dn odeCount )
locks := make ( [ ] string , dm . clnt . dN odeCount )
// Try to acquire the lock.
// Try to acquire the lock.
success := lock ( clnts , & locks , dm . Name , isReadLock )
success := lock ( dm . clnt , & locks , dm . Name , isReadLock )
if success {
if success {
dm . m . Lock ( )
dm . m . Lock ( )
defer dm . m . Unlock ( )
defer dm . m . Unlock ( )
@ -139,7 +141,7 @@ func (dm *DRWMutex) lockBlocking(timeout time.Duration, isReadLock bool) (locked
// If success, copy array to object
// If success, copy array to object
if isReadLock {
if isReadLock {
// Append new array of strings at the end
// Append new array of strings at the end
dm . readersLocks = append ( dm . readersLocks , make ( [ ] string , dn odeCount ) )
dm . readersLocks = append ( dm . readersLocks , make ( [ ] string , dm . clnt . dN odeCount ) )
// and copy stack array into last spot
// and copy stack array into last spot
copy ( dm . readersLocks [ len ( dm . readersLocks ) - 1 ] , locks [ : ] )
copy ( dm . readersLocks [ len ( dm . readersLocks ) - 1 ] , locks [ : ] )
} else {
} else {
@ -158,14 +160,14 @@ func (dm *DRWMutex) lockBlocking(timeout time.Duration, isReadLock bool) (locked
}
}
// lock tries to acquire the distributed lock, returning true or false.
// lock tries to acquire the distributed lock, returning true or false.
func lock ( clnts [ ] NetLocker , locks * [ ] string , lockName string , isReadLock bool ) bool {
func lock ( ds * Dsync , locks * [ ] string , lockName string , isReadLock bool ) bool {
// Create buffered channel of size equal to total number of nodes.
// Create buffered channel of size equal to total number of nodes.
ch := make ( chan Granted , dn odeCount )
ch := make ( chan Granted , ds . dN odeCount )
defer close ( ch )
defer close ( ch )
var wg sync . WaitGroup
var wg sync . WaitGroup
for index , c := range c lnts {
for index , c := range ds . rpcC lnts {
wg . Add ( 1 )
wg . Add ( 1 )
// broadcast lock request to all nodes
// broadcast lock request to all nodes
@ -181,8 +183,8 @@ func lock(clnts []NetLocker, locks *[]string, lockName string, isReadLock bool)
args := LockArgs {
args := LockArgs {
UID : uid ,
UID : uid ,
Resource : lockName ,
Resource : lockName ,
ServerAddr : cl nts[ ownNode ] . ServerAddr ( ) ,
ServerAddr : ds . rpcCl nts[ ds . ownNode ] . ServerAddr ( ) ,
ServiceEndpoint : cl nts[ ownNode ] . ServiceEndpoint ( ) ,
ServiceEndpoint : ds . rpcCl nts[ ds . ownNode ] . ServiceEndpoint ( ) ,
}
}
var locked bool
var locked bool
@ -222,7 +224,7 @@ func lock(clnts []NetLocker, locks *[]string, lockName string, isReadLock bool)
done := false
done := false
timeout := time . After ( DRWMutexAcquireTimeout )
timeout := time . After ( DRWMutexAcquireTimeout )
for ; i < dn odeCount ; i ++ { // Loop until we acquired all locks
for ; i < ds . dN odeCount ; i ++ { // Loop until we acquired all locks
select {
select {
case grant := <- ch :
case grant := <- ch :
@ -231,22 +233,22 @@ func lock(clnts []NetLocker, locks *[]string, lockName string, isReadLock bool)
( * locks ) [ grant . index ] = grant . lockUID
( * locks ) [ grant . index ] = grant . lockUID
} else {
} else {
locksFailed ++
locksFailed ++
if ! isReadLock && locksFailed > dn odeCount - dquorum ||
if ! isReadLock && locksFailed > ds . dN odeCount - ds . dquorum ||
isReadLock && locksFailed > dn odeCount - dquorumReads {
isReadLock && locksFailed > ds . dN odeCount - ds . dquorumReads {
// We know that we are not going to get the lock anymore,
// We know that we are not going to get the lock anymore,
// so exit out and release any locks that did get acquired
// so exit out and release any locks that did get acquired
done = true
done = true
// Increment the number of grants received from the buffered channel.
// Increment the number of grants received from the buffered channel.
i ++
i ++
releaseAll ( clnt s, locks , lockName , isReadLock )
releaseAll ( d s, locks , lockName , isReadLock )
}
}
}
}
case <- timeout :
case <- timeout :
done = true
done = true
// timeout happened, maybe one of the nodes is slow, count
// timeout happened, maybe one of the nodes is slow, count
// number of locks to check whether we have quorum or not
// number of locks to check whether we have quorum or not
if ! quorumMet ( locks , isReadLock ) {
if ! quorumMet ( locks , isReadLock , ds . dquorum , ds . dquorumReads ) {
releaseAll ( clnt s, locks , lockName , isReadLock )
releaseAll ( d s, locks , lockName , isReadLock )
}
}
}
}
@ -256,7 +258,7 @@ func lock(clnts []NetLocker, locks *[]string, lockName string, isReadLock bool)
}
}
// Count locks in order to determine whether we have quorum or not
// Count locks in order to determine whether we have quorum or not
quorum = quorumMet ( locks , isReadLock )
quorum = quorumMet ( locks , isReadLock , ds . dquorum , ds . dquorumReads )
// Signal that we have the quorum
// Signal that we have the quorum
wg . Done ( )
wg . Done ( )
@ -264,11 +266,11 @@ func lock(clnts []NetLocker, locks *[]string, lockName string, isReadLock bool)
// Wait for the other responses and immediately release the locks
// Wait for the other responses and immediately release the locks
// (do not add them to the locks array because the DRWMutex could
// (do not add them to the locks array because the DRWMutex could
// already has been unlocked again by the original calling thread)
// already has been unlocked again by the original calling thread)
for ; i < dn odeCount ; i ++ {
for ; i < ds . dN odeCount ; i ++ {
grantToBeReleased := <- ch
grantToBeReleased := <- ch
if grantToBeReleased . isLocked ( ) {
if grantToBeReleased . isLocked ( ) {
// release lock
// release lock
sendRelease ( c lnts[ grantToBeReleased . index ] , lockName , grantToBeReleased . lockUID , isReadLock )
sendRelease ( ds , ds . rpcC lnts[ grantToBeReleased . index ] , lockName , grantToBeReleased . lockUID , isReadLock )
}
}
}
}
} ( isReadLock )
} ( isReadLock )
@ -276,9 +278,9 @@ func lock(clnts []NetLocker, locks *[]string, lockName string, isReadLock bool)
wg . Wait ( )
wg . Wait ( )
// Verify that localhost server is actively participating in the lock (the lock maintenance relies on this fact)
// Verify that localhost server is actively participating in the lock (the lock maintenance relies on this fact)
if quorum && ! isLocked ( ( * locks ) [ ownNode ] ) {
if quorum && ! isLocked ( ( * locks ) [ ds . ownNode ] ) {
// If not, release lock (and try again later)
// If not, release lock (and try again later)
releaseAll ( clnt s, locks , lockName , isReadLock )
releaseAll ( d s, locks , lockName , isReadLock )
quorum = false
quorum = false
}
}
@ -286,7 +288,7 @@ func lock(clnts []NetLocker, locks *[]string, lockName string, isReadLock bool)
}
}
// quorumMet determines whether we have acquired the required quorum of underlying locks or not
// quorumMet determines whether we have acquired the required quorum of underlying locks or not
func quorumMet ( locks * [ ] string , isReadLock bool ) bool {
func quorumMet ( locks * [ ] string , isReadLock bool , quorum , quorumReads int ) bool {
count := 0
count := 0
for _ , uid := range * locks {
for _ , uid := range * locks {
@ -295,21 +297,21 @@ func quorumMet(locks *[]string, isReadLock bool) bool {
}
}
}
}
var q uorum bool
var metQ uorum bool
if isReadLock {
if isReadLock {
q uorum = count >= d quorumReads
metQ uorum = count >= quorumReads
} else {
} else {
q uorum = count >= d quorum
metQ uorum = count >= quorum
}
}
return q uorum
return metQ uorum
}
}
// releaseAll releases all locks that are marked as locked
// releaseAll releases all locks that are marked as locked
func releaseAll ( clnts [ ] NetLocker , locks * [ ] string , lockName string , isReadLock bool ) {
func releaseAll ( ds * Dsync , locks * [ ] string , lockName string , isReadLock bool ) {
for lock := 0 ; lock < dn odeCount ; lock ++ {
for lock := 0 ; lock < ds . dN odeCount ; lock ++ {
if isLocked ( ( * locks ) [ lock ] ) {
if isLocked ( ( * locks ) [ lock ] ) {
sendRelease ( c lnts[ lock ] , lockName , ( * locks ) [ lock ] , isReadLock )
sendRelease ( ds , ds . rpcC lnts[ lock ] , lockName , ( * locks ) [ lock ] , isReadLock )
( * locks ) [ lock ] = ""
( * locks ) [ lock ] = ""
}
}
}
}
@ -321,7 +323,7 @@ func releaseAll(clnts []NetLocker, locks *[]string, lockName string, isReadLock
func ( dm * DRWMutex ) Unlock ( ) {
func ( dm * DRWMutex ) Unlock ( ) {
// create temp array on stack
// create temp array on stack
locks := make ( [ ] string , dn odeCount )
locks := make ( [ ] string , dm . clnt . dN odeCount )
{
{
dm . m . Lock ( )
dm . m . Lock ( )
@ -342,11 +344,11 @@ func (dm *DRWMutex) Unlock() {
// Copy write locks to stack array
// Copy write locks to stack array
copy ( locks , dm . writeLocks [ : ] )
copy ( locks , dm . writeLocks [ : ] )
// Clear write locks array
// Clear write locks array
dm . writeLocks = make ( [ ] string , dn odeCount )
dm . writeLocks = make ( [ ] string , dm . clnt . dN odeCount )
}
}
isReadLock := false
isReadLock := false
unlock ( locks , dm . Name , isReadLock )
unlock ( dm . clnt , locks , dm . Name , isReadLock )
}
}
// RUnlock releases a read lock held on dm.
// RUnlock releases a read lock held on dm.
@ -355,7 +357,7 @@ func (dm *DRWMutex) Unlock() {
func ( dm * DRWMutex ) RUnlock ( ) {
func ( dm * DRWMutex ) RUnlock ( ) {
// create temp array on stack
// create temp array on stack
locks := make ( [ ] string , dn odeCount )
locks := make ( [ ] string , dm . clnt . dN odeCount )
{
{
dm . m . Lock ( )
dm . m . Lock ( )
@ -370,19 +372,19 @@ func (dm *DRWMutex) RUnlock() {
}
}
isReadLock := true
isReadLock := true
unlock ( locks , dm . Name , isReadLock )
unlock ( dm . clnt , locks , dm . Name , isReadLock )
}
}
func unlock ( locks [ ] string , name string , isReadLock bool ) {
func unlock ( ds * Dsync , locks [ ] string , name string , isReadLock bool ) {
// We don't need to synchronously wait until we have released all the locks (or the quorum)
// We don't need to synchronously wait until we have released all the locks (or the quorum)
// (a subsequent lock will retry automatically in case it would fail to get quorum)
// (a subsequent lock will retry automatically in case it would fail to get quorum)
for index , c := range c lnts {
for index , c := range ds . rpcC lnts {
if isLocked ( locks [ index ] ) {
if isLocked ( locks [ index ] ) {
// broadcast lock release to all nodes that granted the lock
// broadcast lock release to all nodes that granted the lock
sendRelease ( c , name , locks [ index ] , isReadLock )
sendRelease ( ds , c , name , locks [ index ] , isReadLock )
}
}
}
}
}
}
@ -394,24 +396,24 @@ func (dm *DRWMutex) ForceUnlock() {
defer dm . m . Unlock ( )
defer dm . m . Unlock ( )
// Clear write locks array
// Clear write locks array
dm . writeLocks = make ( [ ] string , dn odeCount )
dm . writeLocks = make ( [ ] string , dm . clnt . dN odeCount )
// Clear read locks array
// Clear read locks array
dm . readersLocks = nil
dm . readersLocks = nil
}
}
for _ , c := range c lnts {
for _ , c := range dm . clnt . rpcC lnts {
// broadcast lock release to all nodes that granted the lock
// broadcast lock release to all nodes that granted the lock
sendRelease ( c , dm . Name , "" , false )
sendRelease ( dm . clnt , c , dm . Name , "" , false )
}
}
}
}
// sendRelease sends a release message to a node that previously granted a lock
// sendRelease sends a release message to a node that previously granted a lock
func sendRelease ( c NetLocker , name , uid string , isReadLock bool ) {
func sendRelease ( ds * Dsync , c NetLocker , name , uid string , isReadLock bool ) {
args := LockArgs {
args := LockArgs {
UID : uid ,
UID : uid ,
Resource : name ,
Resource : name ,
ServerAddr : cl nts[ ownNode ] . ServerAddr ( ) ,
ServerAddr : ds . rpcCl nts[ ds . ownNode ] . ServerAddr ( ) ,
ServiceEndpoint : cl nts[ ownNode ] . ServiceEndpoint ( ) ,
ServiceEndpoint : ds . rpcCl nts[ ds . ownNode ] . ServiceEndpoint ( ) ,
}
}
if len ( uid ) == 0 {
if len ( uid ) == 0 {
if _ , err := c . ForceUnlock ( args ) ; err != nil {
if _ , err := c . ForceUnlock ( args ) ; err != nil {