| 1 | package remote |
| 2 | |
| 3 | import "sync" |
| 4 | |
| 5 | // statusHub fans status events out to subscribers and remembers the last event |
| 6 | // so a late subscriber immediately learns the current state. Subscriber |
| 7 | // callbacks are invoked synchronously under no lock; they must not block or |
| 8 | // call back into the Client. |
| 9 | type statusHub struct { |
| 10 | mu sync.Mutex |
| 11 | last StatusEvent |
| 12 | haveL bool |
| 13 | nextID int |
| 14 | subs map[int]func(StatusEvent) |
| 15 | } |
| 16 | |
| 17 | func newStatusHub() *statusHub { |
| 18 | return &statusHub{subs: map[int]func(StatusEvent){}} |
| 19 | } |
| 20 | |
| 21 | // subscribe registers fn, replays the last event to it, and returns a cancel. |
| 22 | func (h *statusHub) subscribe(fn func(StatusEvent)) func() { |
| 23 | h.mu.Lock() |
| 24 | id := h.nextID |
| 25 | h.nextID++ |
| 26 | h.subs[id] = fn |
| 27 | last, have := h.last, h.haveL |
| 28 | h.mu.Unlock() |
| 29 | |
| 30 | if have { |
| 31 | fn(last) |
| 32 | } |
| 33 | return func() { |
| 34 | h.mu.Lock() |
| 35 | delete(h.subs, id) |
| 36 | h.mu.Unlock() |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | // publish records ev as the last event and delivers it to all subscribers. |
| 41 | func (h *statusHub) publish(ev StatusEvent) { |
| 42 | h.mu.Lock() |
| 43 | h.last = ev |
| 44 | h.haveL = true |
| 45 | fns := make([]func(StatusEvent), 0, len(h.subs)) |
| 46 | for _, fn := range h.subs { |
| 47 | fns = append(fns, fn) |
| 48 | } |
| 49 | h.mu.Unlock() |
| 50 | for _, fn := range fns { |
| 51 | fn(ev) |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | func (h *statusHub) current() StatusEvent { |
| 56 | h.mu.Lock() |
| 57 | defer h.mu.Unlock() |
| 58 | return h.last |
| 59 | } |
| 60 |