浏览代码

Make several changes to eliminate potential deadlock or recursive lock conditions, and add back rescan of multicast groups on network startup.

Adam Ierymenko 10 年之前
父节点
当前提交
a8bd8fff93
共有 3 个文件被更改,包括 47 次插入37 次删除
  1. 6 2
      node/Network.cpp
  2. 16 17
      node/Topology.cpp
  3. 25 18
      node/Topology.hpp

+ 6 - 2
node/Network.cpp

@@ -118,12 +118,13 @@ public:
 	AnnounceMulticastGroupsToPeersWithActiveDirectPaths(const RuntimeEnvironment *renv,Network *nw) :
 		RR(renv),
 		_now(Utils::now()),
-		_network(nw)
+		_network(nw),
+		_supernodeAddresses(renv->topology->supernodeAddresses())
 	{}
 
 	inline void operator()(Topology &t,const SharedPtr<Peer> &p)
 	{
-		if ( ( (p->hasActiveDirectPath(_now)) && (_network->isAllowed(p->address())) ) || (t.isSupernode(p->address())) ) {
+		if ( ( (p->hasActiveDirectPath(_now)) && (_network->isAllowed(p->address())) ) || (std::find(_supernodeAddresses.begin(),_supernodeAddresses.end(),p->address()) != _supernodeAddresses.end()) ) {
 			Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
 
 			std::set<MulticastGroup> mgs(_network->multicastGroups());
@@ -151,6 +152,7 @@ private:
 	const RuntimeEnvironment *RR;
 	uint64_t _now;
 	Network *_network;
+	std::vector<Address> _supernodeAddresses;
 };
 
 bool Network::rescanMulticastGroups()
@@ -527,6 +529,8 @@ void Network::threadMain()
 			t->setEnabled(_enabled);
 		}
 	}
+
+	rescanMulticastGroups();
 }
 
 void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data)

+ 16 - 17
node/Topology.cpp

@@ -25,8 +25,6 @@
  * LLC. Start here: http://www.zerotier.com/
  */
 
-#include <algorithm>
-
 #include "Constants.hpp"
 #include "Defaults.hpp"
 #include "Topology.hpp"
@@ -51,7 +49,7 @@ Topology::~Topology()
 
 void Topology::setSupernodes(const std::map< Identity,std::vector< std::pair<InetAddress,bool> > > &sn)
 {
-	Mutex::Lock _l(_supernodes_m);
+	Mutex::Lock _l(_lock);
 
 	if (_supernodes == sn)
 		return; // no change
@@ -62,18 +60,20 @@ void Topology::setSupernodes(const std::map< Identity,std::vector< std::pair<Ine
 	uint64_t now = Utils::now();
 
 	for(std::map< Identity,std::vector< std::pair<InetAddress,bool> > >::const_iterator i(sn.begin());i!=sn.end();++i) {
-		if (i->first != RR->identity) {
-			SharedPtr<Peer> p(getPeer(i->first.address()));
+		if (i->first != RR->identity) { // do not add self as a peer
+			SharedPtr<Peer> &p = _activePeers[i->first.address()];
 			if (!p)
-				p = addPeer(SharedPtr<Peer>(new Peer(RR->identity,i->first)));
+				p = SharedPtr<Peer>(new Peer(RR->identity,i->first));
 			for(std::vector< std::pair<InetAddress,bool> >::const_iterator j(i->second.begin());j!=i->second.end();++j)
 				p->addPath(Path(j->first,(j->second) ? Path::PATH_TYPE_TCP_OUT : Path::PATH_TYPE_UDP,true));
 			p->use(now);
 			_supernodePeers.push_back(p);
 		}
-		_supernodeAddresses.insert(i->first.address());
+		_supernodeAddresses.push_back(i->first.address());
 	}
 
+	std::sort(_supernodeAddresses.begin(),_supernodeAddresses.end());
+
 	_amSupernode = (_supernodes.find(RR->identity) != _supernodes.end());
 }
 
@@ -107,7 +107,7 @@ SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
 	}
 
 	uint64_t now = Utils::now();
-	Mutex::Lock _l(_activePeers_m);
+	Mutex::Lock _l(_lock);
 
 	SharedPtr<Peer> p(_activePeers.insert(std::pair< Address,SharedPtr<Peer> >(peer->address(),peer)).first->second);
 	p->use(now);
@@ -124,7 +124,7 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
 	}
 
 	uint64_t now = Utils::now();
-	Mutex::Lock _l(_activePeers_m);
+	Mutex::Lock _l(_lock);
 
 	SharedPtr<Peer> &ap = _activePeers[zta];
 
@@ -151,7 +151,7 @@ SharedPtr<Peer> Topology::getBestSupernode(const Address *avoid,unsigned int avo
 {
 	SharedPtr<Peer> bestSupernode;
 	uint64_t now = Utils::now();
-	Mutex::Lock _l(_supernodes_m);
+	Mutex::Lock _l(_lock);
 
 	if (_amSupernode) {
 		/* If I am a supernode, the "best" supernode is the one whose address
@@ -160,15 +160,15 @@ SharedPtr<Peer> Topology::getBestSupernode(const Address *avoid,unsigned int avo
 		 * circumnavigate the globe rather than bouncing between just two. */
 
 		if (_supernodeAddresses.size() > 1) { // gotta be one other than me for this to work
-			std::set<Address>::const_iterator sna(_supernodeAddresses.find(RR->identity.address()));
+			std::vector<Address>::const_iterator sna(std::find(_supernodeAddresses.begin(),_supernodeAddresses.end(),RR->identity.address()));
 			if (sna != _supernodeAddresses.end()) { // sanity check -- _amSupernode should've been false in this case
 				for(;;) {
 					if (++sna == _supernodeAddresses.end())
 						sna = _supernodeAddresses.begin(); // wrap around at end
 					if (*sna != RR->identity.address()) { // pick one other than us -- starting from me+1 in sorted set order
-						SharedPtr<Peer> p(getPeer(*sna));
-						if ((p)&&(p->hasActiveDirectPath(now))) {
-							bestSupernode = p;
+						std::map< Address,SharedPtr<Peer> >::const_iterator p(_activePeers.find(*sna));
+						if ((p != _activePeers.end())&&(p->second->hasActiveDirectPath(now))) {
+							bestSupernode = p->second;
 							break;
 						}
 					}
@@ -247,10 +247,9 @@ keep_searching_for_supernodes:
 
 void Topology::clean(uint64_t now)
 {
-	Mutex::Lock _l(_activePeers_m);
-	Mutex::Lock _l2(_supernodes_m);
+	Mutex::Lock _l(_lock);
 	for(std::map< Address,SharedPtr<Peer> >::iterator p(_activePeers.begin());p!=_activePeers.end();) {
-		if (((now - p->second->lastUsed()) >= ZT_PEER_IN_MEMORY_EXPIRATION)&&(!_supernodeAddresses.count(p->second->address()))) {
+		if (((now - p->second->lastUsed()) >= ZT_PEER_IN_MEMORY_EXPIRATION)&&(std::find(_supernodeAddresses.begin(),_supernodeAddresses.end(),p->first) == _supernodeAddresses.end())) {
 			_activePeers.erase(p++);
 		} else {
 			p->second->clean(now);

+ 25 - 18
node/Topology.hpp

@@ -32,9 +32,9 @@
 #include <string.h>
 
 #include <map>
-#include <set>
 #include <vector>
 #include <stdexcept>
+#include <algorithm>
 
 #include "Constants.hpp"
 
@@ -102,7 +102,7 @@ public:
 	 */
 	inline std::vector< SharedPtr<Peer> > supernodePeers() const
 	{
-		Mutex::Lock _l(_supernodes_m);
+		Mutex::Lock _l(_lock);
 		return _supernodePeers;
 	}
 
@@ -111,7 +111,7 @@ public:
 	 */
 	inline unsigned int numSupernodes() const
 	{
-		Mutex::Lock _l(_supernodes_m);
+		Mutex::Lock _l(_lock);
 		return _supernodePeers.size();
 	}
 
@@ -146,16 +146,16 @@ public:
 	inline bool isSupernode(const Address &zta) const
 		throw()
 	{
-		Mutex::Lock _l(_supernodes_m);
-		return (_supernodeAddresses.count(zta) > 0);
+		Mutex::Lock _l(_lock);
+		return (std::find(_supernodeAddresses.begin(),_supernodeAddresses.end(),zta) != _supernodeAddresses.end());
 	}
 
 	/**
-	 * @return Set of supernode addresses
+	 * @return Vector of supernode addresses
 	 */
-	inline std::set<Address> supernodeAddresses() const
+	inline std::vector<Address> supernodeAddresses() const
 	{
-		Mutex::Lock _l(_supernodes_m);
+		Mutex::Lock _l(_lock);
 		return _supernodeAddresses;
 	}
 
@@ -175,13 +175,17 @@ public:
 	 * Note: explicitly template this by reference if you want the object
 	 * passed by reference instead of copied.
 	 *
+	 * Warning: be careful not to use features in these that call any other
+	 * methods of Topology that may lock _lock, otherwise a recursive lock
+	 * and deadlock or lock corruption may occur.
+	 *
 	 * @param f Function to apply
 	 * @tparam F Function or function object type
 	 */
 	template<typename F>
 	inline void eachPeer(F f)
 	{
-		Mutex::Lock _l(_activePeers_m);
+		Mutex::Lock _l(_lock);
 		for(std::map< Address,SharedPtr<Peer> >::const_iterator p(_activePeers.begin());p!=_activePeers.end();++p)
 			f(*this,p->second);
 	}
@@ -192,13 +196,17 @@ public:
 	 * Note: explicitly template this by reference if you want the object
 	 * passed by reference instead of copied.
 	 *
+	 * Warning: be careful not to use features in these that call any other
+	 * methods of Topology that may lock _lock, otherwise a recursive lock
+	 * and deadlock or lock corruption may occur.
+	 *
 	 * @param f Function to apply
 	 * @tparam F Function or function object type
 	 */
 	template<typename F>
 	inline void eachSupernodePeer(F f)
 	{
-		Mutex::Lock _l(_supernodes_m);
+		Mutex::Lock _l(_lock);
 		for(std::vector< SharedPtr<Peer> >::const_iterator p(_supernodePeers.begin());p!=_supernodePeers.end();++p)
 			f(*this,*p);
 	}
@@ -227,7 +235,7 @@ public:
 			 *
 			 * Note that we measure ping time from time of last receive rather
 			 * than time of last send in order to only count full round trips. */
-			if ( (!_supernodeAddresses.count(p->address())) &&
+			if ( (std::find(_supernodeAddresses.begin(),_supernodeAddresses.end(),p->address()) == _supernodeAddresses.end()) &&
 			     ((_now - p->lastFrame()) < ZT_PEER_PATH_ACTIVITY_TIMEOUT) &&
 			     ((_now - p->lastDirectReceive()) >= ZT_PEER_DIRECT_PING_DELAY) ) {
 				p->sendPing(RR,_now);
@@ -236,7 +244,7 @@ public:
 
 	private:
 		uint64_t _now;
-		std::set<Address> _supernodeAddresses;
+		std::vector<Address> _supernodeAddresses;
 		const RuntimeEnvironment *RR;
 	};
 
@@ -305,7 +313,7 @@ public:
 			Packet outp(p->address(),RR->identity.address(),Packet::VERB_NOP);
 			outp.armor(p->key(),false); // no need to encrypt a NOP
 
-			if (_supernodeAddresses.count(p->address())) {
+			if (std::find(_supernodeAddresses.begin(),_supernodeAddresses.end(),p->address()) != _supernodeAddresses.end()) {
 				// Send NOP directly to supernodes
 				p->send(RR,outp.data(),outp.size(),_now);
 			} else {
@@ -320,7 +328,7 @@ public:
 	private:
 		uint64_t _now;
 		SharedPtr<Peer> _supernode;
-		std::set<Address> _supernodeAddresses;
+		std::vector<Address> _supernodeAddresses;
 		const RuntimeEnvironment *RR;
 	};
 
@@ -362,12 +370,11 @@ private:
 	std::string _idCacheBase;
 
 	std::map< Address,SharedPtr<Peer> > _activePeers;
-	Mutex _activePeers_m;
-
 	std::map< Identity,std::vector< std::pair<InetAddress,bool> > > _supernodes;
-	std::set< Address > _supernodeAddresses;
+	std::vector< Address > _supernodeAddresses;
 	std::vector< SharedPtr<Peer> > _supernodePeers;
-	Mutex _supernodes_m;
+
+	Mutex _lock;
 
 	// Set to true if my identity is in _supernodes
 	volatile bool _amSupernode;