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 }