github.com/CyCoreSystems/ari@v4.8.4+incompatible/stdbus/bus.go (about)

     1  package stdbus
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/CyCoreSystems/ari"
     7  )
     8  
     9  // subscriptionEventBufferSize defines the number of events that each
    10  // subscription will queue before accepting more events.
    11  var subscriptionEventBufferSize = 100
    12  
    13  // bus is an event bus for ARI events.  It receives and
    14  // redistributes events based on a subscription
    15  // model.
    16  type bus struct {
    17  	subs []*subscription // The list of subscriptions
    18  
    19  	rwMux sync.RWMutex
    20  
    21  	closed bool
    22  }
    23  
    24  // New creates and returns the event bus.
    25  func New() ari.Bus {
    26  	b := &bus{
    27  		subs: []*subscription{},
    28  	}
    29  
    30  	return b
    31  }
    32  
    33  // Close closes out all subscriptions in the bus.
    34  func (b *bus) Close() {
    35  	if b.closed {
    36  		return
    37  	}
    38  	b.closed = true
    39  
    40  	for _, s := range b.subs {
    41  		s.Cancel()
    42  	}
    43  }
    44  
    45  // Send sends the message to the bus
    46  func (b *bus) Send(e ari.Event) {
    47  	var matched bool
    48  	b.rwMux.RLock()
    49  
    50  	// Disseminate the message to the subscribers
    51  	for _, s := range b.subs {
    52  		matched = false
    53  		for _, k := range e.Keys() {
    54  			if matched {
    55  				break
    56  			}
    57  			if s.key.Match(k) {
    58  				matched = true
    59  				for _, topic := range s.events {
    60  					if topic == e.GetType() || topic == ari.Events.All {
    61  						select {
    62  						case s.C <- e:
    63  						default: // never block
    64  						}
    65  					}
    66  				}
    67  			}
    68  		}
    69  	}
    70  
    71  	b.rwMux.RUnlock()
    72  }
    73  
    74  // Subscribe returns a subscription to the given list
    75  // of event types
    76  func (b *bus) Subscribe(key *ari.Key, eTypes ...string) ari.Subscription {
    77  	s := newSubscription(b, key, eTypes...)
    78  	b.add(s)
    79  	return s
    80  }
    81  
    82  // add appends a new subscription to the bus
    83  func (b *bus) add(s *subscription) {
    84  	b.rwMux.Lock()
    85  	b.subs = append(b.subs, s)
    86  	b.rwMux.Unlock()
    87  }
    88  
    89  // remove deletes the given subscription from the bus
    90  func (b *bus) remove(s *subscription) {
    91  	b.rwMux.Lock()
    92  	for i, si := range b.subs {
    93  		if s == si {
    94  			// Subs are pointers, so we have to explicitly remove them
    95  			// to prevent memory leaks
    96  			b.subs[i] = b.subs[len(b.subs)-1] // replace the current with the end
    97  			b.subs[len(b.subs)-1] = nil       // remove the end
    98  			b.subs = b.subs[:len(b.subs)-1]   // lop off the end
    99  			break
   100  		}
   101  	}
   102  	b.rwMux.Unlock()
   103  }
   104  
   105  // A Subscription is a wrapped channel for receiving
   106  // events from the ARI event bus.
   107  type subscription struct {
   108  	key    *ari.Key
   109  	b      *bus     // reference to the event bus
   110  	events []string // list of events to listen for
   111  
   112  	mu     sync.Mutex
   113  	closed bool           // channel closure protection flag
   114  	C      chan ari.Event // channel for sending events to the subscriber
   115  }
   116  
   117  // newSubscription creates a new, unattached subscription
   118  func newSubscription(b *bus, key *ari.Key, eTypes ...string) *subscription {
   119  	return &subscription{
   120  		key:    key,
   121  		b:      b,
   122  		events: eTypes,
   123  		C:      make(chan ari.Event, subscriptionEventBufferSize),
   124  	}
   125  }
   126  
   127  // Events returns the events channel
   128  func (s *subscription) Events() <-chan ari.Event {
   129  	return s.C
   130  }
   131  
   132  // Cancel cancels the subscription and removes it from
   133  // the event bus.
   134  func (s *subscription) Cancel() {
   135  	if s == nil {
   136  		return
   137  	}
   138  
   139  	s.mu.Lock()
   140  	if s.closed {
   141  		s.mu.Unlock()
   142  		return
   143  	}
   144  	s.closed = true
   145  	s.mu.Unlock()
   146  
   147  	// Remove the subscription from the bus
   148  	if s.b != nil {
   149  		s.b.remove(s)
   150  	}
   151  
   152  	// Close the subscription's deliver channel
   153  	if s.C != nil {
   154  		close(s.C)
   155  	}
   156  }