// ForceUnlock - forcefully unlock a lock based on name.
func (n *nsLockMap) ForceUnlock(volume, path string) {
	n.lockMapMutex.Lock()
	defer n.lockMapMutex.Unlock()

	// Clarification on operation:
	// - In case of FS or XL we call ForceUnlock on the local nsMutex
	//   (since there is only a single server) which will cause the 'stuck'
	//   mutex to be removed from the map. Existing operations for this
	//   will continue to be blocked (and timeout). New operations on this
	//   resource will use a new mutex and proceed normally.
	//
	// - In case of Distributed setup (using dsync), there is no need to call
	//   ForceUnlock on the server where the lock was acquired and is presumably
	//   'stuck'. Instead dsync.ForceUnlock() will release the underlying locks
	//   that participated in granting the lock. Any pending dsync locks that
	//   are blocking can now proceed as normal and any new locks will also
	//   participate normally.

	if n.isDist { // For distributed mode, broadcast ForceUnlock message.
		dsync.NewDRWMutex(pathutil.Join(volume, path)).ForceUnlock()
	}

	param := nsParam{volume, path}
	if _, found := n.lockMap[param]; found {
		// Remove lock from the map.
		delete(n.lockMap, param)

		// delete the lock state entry for given
		// <volume, path> pair.
		err := n.deleteLockInfoEntryForVolumePath(param)
		if err != nil {
			errorIf(err, "Failed to delete lock info entry")
		}
	}
}
// Lock the namespace resource.
func (n *nsLockMap) lock(volume, path string, lockOrigin, opsID string, readLock bool) {
	var nsLk *nsLock
	n.lockMapMutex.Lock()

	param := nsParam{volume, path}
	nsLk, found := n.lockMap[param]
	if !found {
		nsLk = &nsLock{
			RWLocker: func() RWLocker {
				if n.isDist {
					return dsync.NewDRWMutex(pathutil.Join(volume, path))
				}
				return &sync.RWMutex{}
			}(),
			ref: 0,
		}
		n.lockMap[param] = nsLk
	}
	nsLk.ref++ // Update ref count here to avoid multiple races.

	if globalDebugLock {
		// change the state of the lock to be  blocked for the given pair of <volume, path> and <OperationID> till the lock unblocks.
		// The lock for accessing `nsMutex` is held inside the function itself.
		err := n.statusNoneToBlocked(param, lockOrigin, opsID, readLock)
		if err != nil {
			errorIf(err, "Failed to set lock state to blocked.")
		}
	}
	// Unlock map before Locking NS which might block.
	n.lockMapMutex.Unlock()

	// Locking here can block.
	if readLock {
		nsLk.RLock()
	} else {
		nsLk.Lock()
	}

	// check if lock debugging enabled.
	if globalDebugLock {
		// Changing the status of the operation from blocked to running.
		// change the state of the lock to be  running (from blocked) for the given pair of <volume, path> and <OperationID>.
		err := n.statusBlockedToRunning(param, lockOrigin, opsID, readLock)
		if err != nil {
			errorIf(err, "Failed to set the lock state to running.")
		}
	}
}