| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/dsync" | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // lockRequesterInfo stores various info from the client for each lock that is requested.
 | 
					
						
							|  |  |  | type lockRequesterInfo struct { | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 	Name            string    // name of the resource lock was requested for
 | 
					
						
							|  |  |  | 	Writer          bool      // Bool whether write or read lock.
 | 
					
						
							|  |  |  | 	UID             string    // UID to uniquely identify request of client.
 | 
					
						
							|  |  |  | 	Timestamp       time.Time // Timestamp set at the time of initialization.
 | 
					
						
							|  |  |  | 	TimeLastRefresh time.Time // Timestamp for last lock refresh.
 | 
					
						
							|  |  |  | 	Source          string    // Contains line, function and filename reqesting the lock.
 | 
					
						
							|  |  |  | 	Group           bool      // indicates if it was a group lock.
 | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	// Owner represents the UUID of the owner who originally requested the lock
 | 
					
						
							|  |  |  | 	// useful in expiry.
 | 
					
						
							|  |  |  | 	Owner string | 
					
						
							| 
									
										
										
										
											2020-10-25 04:23:16 +08:00
										 |  |  | 	// Quorum represents the quorum required for this lock to be active.
 | 
					
						
							|  |  |  | 	Quorum int | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	idx    int | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // isWriteLock returns whether the lock is a write or read lock.
 | 
					
						
							|  |  |  | func isWriteLock(lri []lockRequesterInfo) bool { | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	return len(lri) == 1 && lri[0].Writer | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | // localLocker implements Dsync.NetLocker
 | 
					
						
							|  |  |  | type localLocker struct { | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 	mutex   sync.Mutex | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 	lockMap map[string][]lockRequesterInfo | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	lockUID map[string]string // UUID -> resource map.
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | func (l *localLocker) String() string { | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 	return globalEndpoints.Localhost() | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | func (l *localLocker) canTakeLock(resources ...string) bool { | 
					
						
							|  |  |  | 	for _, resource := range resources { | 
					
						
							|  |  |  | 		_, lockTaken := l.lockMap[resource] | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		if lockTaken { | 
					
						
							|  |  |  | 			return false | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	return true | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | func (l *localLocker) Lock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							| 
									
										
										
										
											2021-10-18 23:38:33 +08:00
										 |  |  | 	if len(args.Resources) > maxDeleteList { | 
					
						
							|  |  |  | 		return false, fmt.Errorf("internal error: localLocker.Lock called with more than %d resources", maxDeleteList) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 	l.mutex.Lock() | 
					
						
							|  |  |  | 	defer l.mutex.Unlock() | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	if !l.canTakeLock(args.Resources...) { | 
					
						
							|  |  |  | 		// Not all locks can be taken on resources,
 | 
					
						
							|  |  |  | 		// reject it completely.
 | 
					
						
							|  |  |  | 		return false, nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	// No locks held on the all resources, so claim write
 | 
					
						
							|  |  |  | 	// lock on all resources at once.
 | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	for i, resource := range args.Resources { | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 		l.lockMap[resource] = []lockRequesterInfo{ | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 				Name:            resource, | 
					
						
							|  |  |  | 				Writer:          true, | 
					
						
							|  |  |  | 				Source:          args.Source, | 
					
						
							|  |  |  | 				Owner:           args.Owner, | 
					
						
							|  |  |  | 				UID:             args.UID, | 
					
						
							|  |  |  | 				Timestamp:       UTCNow(), | 
					
						
							|  |  |  | 				TimeLastRefresh: UTCNow(), | 
					
						
							|  |  |  | 				Group:           len(args.Resources) > 1, | 
					
						
							|  |  |  | 				Quorum:          args.Quorum, | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 				idx:             i, | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		l.lockUID[formatUUID(args.UID, i)] = resource | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	return true, nil | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | func formatUUID(s string, idx int) string { | 
					
						
							|  |  |  | 	return s + strconv.Itoa(idx) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 17:11:29 +08:00
										 |  |  | func (l *localLocker) Unlock(_ context.Context, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							| 
									
										
										
										
											2021-10-18 23:38:33 +08:00
										 |  |  | 	if len(args.Resources) > maxDeleteList { | 
					
						
							|  |  |  | 		return false, fmt.Errorf("internal error: localLocker.Unlock called with more than %d resources", maxDeleteList) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 	l.mutex.Lock() | 
					
						
							|  |  |  | 	defer l.mutex.Unlock() | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	err = nil | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for _, resource := range args.Resources { | 
					
						
							| 
									
										
										
										
											2022-09-03 05:24:32 +08:00
										 |  |  | 		lri, ok := l.lockMap[resource] | 
					
						
							|  |  |  | 		if ok && !isWriteLock(lri) { | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 			// Unless it is a write lock reject it.
 | 
					
						
							|  |  |  | 			err = fmt.Errorf("unlock attempted on a read locked entity: %s", resource) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-10-27 01:29:29 +08:00
										 |  |  | 		if ok { | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 			reply = l.removeEntry(resource, args, &lri) || reply | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	return | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | // removeEntry based on the uid of the lock message, removes a single entry from the
 | 
					
						
							|  |  |  | // lockRequesterInfo array or the whole array from the map (in case of a write lock
 | 
					
						
							|  |  |  | // or last read lock)
 | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | // UID and optionally owner must match for entries to be deleted.
 | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | func (l *localLocker) removeEntry(name string, args dsync.LockArgs, lri *[]lockRequesterInfo) bool { | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	// Find correct entry to remove based on uid.
 | 
					
						
							|  |  |  | 	for index, entry := range *lri { | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		if entry.UID == args.UID && (args.Owner == "" || entry.Owner == args.Owner) { | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 			if len(*lri) == 1 { | 
					
						
							|  |  |  | 				// Remove the write lock.
 | 
					
						
							|  |  |  | 				delete(l.lockMap, name) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// Remove the appropriate read lock.
 | 
					
						
							|  |  |  | 				*lri = append((*lri)[:index], (*lri)[index+1:]...) | 
					
						
							|  |  |  | 				l.lockMap[name] = *lri | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 			delete(l.lockUID, formatUUID(args.UID, entry.idx)) | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// None found return false, perhaps entry removed in previous run.
 | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | func (l *localLocker) RLock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	if len(args.Resources) > 1 { | 
					
						
							|  |  |  | 		return false, fmt.Errorf("internal error: localLocker.RLock called with more than one resource") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 	l.mutex.Lock() | 
					
						
							|  |  |  | 	defer l.mutex.Unlock() | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 	resource := args.Resources[0] | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	lrInfo := lockRequesterInfo{ | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 		Name:            resource, | 
					
						
							|  |  |  | 		Writer:          false, | 
					
						
							|  |  |  | 		Source:          args.Source, | 
					
						
							|  |  |  | 		Owner:           args.Owner, | 
					
						
							|  |  |  | 		UID:             args.UID, | 
					
						
							|  |  |  | 		Timestamp:       UTCNow(), | 
					
						
							|  |  |  | 		TimeLastRefresh: UTCNow(), | 
					
						
							|  |  |  | 		Quorum:          args.Quorum, | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if lri, ok := l.lockMap[resource]; ok { | 
					
						
							|  |  |  | 		if reply = !isWriteLock(lri); reply { | 
					
						
							|  |  |  | 			// Unless there is a write lock
 | 
					
						
							|  |  |  | 			l.lockMap[resource] = append(l.lockMap[resource], lrInfo) | 
					
						
							| 
									
										
										
										
											2021-11-08 23:35:50 +08:00
										 |  |  | 			l.lockUID[formatUUID(args.UID, 0)] = resource | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		// No locks held on the given name, so claim (first) read lock
 | 
					
						
							|  |  |  | 		l.lockMap[resource] = []lockRequesterInfo{lrInfo} | 
					
						
							| 
									
										
										
										
											2021-11-08 23:35:50 +08:00
										 |  |  | 		l.lockUID[formatUUID(args.UID, 0)] = resource | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 		reply = true | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	return reply, nil | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 17:11:29 +08:00
										 |  |  | func (l *localLocker) RUnlock(_ context.Context, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	if len(args.Resources) > 1 { | 
					
						
							|  |  |  | 		return false, fmt.Errorf("internal error: localLocker.RUnlock called with more than one resource") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 	l.mutex.Lock() | 
					
						
							|  |  |  | 	defer l.mutex.Unlock() | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	var lri []lockRequesterInfo | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	resource := args.Resources[0] | 
					
						
							|  |  |  | 	if lri, reply = l.lockMap[resource]; !reply { | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 		// No lock is held on the given name
 | 
					
						
							| 
									
										
										
										
											2020-10-27 01:29:29 +08:00
										 |  |  | 		return true, nil | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	if isWriteLock(lri) { | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 		// A write-lock is held, cannot release a read lock
 | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		return false, fmt.Errorf("RUnlock attempted on a write locked entity: %s", resource) | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-10-27 01:29:29 +08:00
										 |  |  | 	l.removeEntry(resource, args, &lri) | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	return reply, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-04 12:23:24 +08:00
										 |  |  | type lockStats struct { | 
					
						
							|  |  |  | 	Total  int | 
					
						
							|  |  |  | 	Writes int | 
					
						
							|  |  |  | 	Reads  int | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *localLocker) stats() lockStats { | 
					
						
							|  |  |  | 	l.mutex.Lock() | 
					
						
							|  |  |  | 	defer l.mutex.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	st := lockStats{Total: len(l.lockMap)} | 
					
						
							|  |  |  | 	for _, v := range l.lockMap { | 
					
						
							|  |  |  | 		if len(v) == 0 { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		entry := v[0] | 
					
						
							|  |  |  | 		if entry.Writer { | 
					
						
							|  |  |  | 			st.Writes++ | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			st.Reads += len(v) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return st | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | func (l *localLocker) DupLockMap() map[string][]lockRequesterInfo { | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 	l.mutex.Lock() | 
					
						
							|  |  |  | 	defer l.mutex.Unlock() | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	lockCopy := make(map[string][]lockRequesterInfo, len(l.lockMap)) | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	for k, v := range l.lockMap { | 
					
						
							| 
									
										
										
										
											2023-04-04 12:23:24 +08:00
										 |  |  | 		if len(v) == 0 { | 
					
						
							|  |  |  | 			delete(l.lockMap, k) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		lockCopy[k] = append(make([]lockRequesterInfo, 0, len(v)), v...) | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return lockCopy | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (l *localLocker) Close() error { | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 03:32:32 +08:00
										 |  |  | // IsOnline - local locker is always online.
 | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | func (l *localLocker) IsOnline() bool { | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 03:32:32 +08:00
										 |  |  | // IsLocal - local locker returns true.
 | 
					
						
							|  |  |  | func (l *localLocker) IsLocal() bool { | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | func (l *localLocker) ForceUnlock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 	select { | 
					
						
							|  |  |  | 	case <-ctx.Done(): | 
					
						
							|  |  |  | 		return false, ctx.Err() | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		l.mutex.Lock() | 
					
						
							|  |  |  | 		defer l.mutex.Unlock() | 
					
						
							|  |  |  | 		if len(args.UID) == 0 { | 
					
						
							|  |  |  | 			for _, resource := range args.Resources { | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 				lris, ok := l.lockMap[resource] | 
					
						
							|  |  |  | 				if !ok { | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				// Collect uids, so we don't mutate while we delete
 | 
					
						
							|  |  |  | 				uids := make([]string, 0, len(lris)) | 
					
						
							|  |  |  | 				for _, lri := range lris { | 
					
						
							|  |  |  | 					uids = append(uids, lri.UID) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// Delete collected uids:
 | 
					
						
							|  |  |  | 				for _, uid := range uids { | 
					
						
							|  |  |  | 					lris, ok := l.lockMap[resource] | 
					
						
							|  |  |  | 					if !ok { | 
					
						
							|  |  |  | 						// Just to be safe, delete uuids.
 | 
					
						
							|  |  |  | 						for idx := 0; idx < maxDeleteList; idx++ { | 
					
						
							|  |  |  | 							mapID := formatUUID(uid, idx) | 
					
						
							|  |  |  | 							if _, ok := l.lockUID[mapID]; !ok { | 
					
						
							|  |  |  | 								break | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							delete(l.lockUID, mapID) | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 						continue | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 					l.removeEntry(resource, dsync.LockArgs{UID: uid}, &lris) | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-08-27 23:59:36 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 			return true, nil | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-27 23:59:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 		idx := 0 | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			mapID := formatUUID(args.UID, idx) | 
					
						
							|  |  |  | 			resource, ok := l.lockUID[mapID] | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				return idx > 0, nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			lris, ok := l.lockMap[resource] | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				// Unexpected  inconsistency, delete.
 | 
					
						
							|  |  |  | 				delete(l.lockUID, mapID) | 
					
						
							|  |  |  | 				idx++ | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			reply = true | 
					
						
							|  |  |  | 			l.removeEntry(resource, dsync.LockArgs{UID: args.UID}, &lris) | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 			idx++ | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | func (l *localLocker) Refresh(ctx context.Context, args dsync.LockArgs) (refreshed bool, err error) { | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 	select { | 
					
						
							|  |  |  | 	case <-ctx.Done(): | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | 		return false, ctx.Err() | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		l.mutex.Lock() | 
					
						
							|  |  |  | 		defer l.mutex.Unlock() | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 		// Check whether uid is still active.
 | 
					
						
							|  |  |  | 		resource, ok := l.lockUID[formatUUID(args.UID, 0)] | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		if !ok { | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 			return false, nil | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 		idx := 0 | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			lris, ok := l.lockMap[resource] | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				// Inconsistent. Delete UID.
 | 
					
						
							|  |  |  | 				delete(l.lockUID, formatUUID(args.UID, idx)) | 
					
						
							|  |  |  | 				return idx > 0, nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			for i := range lris { | 
					
						
							|  |  |  | 				if lris[i].UID == args.UID { | 
					
						
							|  |  |  | 					lris[i].TimeLastRefresh = UTCNow() | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			idx++ | 
					
						
							|  |  |  | 			resource, ok = l.lockUID[formatUUID(args.UID, idx)] | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				// No more resources for UID, but we did update at least one.
 | 
					
						
							|  |  |  | 				return true, nil | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-02-01 01:41:17 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Similar to removeEntry but only removes an entry only if the lock entry exists in map.
 | 
					
						
							|  |  |  | // Caller must hold 'l.mutex' lock.
 | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | func (l *localLocker) expireOldLocks(interval time.Duration) { | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 	l.mutex.Lock() | 
					
						
							|  |  |  | 	defer l.mutex.Unlock() | 
					
						
							| 
									
										
										
										
											2020-10-27 01:29:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-28 06:10:57 +08:00
										 |  |  | 	for k, lris := range l.lockMap { | 
					
						
							|  |  |  | 		modified := false | 
					
						
							|  |  |  | 		for i := 0; i < len(lris); { | 
					
						
							|  |  |  | 			lri := &lris[i] | 
					
						
							|  |  |  | 			if time.Since(lri.TimeLastRefresh) > interval { | 
					
						
							|  |  |  | 				delete(l.lockUID, formatUUID(lri.UID, lri.idx)) | 
					
						
							|  |  |  | 				if len(lris) == 1 { | 
					
						
							|  |  |  | 					// Remove the write lock.
 | 
					
						
							|  |  |  | 					delete(l.lockMap, lri.Name) | 
					
						
							|  |  |  | 					modified = false | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-01-28 06:10:57 +08:00
										 |  |  | 				modified = true | 
					
						
							|  |  |  | 				// Remove the appropriate lock.
 | 
					
						
							|  |  |  | 				lris = append(lris[:i], lris[i+1:]...) | 
					
						
							|  |  |  | 				// Check same i
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// Move to next
 | 
					
						
							|  |  |  | 				i++ | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-01-28 06:10:57 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if modified { | 
					
						
							|  |  |  | 			l.lockMap[k] = lris | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | func newLocker() *localLocker { | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	return &localLocker{ | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		lockMap: make(map[string][]lockRequesterInfo, 1000), | 
					
						
							|  |  |  | 		lockUID: make(map[string]string, 1000), | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } |