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  }