| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package mock |
| |
| import ( |
| "reflect" |
| "sync" |
| |
| "v.io/v23/context" |
| "v.io/v23/discovery" |
| |
| idiscovery "v.io/x/ref/lib/discovery" |
| ) |
| |
| type plugin struct { |
| mu sync.Mutex |
| |
| updated *sync.Cond |
| updateSeq int // GUARDED_BY(mu) |
| |
| adStatus idiscovery.AdStatus |
| adinfoMap map[string]map[discovery.AdId]*idiscovery.AdInfo // GUARDED_BY(mu) |
| } |
| |
| func (p *plugin) Advertise(ctx *context.T, adinfo *idiscovery.AdInfo, done func()) error { |
| p.RegisterAd(adinfo) |
| go func() { |
| <-ctx.Done() |
| p.UnregisterAd(adinfo) |
| done() |
| }() |
| return nil |
| } |
| |
| func (p *plugin) Scan(ctx *context.T, interfaceName string, ch chan<- *idiscovery.AdInfo, done func()) error { |
| rescan := make(chan struct{}) |
| go func() { |
| defer close(rescan) |
| |
| var updateSeqSeen int |
| for { |
| p.mu.Lock() |
| for updateSeqSeen == p.updateSeq { |
| p.updated.Wait() |
| } |
| updateSeqSeen = p.updateSeq |
| p.mu.Unlock() |
| select { |
| case rescan <- struct{}{}: |
| case <-ctx.Done(): |
| return |
| } |
| } |
| }() |
| |
| go func() { |
| defer done() |
| |
| seen := make(map[discovery.AdId]idiscovery.AdInfo) |
| for { |
| current := make(map[discovery.AdId]idiscovery.AdInfo) |
| p.mu.Lock() |
| for key, adinfos := range p.adinfoMap { |
| if len(interfaceName) > 0 && key != interfaceName { |
| continue |
| } |
| for id, adinfo := range adinfos { |
| current[id] = copyAd(adinfo, p.adStatus) |
| } |
| } |
| p.mu.Unlock() |
| |
| changed := make([]idiscovery.AdInfo, 0, len(current)) |
| for id, adinfo := range current { |
| old, ok := seen[id] |
| if !ok || !reflect.DeepEqual(old, adinfo) { |
| changed = append(changed, adinfo) |
| } |
| } |
| for id, adinfo := range seen { |
| if _, ok := current[id]; !ok { |
| adinfo.Status = idiscovery.AdReady |
| adinfo.Lost = true |
| changed = append(changed, adinfo) |
| } |
| } |
| |
| // Push new changes. |
| for i := range changed { |
| select { |
| case ch <- &changed[i]: |
| case <-ctx.Done(): |
| return |
| } |
| } |
| |
| seen = current |
| |
| // Wait the next update. |
| select { |
| case <-rescan: |
| case <-ctx.Done(): |
| return |
| } |
| } |
| }() |
| return nil |
| } |
| |
| func (p *plugin) Close() {} |
| |
| func copyAd(adinfo *idiscovery.AdInfo, status idiscovery.AdStatus) idiscovery.AdInfo { |
| copied := *adinfo |
| copied.Status = status |
| switch copied.Status { |
| case idiscovery.AdReady: |
| copied.DirAddrs = nil |
| case idiscovery.AdNotReady: |
| copied.Ad = discovery.Advertisement{Id: adinfo.Ad.Id} |
| case idiscovery.AdPartiallyReady: |
| copied.Ad.Attachments = nil |
| } |
| return copied |
| } |
| |
| // RegisterAd registers an advertisement to the plugin. If there is already an |
| // advertisement with the same id, it will be updated with the given advertisement. |
| func (p *plugin) RegisterAd(adinfo *idiscovery.AdInfo) { |
| p.mu.Lock() |
| adinfos := p.adinfoMap[adinfo.Ad.InterfaceName] |
| if adinfos == nil { |
| adinfos = make(map[discovery.AdId]*idiscovery.AdInfo) |
| p.adinfoMap[adinfo.Ad.InterfaceName] = adinfos |
| } |
| adinfos[adinfo.Ad.Id] = adinfo |
| p.updateSeq++ |
| p.mu.Unlock() |
| p.updated.Broadcast() |
| } |
| |
| // UnregisterAd unregisters a registered advertisement from the plugin. |
| func (p *plugin) UnregisterAd(adinfo *idiscovery.AdInfo) { |
| p.mu.Lock() |
| adinfos := p.adinfoMap[adinfo.Ad.InterfaceName] |
| delete(adinfos, adinfo.Ad.Id) |
| if len(adinfos) == 0 { |
| p.adinfoMap[adinfo.Ad.InterfaceName] = nil |
| } |
| p.updateSeq++ |
| p.mu.Unlock() |
| p.updated.Broadcast() |
| } |
| |
| func New() *plugin { |
| return NewWithAdStatus(idiscovery.AdReady) |
| } |
| |
| func NewWithAdStatus(status idiscovery.AdStatus) *plugin { |
| p := &plugin{adStatus: status, adinfoMap: make(map[string]map[discovery.AdId]*idiscovery.AdInfo)} |
| p.updated = sync.NewCond(&p.mu) |
| return p |
| } |