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