github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/gossip/comm/conn.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package comm 18 19 import ( 20 "errors" 21 "sync" 22 "sync/atomic" 23 24 "github.com/hyperledger/fabric/gossip/common" 25 "github.com/hyperledger/fabric/gossip/util" 26 proto "github.com/hyperledger/fabric/protos/gossip" 27 "github.com/op/go-logging" 28 "google.golang.org/grpc" 29 ) 30 31 type handler func(message *proto.SignedGossipMessage) 32 33 type connFactory interface { 34 createConnection(endpoint string, pkiID common.PKIidType) (*connection, error) 35 } 36 37 type connectionStore struct { 38 logger *logging.Logger // logger 39 isClosing bool // whether this connection store is shutting down 40 connFactory connFactory // creates a connection to remote peer 41 sync.RWMutex // synchronize access to shared variables 42 pki2Conn map[string]*connection // mapping between pkiID to connections 43 destinationLocks map[string]*sync.RWMutex //mapping between pkiIDs and locks, 44 // used to prevent concurrent connection establishment to the same remote endpoint 45 } 46 47 func newConnStore(connFactory connFactory, logger *logging.Logger) *connectionStore { 48 return &connectionStore{ 49 connFactory: connFactory, 50 isClosing: false, 51 pki2Conn: make(map[string]*connection), 52 destinationLocks: make(map[string]*sync.RWMutex), 53 logger: logger, 54 } 55 } 56 57 func (cs *connectionStore) getConnection(peer *RemotePeer) (*connection, error) { 58 cs.RLock() 59 isClosing := cs.isClosing 60 cs.RUnlock() 61 62 if isClosing { 63 return nil, errors.New("Shutting down") 64 } 65 66 pkiID := peer.PKIID 67 endpoint := peer.Endpoint 68 69 cs.Lock() 70 destinationLock, hasConnected := cs.destinationLocks[string(pkiID)] 71 if !hasConnected { 72 destinationLock = &sync.RWMutex{} 73 cs.destinationLocks[string(pkiID)] = destinationLock 74 } 75 cs.Unlock() 76 77 destinationLock.Lock() 78 79 cs.RLock() 80 conn, exists := cs.pki2Conn[string(pkiID)] 81 if exists { 82 cs.RUnlock() 83 destinationLock.Unlock() 84 return conn, nil 85 } 86 cs.RUnlock() 87 88 createdConnection, err := cs.connFactory.createConnection(endpoint, pkiID) 89 90 destinationLock.Unlock() 91 92 if cs.isClosing { 93 return nil, errors.New("ConnStore is closing") 94 } 95 96 cs.Lock() 97 delete(cs.destinationLocks, string(pkiID)) 98 defer cs.Unlock() 99 100 // check again, maybe someone connected to us during the connection creation? 101 conn, exists = cs.pki2Conn[string(pkiID)] 102 103 if exists { 104 if createdConnection != nil { 105 createdConnection.close() 106 } 107 return conn, nil 108 } 109 110 // no one connected to us AND we failed connecting! 111 if err != nil { 112 return nil, err 113 } 114 115 // at this point in the code, we created a connection to a remote peer 116 conn = createdConnection 117 cs.pki2Conn[string(createdConnection.pkiID)] = conn 118 119 go conn.serviceConnection() 120 121 return conn, nil 122 } 123 124 func (cs *connectionStore) connNum() int { 125 cs.RLock() 126 defer cs.RUnlock() 127 return len(cs.pki2Conn) 128 } 129 130 func (cs *connectionStore) closeConn(peer *RemotePeer) { 131 cs.Lock() 132 defer cs.Unlock() 133 134 if conn, exists := cs.pki2Conn[string(peer.PKIID)]; exists { 135 conn.close() 136 delete(cs.pki2Conn, string(conn.pkiID)) 137 } 138 } 139 140 func (cs *connectionStore) shutdown() { 141 cs.Lock() 142 cs.isClosing = true 143 pkiIds2conn := cs.pki2Conn 144 cs.Unlock() 145 146 wg := sync.WaitGroup{} 147 for _, conn := range pkiIds2conn { 148 wg.Add(1) 149 go func(conn *connection) { 150 cs.closeByPKIid(conn.pkiID) 151 wg.Done() 152 }(conn) 153 } 154 wg.Wait() 155 } 156 157 func (cs *connectionStore) onConnected(serverStream proto.Gossip_GossipStreamServer, pkiID common.PKIidType) *connection { 158 cs.Lock() 159 defer cs.Unlock() 160 161 if c, exists := cs.pki2Conn[string(pkiID)]; exists { 162 c.close() 163 } 164 165 return cs.registerConn(pkiID, serverStream) 166 } 167 168 func (cs *connectionStore) registerConn(pkiID common.PKIidType, serverStream proto.Gossip_GossipStreamServer) *connection { 169 conn := newConnection(nil, nil, nil, serverStream) 170 conn.pkiID = pkiID 171 conn.logger = cs.logger 172 cs.pki2Conn[string(pkiID)] = conn 173 return conn 174 } 175 176 func (cs *connectionStore) closeByPKIid(pkiID common.PKIidType) { 177 cs.Lock() 178 defer cs.Unlock() 179 if conn, exists := cs.pki2Conn[string(pkiID)]; exists { 180 conn.close() 181 delete(cs.pki2Conn, string(pkiID)) 182 } 183 } 184 185 func newConnection(cl proto.GossipClient, c *grpc.ClientConn, cs proto.Gossip_GossipStreamClient, ss proto.Gossip_GossipStreamServer) *connection { 186 connection := &connection{ 187 outBuff: make(chan *msgSending, util.GetIntOrDefault("peer.gossip.sendBuffSize", defSendBuffSize)), 188 cl: cl, 189 conn: c, 190 clientStream: cs, 191 serverStream: ss, 192 stopFlag: int32(0), 193 stopChan: make(chan struct{}, 1), 194 } 195 196 return connection 197 } 198 199 type connection struct { 200 outBuff chan *msgSending 201 logger *logging.Logger // logger 202 pkiID common.PKIidType // pkiID of the remote endpoint 203 handler handler // function to invoke upon a message reception 204 conn *grpc.ClientConn // gRPC connection to remote endpoint 205 cl proto.GossipClient // gRPC stub of remote endpoint 206 clientStream proto.Gossip_GossipStreamClient // client-side stream to remote endpoint 207 serverStream proto.Gossip_GossipStreamServer // server-side stream to remote endpoint 208 stopFlag int32 // indicates whether this connection is in process of stopping 209 stopChan chan struct{} // a method to stop the server-side gRPC call from a different go-routine 210 sync.RWMutex // synchronizes access to shared variables 211 } 212 213 func (conn *connection) close() { 214 if conn.toDie() { 215 return 216 } 217 218 amIFirst := atomic.CompareAndSwapInt32(&conn.stopFlag, int32(0), int32(1)) 219 if !amIFirst { 220 return 221 } 222 223 conn.stopChan <- struct{}{} 224 225 conn.Lock() 226 227 if conn.clientStream != nil { 228 conn.clientStream.CloseSend() 229 } 230 if conn.conn != nil { 231 conn.conn.Close() 232 } 233 234 conn.Unlock() 235 236 } 237 238 func (conn *connection) toDie() bool { 239 return atomic.LoadInt32(&(conn.stopFlag)) == int32(1) 240 } 241 242 func (conn *connection) send(msg *proto.SignedGossipMessage, onErr func(error)) { 243 conn.Lock() 244 defer conn.Unlock() 245 246 if len(conn.outBuff) == util.GetIntOrDefault("peer.gossip.sendBuffSize", defSendBuffSize) { 247 go onErr(errSendOverflow) 248 return 249 } 250 251 m := &msgSending{ 252 envelope: msg.Envelope, 253 onErr: onErr, 254 } 255 256 conn.outBuff <- m 257 } 258 259 func (conn *connection) serviceConnection() error { 260 errChan := make(chan error, 1) 261 msgChan := make(chan *proto.SignedGossipMessage, util.GetIntOrDefault("peer.gossip.recvBuffSize", defRecvBuffSize)) 262 defer close(msgChan) 263 264 // Call stream.Recv() asynchronously in readFromStream(), 265 // and wait for either the Recv() call to end, 266 // or a signal to close the connection, which exits 267 // the method and makes the Recv() call to fail in the 268 // readFromStream() method 269 go conn.readFromStream(errChan, msgChan) 270 271 go conn.writeToStream() 272 273 for !conn.toDie() { 274 select { 275 case stop := <-conn.stopChan: 276 conn.logger.Debug("Closing reading from stream") 277 conn.stopChan <- stop 278 return nil 279 case err := <-errChan: 280 return err 281 case msg := <-msgChan: 282 conn.handler(msg) 283 } 284 } 285 return nil 286 } 287 288 func (conn *connection) writeToStream() { 289 for !conn.toDie() { 290 stream := conn.getStream() 291 if stream == nil { 292 conn.logger.Error(conn.pkiID, "Stream is nil, aborting!") 293 return 294 } 295 select { 296 case m := <-conn.outBuff: 297 err := stream.Send(m.envelope) 298 if err != nil { 299 go m.onErr(err) 300 return 301 } 302 break 303 case stop := <-conn.stopChan: 304 conn.logger.Debug("Closing writing to stream") 305 conn.stopChan <- stop 306 return 307 } 308 } 309 } 310 311 func (conn *connection) readFromStream(errChan chan error, msgChan chan *proto.SignedGossipMessage) { 312 defer func() { 313 recover() 314 }() // msgChan might be closed 315 for !conn.toDie() { 316 stream := conn.getStream() 317 if stream == nil { 318 conn.logger.Error(conn.pkiID, "Stream is nil, aborting!") 319 errChan <- errors.New("Stream is nil") 320 return 321 } 322 envelope, err := stream.Recv() 323 if conn.toDie() { 324 conn.logger.Debug(conn.pkiID, "canceling read because closing") 325 return 326 } 327 if err != nil { 328 errChan <- err 329 conn.logger.Debug(conn.pkiID, "Got error, aborting:", err) 330 return 331 } 332 msg, err := envelope.ToGossipMessage() 333 if err != nil { 334 errChan <- err 335 conn.logger.Warning(conn.pkiID, "Got error, aborting:", err) 336 } 337 msgChan <- msg 338 } 339 } 340 341 func (conn *connection) getStream() stream { 342 conn.Lock() 343 defer conn.Unlock() 344 345 if conn.clientStream != nil && conn.serverStream != nil { 346 e := "Both client and server stream are not nil, something went wrong" 347 conn.logger.Error(e) 348 } 349 350 if conn.clientStream != nil { 351 return conn.clientStream 352 } 353 354 if conn.serverStream != nil { 355 return conn.serverStream 356 } 357 358 return nil 359 } 360 361 type msgSending struct { 362 envelope *proto.Envelope 363 onErr func(error) 364 }