blob: 87a5059499bafad5358e453ddbad43c7491ca49c [file] [log] [blame]
// 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, callback func(*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 {
callback(&changed[i])
}
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
}