github.com/gitbookio/syncgroup@v0.0.0-20181003125046-3e73b2e6a972/conds.go (about)

     1  package syncgroup
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  )
     7  
     8  type CondGroup struct {
     9  	groups map[string]*sync.Cond
    10  	lock   *sync.Mutex
    11  }
    12  
    13  func NewCondGroup() *CondGroup {
    14  	return &CondGroup{
    15  		groups: map[string]*sync.Cond{},
    16  		lock:   &sync.Mutex{},
    17  	}
    18  }
    19  
    20  // Lock returns true if the caller is the first to lock for this key
    21  func (cg *CondGroup) Lock(key string) (first bool) {
    22  	// Lock to prevent race conditions
    23  	cg.lock.Lock()
    24  
    25  	// Check if we have an existing group
    26  	if cond, ok := cg.groups[key]; ok {
    27  		// Unlock so other callers can come in
    28  		cg.lock.Unlock()
    29  		cond.L.Lock()
    30  		cond.Wait()
    31  		cond.L.Unlock()
    32  		return false
    33  	}
    34  	// Unlock when we're finished setting up the new cond
    35  	defer cg.lock.Unlock()
    36  
    37  	// So we're the first caller
    38  	cond := sync.NewCond(&sync.Mutex{})
    39  
    40  	// Add cond for group
    41  	cg.groups[key] = cond
    42  
    43  	return true
    44  }
    45  
    46  // Unlock should only be called by the original caller of "Lock"
    47  // (that got the "true" return value)
    48  // All subsequent callers to "Lock" are now unpaused
    49  func (cg *CondGroup) Unlock(key string) error {
    50  	cg.lock.Lock()
    51  	defer cg.lock.Unlock()
    52  
    53  	cond, ok := cg.groups[key]
    54  	if !ok {
    55  		return fmt.Errorf("Can not unlock group %s: no lock exists", key)
    56  	}
    57  
    58  	// Free all wailting funcs
    59  	cond.Broadcast()
    60  
    61  	// Delete cond
    62  	delete(cg.groups, key)
    63  
    64  	return nil
    65  }