| // 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 |
| } |