blob: d962cfaf1ad5acdb3bd4010e473ac378fd5d3904 [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 vc
import (
"sync"
"v.io/v23/naming"
"v.io/v23/security"
"v.io/v23/verror"
)
var errVCCacheClosed = reg(".errVCCacheClosed", "vc cache has been closed")
// VCCache implements a set of VIFs keyed by the endpoint of the remote end and the
// local principal. Multiple goroutines can invoke methods on the VCCache simultaneously.
type VCCache struct {
mu sync.Mutex
cache map[vcKey]*VC // GUARDED_BY(mu)
started map[vcKey]bool // GUARDED_BY(mu)
cond *sync.Cond
}
// NewVCCache returns a new cache for VCs.
func NewVCCache() *VCCache {
c := &VCCache{
cache: make(map[vcKey]*VC),
started: make(map[vcKey]bool),
}
c.cond = sync.NewCond(&c.mu)
return c
}
// ReservedFind returns a VC where the remote end of the underlying connection
// is identified by the provided (ep, p.PublicKey). Returns nil if there is no
// such VC.
//
// Iff the cache is closed, ReservedFind will return an error.
// If ReservedFind has no error, the caller is required to call Unreserve, to avoid deadlock.
// The ep, and p.PublicKey in Unreserve must be the same as used in the ReservedFind call.
// During this time, all new ReservedFind calls for this ep and p will Block until
// the corresponding Unreserve call is made.
func (c *VCCache) ReservedFind(ep naming.Endpoint, p security.Principal) (*VC, error) {
k := c.key(ep, p)
c.mu.Lock()
defer c.mu.Unlock()
for c.started[k] {
c.cond.Wait()
}
if c.cache == nil {
return nil, verror.New(errVCCacheClosed, nil)
}
c.started[k] = true
return c.cache[k], nil
}
// Unreserve marks the status of the ep, p as no longer started, and
// broadcasts waiting threads.
func (c *VCCache) Unreserve(ep naming.Endpoint, p security.Principal) {
c.mu.Lock()
delete(c.started, c.key(ep, p))
c.cond.Broadcast()
c.mu.Unlock()
}
// Insert adds vc to the cache and returns an error iff the cache has been closed.
func (c *VCCache) Insert(vc *VC) error {
c.mu.Lock()
defer c.mu.Unlock()
if c.cache == nil {
return verror.New(errVCCacheClosed, nil)
}
c.cache[c.key(vc.RemoteEndpoint(), vc.LocalPrincipal())] = vc
return nil
}
// Close marks the VCCache as closed and returns the VCs remaining in the cache.
func (c *VCCache) Close() []*VC {
c.mu.Lock()
vcs := make([]*VC, 0, len(c.cache))
for _, vc := range c.cache {
vcs = append(vcs, vc)
}
c.cache = nil
c.started = nil
c.mu.Unlock()
return vcs
}
// Delete removes vc from the cache, returning an error iff the cache has been closed.
func (c *VCCache) Delete(vc *VC) error {
c.mu.Lock()
defer c.mu.Unlock()
if c.cache == nil {
return verror.New(errVCCacheClosed, nil)
}
delete(c.cache, c.key(vc.RemoteEndpoint(), vc.LocalPrincipal()))
return nil
}
type vcKey struct {
remoteEP string
localPublicKey string // localPublicKey = "" means we are running unencrypted (i.e. SecurityNone)
}
func (c *VCCache) key(ep naming.Endpoint, p security.Principal) vcKey {
k := vcKey{remoteEP: ep.String()}
if p != nil {
k.localPublicKey = p.PublicKey().String()
}
return k
}