github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/shared-lock.go (about) 1 // Copyright (c) 2015-2022 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "context" 22 "time" 23 ) 24 25 var sharedLockTimeout = newDynamicTimeoutWithOpts(dynamicTimeoutOpts{ 26 timeout: 30 * time.Second, 27 minimum: 10 * time.Second, 28 retryInterval: time.Minute, 29 }) 30 31 type sharedLock struct { 32 lockContext chan LockContext 33 } 34 35 func (ld sharedLock) backgroundRoutine(ctx context.Context, objAPI ObjectLayer, lockName string) { 36 for { 37 locker := objAPI.NewNSLock(minioMetaBucket, lockName) 38 lkctx, err := locker.GetLock(ctx, sharedLockTimeout) 39 if err != nil { 40 continue 41 } 42 43 keepLock: 44 for { 45 select { 46 case <-ctx.Done(): 47 return 48 case <-lkctx.Context().Done(): 49 // The context of the lock is canceled, this can happen 50 // if one lock lost quorum due to cluster instability 51 // in that case, try to lock again. 52 break keepLock 53 case ld.lockContext <- lkctx: 54 // Send the lock context to anyone asking for it 55 } 56 } 57 } 58 } 59 60 func mergeContext(ctx1, ctx2 context.Context) (context.Context, context.CancelFunc) { 61 ctx, cancel := context.WithCancel(context.Background()) 62 go func() { 63 select { 64 case <-ctx1.Done(): 65 case <-ctx2.Done(): 66 // The lock acquirer decides to cancel, exit this goroutine 67 case <-ctx.Done(): 68 } 69 70 cancel() 71 }() 72 return ctx, cancel 73 } 74 75 func (ld sharedLock) GetLock(ctx context.Context) (context.Context, context.CancelFunc) { 76 l := <-ld.lockContext 77 return mergeContext(l.Context(), ctx) 78 } 79 80 func newSharedLock(ctx context.Context, objAPI ObjectLayer, lockName string) *sharedLock { 81 l := &sharedLock{ 82 lockContext: make(chan LockContext), 83 } 84 go l.backgroundRoutine(ctx, objAPI, lockName) 85 86 return l 87 }