github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/libkb/locktab.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package libkb
     5  
     6  import (
     7  	"sync"
     8  	"time"
     9  
    10  	"golang.org/x/net/context"
    11  )
    12  
    13  type NamedLock struct {
    14  	sync.Mutex
    15  	lctx   VLogContext
    16  	refs   int
    17  	name   string
    18  	parent *LockTable
    19  }
    20  
    21  func (l *NamedLock) incref() {
    22  	l.refs++
    23  }
    24  
    25  func (l *NamedLock) decref() {
    26  	l.refs--
    27  }
    28  
    29  func (l *NamedLock) Release(ctx context.Context) {
    30  	l.lctx.GetVDebugLog().CLogf(ctx, VLog1, "+ LockTable.Release(%s)", l.name)
    31  	l.Unlock()
    32  	l.parent.Lock()
    33  	l.decref()
    34  	if l.refs == 0 {
    35  		l.lctx.GetVDebugLog().CLogf(ctx, VLog1, "| LockTable.unref(%s)", l.name)
    36  		delete(l.parent.locks, l.name)
    37  	}
    38  	l.parent.Unlock()
    39  	l.lctx.GetVDebugLog().CLogf(ctx, VLog1, "- LockTable.Unlock(%s)", l.name)
    40  }
    41  
    42  type LockTable struct {
    43  	sync.Mutex
    44  	locks map[string]*NamedLock
    45  }
    46  
    47  func NewLockTable() *LockTable {
    48  	return &LockTable{}
    49  }
    50  
    51  func (t *LockTable) init() {
    52  	if t.locks == nil {
    53  		t.locks = make(map[string]*NamedLock)
    54  	}
    55  }
    56  
    57  // AcquireOnName acquires s's lock.
    58  // Never gives up.
    59  func (t *LockTable) AcquireOnName(ctx context.Context, g VLogContext, s string) (ret *NamedLock) {
    60  	g.GetVDebugLog().CLogf(ctx, VLog1, "+ LockTable.Lock(%s)", s)
    61  	t.Lock()
    62  	t.init()
    63  	if ret = t.locks[s]; ret == nil {
    64  		ret = &NamedLock{lctx: g, refs: 0, name: s, parent: t}
    65  		t.locks[s] = ret
    66  	}
    67  	ret.incref()
    68  	t.Unlock()
    69  	ret.Lock()
    70  	g.GetVDebugLog().CLogf(ctx, VLog1, "- LockTable.Lock(%s)", s)
    71  	return ret
    72  }
    73  
    74  // AcquireOnNameWithContext acquires s's lock.
    75  // Returns (ret, nil) if the lock was acquired.
    76  // Returns (nil, err) if it was not. The error is from ctx.Err().
    77  func (t *LockTable) AcquireOnNameWithContext(ctx context.Context, g VLogContext, s string) (ret *NamedLock, err error) {
    78  	g.GetVDebugLog().CLogf(ctx, VLog1, "+ LockTable.Lock(%s)", s)
    79  	err = AcquireWithContext(ctx, t)
    80  	if err != nil {
    81  		g.GetVDebugLog().CLogf(ctx, VLog1, "- LockTable.Lock(%s) outer canceled: %v", s, err)
    82  		return nil, err
    83  	}
    84  	t.init()
    85  	if ret = t.locks[s]; ret == nil {
    86  		ret = &NamedLock{lctx: g, refs: 0, name: s, parent: t}
    87  		t.locks[s] = ret
    88  	}
    89  	ret.incref()
    90  	t.Unlock()
    91  	err = AcquireWithContext(ctx, ret)
    92  	if err != nil {
    93  		g.GetVDebugLog().CLogf(ctx, VLog1, "- LockTable.Lock(%s) inner canceled: %v", s, err)
    94  		return nil, err
    95  	}
    96  	g.GetVDebugLog().CLogf(ctx, VLog1, "- LockTable.Lock(%s)", s)
    97  	return ret, nil
    98  }
    99  
   100  // AcquireOnNameWithContextAndTimeout acquires s's lock.
   101  // Returns (ret, nil) if the lock was acquired.
   102  // Returns (nil, err) if it was not. The error is from ctx.Err() or context.DeadlineExceeded.
   103  func (t *LockTable) AcquireOnNameWithContextAndTimeout(ctx context.Context, g VLogContext, s string, timeout time.Duration) (ret *NamedLock, err error) {
   104  	ctx2, cancel := context.WithTimeout(ctx, timeout)
   105  	defer cancel()
   106  	return t.AcquireOnNameWithContext(ctx2, g, s)
   107  }