blob: 3032dfce8bf4d6f376727729ffed06d89da955f2 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Jiri Simsa5293dcb2014-05-10 09:56:38 -07005package vif
6
7import (
8 "math/rand"
Jungho Ahn9a5b3072015-02-24 13:59:25 -08009 "net"
Asim Shankar0cd73532014-08-25 11:01:29 -070010 "runtime"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070011 "sync"
Jungho Ahn9a5b3072015-02-24 13:59:25 -080012
Matt Rosencrantz94502cf2015-03-18 09:43:44 -070013 "v.io/v23/rpc"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070014)
15
16// Set implements a set of VIFs keyed by (network, address) of the underlying
17// connection. Multiple goroutines can invoke methods on the Set
18// simultaneously.
19type Set struct {
Suharsh Sivakumar859ea0f2015-04-29 23:51:39 -070020 mu sync.RWMutex
21 set map[string][]*VIF // GUARDED_BY(mu)
22 started map[string]bool // GUARDED_BY(mu)
Suharsh Sivakumar7e93ce52015-05-07 17:46:13 -070023 keys map[*VIF]string // GUARDED_BY(mu)
Suharsh Sivakumar859ea0f2015-04-29 23:51:39 -070024 cond *sync.Cond
Jiri Simsa5293dcb2014-05-10 09:56:38 -070025}
26
27// NewSet returns a new Set of VIFs.
28func NewSet() *Set {
Suharsh Sivakumar859ea0f2015-04-29 23:51:39 -070029 s := &Set{
30 set: make(map[string][]*VIF),
31 started: make(map[string]bool),
Suharsh Sivakumar7e93ce52015-05-07 17:46:13 -070032 keys: make(map[*VIF]string),
Suharsh Sivakumar859ea0f2015-04-29 23:51:39 -070033 }
34 s.cond = sync.NewCond(&s.mu)
35 return s
36}
37
38// BlockingFind returns a VIF where the remote end of the underlying network connection
39// is identified by the provided (network, address). Returns nil if there is no
40// such VIF.
41//
Suharsh Sivakumar7e93ce52015-05-07 17:46:13 -070042// The caller is required to call the returned unblock function, to avoid deadlock.
43// Until the returned function is called, all new BlockingFind calls for this
44// network and address will block.
45func (s *Set) BlockingFind(network, address string) (*VIF, func()) {
46 if isNonDistinctConn(network, address) {
47 return nil, func() {}
48 }
49
50 k := key(network, address)
51
52 s.mu.Lock()
53 defer s.mu.Unlock()
54
55 for s.started[k] {
56 s.cond.Wait()
57 }
58
59 _, _, _, p := rpc.RegisteredProtocol(network)
60 for _, n := range p {
61 if vifs := s.set[key(n, address)]; len(vifs) > 0 {
62 return vifs[rand.Intn(len(vifs))], func() {}
63 }
64 }
65
66 s.started[k] = true
67 return nil, func() { s.unblock(network, address) }
Suharsh Sivakumar859ea0f2015-04-29 23:51:39 -070068}
69
Suharsh Sivakumar7e93ce52015-05-07 17:46:13 -070070// unblock marks the status of the network, address as no longer started, and
Suharsh Sivakumar04e0e282015-05-02 23:24:04 -070071// broadcasts waiting threads.
Suharsh Sivakumar7e93ce52015-05-07 17:46:13 -070072func (s *Set) unblock(network, address string) {
Suharsh Sivakumar859ea0f2015-04-29 23:51:39 -070073 s.mu.Lock()
74 delete(s.started, key(network, address))
75 s.cond.Broadcast()
76 s.mu.Unlock()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070077}
78
Suharsh Sivakumar04e0e282015-05-02 23:24:04 -070079// Insert adds a VIF to the set.
Suharsh Sivakumar7e93ce52015-05-07 17:46:13 -070080func (s *Set) Insert(vif *VIF, network, address string) {
81 k := key(network, address)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070082 s.mu.Lock()
83 defer s.mu.Unlock()
Suharsh Sivakumar7e93ce52015-05-07 17:46:13 -070084 s.keys[vif] = k
Jungho Ahn9a5b3072015-02-24 13:59:25 -080085 vifs := s.set[k]
86 for _, v := range vifs {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070087 if v == vif {
88 return
89 }
90 }
Jungho Ahn9a5b3072015-02-24 13:59:25 -080091 s.set[k] = append(vifs, vif)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070092}
93
Suharsh Sivakumar04e0e282015-05-02 23:24:04 -070094// Delete removes a VIF from the set.
Jiri Simsa5293dcb2014-05-10 09:56:38 -070095func (s *Set) Delete(vif *VIF) {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070096 s.mu.Lock()
97 defer s.mu.Unlock()
Suharsh Sivakumar7e93ce52015-05-07 17:46:13 -070098 k := s.keys[vif]
Jungho Ahn9a5b3072015-02-24 13:59:25 -080099 vifs := s.set[k]
100 for i, v := range vifs {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700101 if v == vif {
Jungho Ahn9a5b3072015-02-24 13:59:25 -0800102 if len(vifs) == 1 {
103 delete(s.set, k)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700104 } else {
Jungho Ahn9a5b3072015-02-24 13:59:25 -0800105 s.set[k] = append(vifs[:i], vifs[i+1:]...)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700106 }
Suharsh Sivakumar7e93ce52015-05-07 17:46:13 -0700107 delete(s.keys, vif)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700108 return
109 }
110 }
111}
112
113// List returns the elements in the set as a slice.
114func (s *Set) List() []*VIF {
115 s.mu.RLock()
116 defer s.mu.RUnlock()
117 l := make([]*VIF, 0, len(s.set))
118 for _, vifs := range s.set {
119 l = append(l, vifs...)
120 }
121 return l
122}
123
Jungho Ahn9a5b3072015-02-24 13:59:25 -0800124func key(network, address string) string {
125 if network == "tcp" || network == "ws" {
126 host, _, _ := net.SplitHostPort(address)
127 switch ip := net.ParseIP(host); {
128 case ip == nil:
129 // This may happen when address is a hostname. But we do not care
130 // about it, since vif cannot be found with a hostname anyway.
131 case ip.To4() != nil:
132 network += "4"
133 default:
134 network += "6"
135 }
136 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700137 return network + ":" + address
138}
Suharsh Sivakumar859ea0f2015-04-29 23:51:39 -0700139
140// Some network connections (like those created with net.Pipe or Unix sockets)
141// do not end up with distinct net.Addrs on distinct net.Conns.
142func isNonDistinctConn(network, address string) bool {
143 return len(address) == 0 ||
144 (network == "pipe" && address == "pipe") ||
145 (runtime.GOOS == "linux" && network == "unix" && address == "@")
146}