launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/state/apiserver/common/resource.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "launchpad.net/juju-core/log" 8 "strconv" 9 "sync" 10 ) 11 12 // Resource represents any resource that should be cleaned up when an 13 // API connection terminates. The Stop method will be called when 14 // that happens. 15 type Resource interface { 16 Stop() error 17 } 18 19 // Resources holds all the resources for a connection. 20 // It allows the registration of resources that will be cleaned 21 // up when a connection terminates. 22 type Resources struct { 23 mu sync.Mutex 24 maxId uint64 25 resources map[string]Resource 26 } 27 28 func NewResources() *Resources { 29 return &Resources{ 30 resources: make(map[string]Resource), 31 } 32 } 33 34 // Get returns the resource for the given id, or 35 // nil if there is no such resource. 36 func (rs *Resources) Get(id string) Resource { 37 rs.mu.Lock() 38 defer rs.mu.Unlock() 39 return rs.resources[id] 40 } 41 42 // Register registers the given resource. It returns a unique 43 // identifier for the resource which can then be used in 44 // subsequent API requests to refer to the resource. 45 func (rs *Resources) Register(r Resource) string { 46 rs.mu.Lock() 47 defer rs.mu.Unlock() 48 rs.maxId++ 49 id := strconv.FormatUint(rs.maxId, 10) 50 rs.resources[id] = r 51 return id 52 } 53 54 // Stop stops the resource with the given id and unregisters it. 55 // It returns any error from the underlying Stop call. 56 // It does not return an error if the resource has already 57 // been unregistered. 58 func (rs *Resources) Stop(id string) error { 59 // We don't hold the mutex while calling Stop, because 60 // that might take a while and we don't want to 61 // stop all other resource manipulation while we do so. 62 // If resources.Stop is called concurrently, we'll get 63 // two concurrent calls to Stop, but that should fit 64 // well with the way we invariably implement Stop. 65 r := rs.Get(id) 66 if r == nil { 67 return nil 68 } 69 err := r.Stop() 70 rs.mu.Lock() 71 defer rs.mu.Unlock() 72 delete(rs.resources, id) 73 return err 74 } 75 76 // StopAll stops all the resources. 77 func (rs *Resources) StopAll() { 78 rs.mu.Lock() 79 defer rs.mu.Unlock() 80 for _, r := range rs.resources { 81 if err := r.Stop(); err != nil { 82 log.Errorf("state/api: error stopping %T resource: %v", r, err) 83 } 84 } 85 rs.resources = make(map[string]Resource) 86 } 87 88 // Count returns the number of resources currently held. 89 func (rs *Resources) Count() int { 90 rs.mu.Lock() 91 defer rs.mu.Unlock() 92 return len(rs.resources) 93 }