mirror of
https://github.com/ipfs/kubo.git
synced 2026-03-07 09:17:49 +08:00
116 lines
3.3 KiB
Go
116 lines
3.3 KiB
Go
// Package notifier provides a simple notification dispatcher
|
|
// meant to be embedded in larger structres who wish to allow
|
|
// clients to sign up for event notifications.
|
|
package notifier
|
|
|
|
import (
|
|
"sync"
|
|
)
|
|
|
|
// Notifiee is a generic interface. Clients implement
|
|
// their own Notifiee interfaces to ensure type-safety
|
|
// of notifications:
|
|
//
|
|
// type RocketNotifiee interface{
|
|
// Countdown(r Rocket, countdown time.Duration)
|
|
// LiftedOff(Rocket)
|
|
// ReachedOrbit(Rocket)
|
|
// Detached(Rocket, Capsule)
|
|
// Landed(Rocket)
|
|
// }
|
|
//
|
|
type Notifiee interface{}
|
|
|
|
// Notifier is a notification dispatcher. It's meant
|
|
// to be composed, and its zero-value is ready to be used.
|
|
//
|
|
// type Rocket struct {
|
|
// notifier notifier.Notifier
|
|
// }
|
|
//
|
|
type Notifier struct {
|
|
mu sync.RWMutex // guards notifiees
|
|
nots map[Notifiee]struct{}
|
|
}
|
|
|
|
// Notify signs up Notifiee e for notifications. This function
|
|
// is meant to be called behind your own type-safe function(s):
|
|
//
|
|
// // generic function for pattern-following
|
|
// func (r *Rocket) Notify(n Notifiee) {
|
|
// r.notifier.Notify(n)
|
|
// }
|
|
//
|
|
// // or as part of other functions
|
|
// func (r *Rocket) Onboard(a Astronaut) {
|
|
// r.astronauts = append(r.austronauts, a)
|
|
// r.notifier.Notify(a)
|
|
// }
|
|
//
|
|
func (n *Notifier) Notify(e Notifiee) {
|
|
n.mu.Lock()
|
|
if n.nots == nil { // so that zero-value is ready to be used.
|
|
n.nots = make(map[Notifiee]struct{})
|
|
}
|
|
n.nots[e] = struct{}{}
|
|
n.mu.Unlock()
|
|
}
|
|
|
|
// StopNotifying stops notifying Notifiee e. This function
|
|
// is meant to be called behind your own type-safe function(s):
|
|
//
|
|
// // generic function for pattern-following
|
|
// func (r *Rocket) StopNotify(n Notifiee) {
|
|
// r.notifier.StopNotify(n)
|
|
// }
|
|
//
|
|
// // or as part of other functions
|
|
// func (r *Rocket) Detach(c Capsule) {
|
|
// r.notifier.StopNotify(c)
|
|
// r.capsule = nil
|
|
// }
|
|
//
|
|
func (n *Notifier) StopNotify(e Notifiee) {
|
|
n.mu.Lock()
|
|
if n.nots != nil { // so that zero-value is ready to be used.
|
|
delete(n.nots, e)
|
|
}
|
|
n.mu.Unlock()
|
|
}
|
|
|
|
// NotifyAll messages the notifier's notifiees with a given notification.
|
|
// This is done by calling the given function with each notifiee. It is
|
|
// meant to be called with your own type-safe notification functions:
|
|
//
|
|
// func (r *Rocket) Launch() {
|
|
// r.notifyAll(func(n Notifiee) {
|
|
// n.Launched(r)
|
|
// })
|
|
// }
|
|
//
|
|
// // make it private so only you can use it. This function is necessary
|
|
// // to make sure you only up-cast in one place. You control who you added
|
|
// // to be a notifiee. If Go adds generics, maybe we can get rid of this
|
|
// // method but for now it is like wrapping a type-less container with
|
|
// // a type safe interface.
|
|
// func (r *Rocket) notifyAll(notify func(Notifiee)) {
|
|
// r.notifier.NotifyAll(func(n notifier.Notifiee) {
|
|
// notify(n.(Notifiee))
|
|
// })
|
|
// }
|
|
//
|
|
// Note well: each notification is launched in its own goroutine, so they
|
|
// can be processed concurrently, and so that whatever the notification does
|
|
// it _never_ blocks out the client. This is so that consumers _cannot_ add
|
|
// hooks into your object that block you accidentally.
|
|
func (n *Notifier) NotifyAll(notify func(Notifiee)) {
|
|
n.mu.Lock()
|
|
if n.nots != nil { // so that zero-value is ready to be used.
|
|
for notifiee := range n.nots {
|
|
go notify(notifiee)
|
|
// TODO find a good way to rate limit this without blocking notifier.
|
|
}
|
|
}
|
|
n.mu.Unlock()
|
|
}
|