github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/repo/onlyone.go (about)

     1  package repo
     2  
     3  import (
     4  	"sync"
     5  )
     6  
     7  // OnlyOne tracks open Repos by arbitrary key and returns the already
     8  // open one.
     9  type OnlyOne struct {
    10  	mu     sync.Mutex
    11  	active map[interface{}]*ref
    12  }
    13  
    14  // Open a Repo identified by key. If Repo is not already open, the
    15  // open function is called, and the result is remember for further
    16  // use.
    17  //
    18  // Key must be comparable, or Open will panic. Make sure to pick keys
    19  // that are unique across different concrete Repo implementations,
    20  // e.g. by creating a local type:
    21  //
    22  //     type repoKey string
    23  //     r, err := o.Open(repoKey(path), open)
    24  //
    25  // Call Repo.Close when done.
    26  func (o *OnlyOne) Open(key interface{}, open func() (Repo, error)) (Repo, error) {
    27  	o.mu.Lock()
    28  	defer o.mu.Unlock()
    29  	if o.active == nil {
    30  		o.active = make(map[interface{}]*ref)
    31  	}
    32  
    33  	item, found := o.active[key]
    34  	if !found {
    35  		repo, err := open()
    36  		if err != nil {
    37  			return nil, err
    38  		}
    39  		item = &ref{
    40  			parent: o,
    41  			key:    key,
    42  			Repo:   repo,
    43  		}
    44  		o.active[key] = item
    45  	}
    46  	item.refs++
    47  	return item, nil
    48  }
    49  
    50  type ref struct {
    51  	parent *OnlyOne
    52  	key    interface{}
    53  	refs   uint32
    54  	Repo
    55  }
    56  
    57  var _ Repo = (*ref)(nil)
    58  
    59  func (r *ref) Close() error {
    60  	r.parent.mu.Lock()
    61  	defer r.parent.mu.Unlock()
    62  
    63  	r.refs--
    64  	if r.refs > 0 {
    65  		// others are holding it open
    66  		return nil
    67  	}
    68  
    69  	// last one
    70  	delete(r.parent.active, r.key)
    71  	return r.Repo.Close()
    72  }