github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/sync/ctxsync/mutex.go (about)

     1  // Copyright 2022 GRAIL, Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache 2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  package ctxsync
     6  
     7  import (
     8  	"context"
     9  	"sync"
    10  
    11  	"github.com/Schaudge/grailbase/errors"
    12  )
    13  
    14  // Mutex is a context-aware mutex.  It must not be copied.
    15  // The zero value is ready to use.
    16  type Mutex struct {
    17  	initOnce sync.Once
    18  	lockCh   chan struct{}
    19  }
    20  
    21  // Lock attempts to exclusively lock m.  If the m is already locked, it will
    22  // wait until it is unlocked.  If ctx is canceled before the lock can be taken,
    23  // Lock will not take the lock, and a non-nil error is returned.
    24  func (m *Mutex) Lock(ctx context.Context) error {
    25  	m.init()
    26  	select {
    27  	case m.lockCh <- struct{}{}:
    28  		return nil
    29  	case <-ctx.Done():
    30  		return errors.E(ctx.Err(), "waiting for lock")
    31  	}
    32  }
    33  
    34  // Unlock unlocks m.  It must be called exactly once iff Lock returns nil.
    35  // Unlock panics if it is called while m is not locked.
    36  func (m *Mutex) Unlock() {
    37  	m.init()
    38  	select {
    39  	case <-m.lockCh:
    40  	default:
    41  		panic("Unlock called on mutex that is not locked")
    42  	}
    43  }
    44  
    45  func (m *Mutex) init() {
    46  	m.initOnce.Do(func() {
    47  		m.lockCh = make(chan struct{}, 1)
    48  	})
    49  }