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  }