github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/thirdparty/notifier/notifier.go (about)

     1  // Package notifier provides a simple notification dispatcher
     2  // meant to be embedded in larger structres who wish to allow
     3  // clients to sign up for event notifications.
     4  package notifier
     5  
     6  import (
     7  	"sync"
     8  
     9  	process "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
    10  	ratelimit "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/ratelimit"
    11  )
    12  
    13  // Notifiee is a generic interface. Clients implement
    14  // their own Notifiee interfaces to ensure type-safety
    15  // of notifications:
    16  //
    17  //  type RocketNotifiee interface{
    18  //    Countdown(r Rocket, countdown time.Duration)
    19  //    LiftedOff(Rocket)
    20  //    ReachedOrbit(Rocket)
    21  //    Detached(Rocket, Capsule)
    22  //    Landed(Rocket)
    23  //  }
    24  //
    25  type Notifiee interface{}
    26  
    27  // Notifier is a notification dispatcher. It's meant
    28  // to be composed, and its zero-value is ready to be used.
    29  //
    30  //  type Rocket struct {
    31  //    notifier notifier.Notifier
    32  //  }
    33  //
    34  type Notifier struct {
    35  	mu   sync.RWMutex // guards notifiees
    36  	nots map[Notifiee]struct{}
    37  	lim  *ratelimit.RateLimiter
    38  }
    39  
    40  // RateLimited returns a rate limited Notifier. only limit goroutines
    41  // will be spawned. If limit is zero, no rate limiting happens. This
    42  // is the same as `Notifier{}`.
    43  func RateLimited(limit int) Notifier {
    44  	n := Notifier{}
    45  	if limit > 0 {
    46  		n.lim = ratelimit.NewRateLimiter(process.Background(), limit)
    47  	}
    48  	return n
    49  }
    50  
    51  // Notify signs up Notifiee e for notifications. This function
    52  // is meant to be called behind your own type-safe function(s):
    53  //
    54  //   // generic function for pattern-following
    55  //   func (r *Rocket) Notify(n Notifiee) {
    56  //     r.notifier.Notify(n)
    57  //   }
    58  //
    59  //   // or as part of other functions
    60  //   func (r *Rocket) Onboard(a Astronaut) {
    61  //     r.astronauts = append(r.austronauts, a)
    62  //     r.notifier.Notify(a)
    63  //   }
    64  //
    65  func (n *Notifier) Notify(e Notifiee) {
    66  	n.mu.Lock()
    67  	if n.nots == nil { // so that zero-value is ready to be used.
    68  		n.nots = make(map[Notifiee]struct{})
    69  	}
    70  	n.nots[e] = struct{}{}
    71  	n.mu.Unlock()
    72  }
    73  
    74  // StopNotify stops notifying Notifiee e. This function
    75  // is meant to be called behind your own type-safe function(s):
    76  //
    77  //   // generic function for pattern-following
    78  //   func (r *Rocket) StopNotify(n Notifiee) {
    79  //     r.notifier.StopNotify(n)
    80  //   }
    81  //
    82  //   // or as part of other functions
    83  //   func (r *Rocket) Detach(c Capsule) {
    84  //     r.notifier.StopNotify(c)
    85  //     r.capsule = nil
    86  //   }
    87  //
    88  func (n *Notifier) StopNotify(e Notifiee) {
    89  	n.mu.Lock()
    90  	if n.nots != nil { // so that zero-value is ready to be used.
    91  		delete(n.nots, e)
    92  	}
    93  	n.mu.Unlock()
    94  }
    95  
    96  // NotifyAll messages the notifier's notifiees with a given notification.
    97  // This is done by calling the given function with each notifiee. It is
    98  // meant to be called with your own type-safe notification functions:
    99  //
   100  //  func (r *Rocket) Launch() {
   101  //    r.notifyAll(func(n Notifiee) {
   102  //      n.Launched(r)
   103  //    })
   104  //  }
   105  //
   106  //  // make it private so only you can use it. This function is necessary
   107  //  // to make sure you only up-cast in one place. You control who you added
   108  //  // to be a notifiee. If Go adds generics, maybe we can get rid of this
   109  //  // method but for now it is like wrapping a type-less container with
   110  //  // a type safe interface.
   111  //  func (r *Rocket) notifyAll(notify func(Notifiee)) {
   112  //    r.notifier.NotifyAll(func(n notifier.Notifiee) {
   113  //      notify(n.(Notifiee))
   114  //    })
   115  //  }
   116  //
   117  // Note well: each notification is launched in its own goroutine, so they
   118  // can be processed concurrently, and so that whatever the notification does
   119  // it _never_ blocks out the client. This is so that consumers _cannot_ add
   120  // hooks into your object that block you accidentally.
   121  func (n *Notifier) NotifyAll(notify func(Notifiee)) {
   122  	n.mu.Lock()
   123  	defer n.mu.Unlock()
   124  
   125  	if n.nots == nil { // so that zero-value is ready to be used.
   126  		return
   127  	}
   128  
   129  	// no rate limiting.
   130  	if n.lim == nil {
   131  		for notifiee := range n.nots {
   132  			go notify(notifiee)
   133  		}
   134  		return
   135  	}
   136  
   137  	// with rate limiting.
   138  	n.lim.Go(func(worker process.Process) {
   139  		for notifiee := range n.nots {
   140  			notifiee := notifiee // rebind for loop data races
   141  			n.lim.LimitedGo(func(worker process.Process) {
   142  				notify(notifiee)
   143  			})
   144  		}
   145  	})
   146  }