← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 2961: Avoid deadlocks when changing user matchings

 

------------------------------------------------------------
revno: 2961
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Fri 2012-06-22 16:46:44 +0200
message:
  Avoid deadlocks when changing user matchings
modified:
  changelog.txt
  dcpp/AdcHub.cpp
  dcpp/AdcHub.h
  dcpp/BufferedSocket.cpp
  dcpp/BufferedSocket.h
  dcpp/BufferedSocketListener.h
  dcpp/Client.cpp
  dcpp/Client.h
  dcpp/DownloadManager.cpp
  dcpp/DownloadManager.h
  dcpp/NmdcHub.cpp
  dcpp/NmdcHub.h
  dcpp/UserConnection.cpp
  dcpp/UserConnection.h
  dcpp/UserMatchManager.cpp
  dcpp/UserMatchManager.h
  win32/UserInfoBase.cpp
  win32/UserInfoBase.h


--
lp:dcplusplus
https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk

Your team Dcplusplus-team is subscribed to branch lp:dcplusplus.
To unsubscribe from this branch go to https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk/+edit-subscription
=== modified file 'changelog.txt'
--- changelog.txt	2012-06-21 18:37:35 +0000
+++ changelog.txt	2012-06-22 14:46:44 +0000
@@ -18,6 +18,7 @@
 * [L#745162] Fix upload log format for partial lists (emtee)
 * Fix GUI problems in a download attempt of a public hublist with invalid address (emtee)
 * Fix unsuccessful HTTP redirections (emtee)
+* [L#1016205] Avoid deadlocks when changing user matchings (poy)
 
 -- 0.799 2012-05-05 --
 * Add icons (iceman50)

=== modified file 'dcpp/AdcHub.cpp'
--- dcpp/AdcHub.cpp	2012-06-21 18:52:47 +0000
+++ dcpp/AdcHub.cpp	2012-06-22 14:46:44 +0000
@@ -125,7 +125,7 @@
 }
 
 void AdcHub::clearUsers() {
-	SIDMap tmp;
+	decltype(users) tmp;
 	{
 		Lock l(cs);
 		users.swap(tmp);
@@ -1106,4 +1106,13 @@
 	}
 }
 
+OnlineUserList AdcHub::getUsers() const {
+	Lock l(cs);
+	OnlineUserList ret;
+	ret.reserve(users.size());
+	for(auto& i: users)
+		ret.push_back(i.second);
+	return ret;
+}
+
 } // namespace dcpp

=== modified file 'dcpp/AdcHub.h'
--- dcpp/AdcHub.h	2012-05-15 23:26:22 +0000
+++ dcpp/AdcHub.h	2012-06-22 14:46:44 +0000
@@ -77,18 +77,11 @@
 	friend class Identity;
 
 	AdcHub(const string& aHubURL, bool secure);
-
-	AdcHub(const AdcHub&);
-	AdcHub& operator=(const AdcHub&);
 	virtual ~AdcHub();
 
-	/** Map session id to OnlineUser */
-	typedef unordered_map<uint32_t, OnlineUser*> SIDMap;
-	typedef SIDMap::iterator SIDIter;
-
 	bool oldPassword;
 	Socket udp;
-	SIDMap users;
+	unordered_map<uint32_t, OnlineUser*> users; /** Map session id to OnlineUser */
 	StringMap lastInfoMap;
 	mutable CriticalSection cs;
 
@@ -141,6 +134,7 @@
 
 	virtual void on(Second, uint64_t aTick) noexcept;
 
+	OnlineUserList getUsers() const;
 };
 
 } // namespace dcpp

=== modified file 'dcpp/BufferedSocket.cpp'
--- dcpp/BufferedSocket.cpp	2012-03-22 19:20:44 +0000
+++ dcpp/BufferedSocket.cpp	2012-06-22 14:46:44 +0000
@@ -444,8 +444,8 @@
 
 		if(p.first == SHUTDOWN) {
 			return false;
-		} else if(p.first == UPDATED) {
-			fire(BufferedSocketListener::Updated());
+		} else if(p.first == ASYNC_CALL) {
+			static_cast<CallData*>(p.second.get())->f();
 			continue;
 		}
 
@@ -522,7 +522,7 @@
 }
 
 void BufferedSocket::addTask(Tasks task, TaskData* data) {
-	dcassert(task == DISCONNECT || task == SHUTDOWN || task == UPDATED || sock.get());
+	dcassert(task == DISCONNECT || task == SHUTDOWN || sock.get());
 	tasks.push_back(make_pair(task, unique_ptr<TaskData>(data))); taskSem.signal();
 }
 

=== modified file 'dcpp/BufferedSocket.h'
--- dcpp/BufferedSocket.h	2012-01-13 20:55:20 +0000
+++ dcpp/BufferedSocket.h	2012-06-22 14:46:44 +0000
@@ -34,6 +34,7 @@
 namespace dcpp {
 
 using std::deque;
+using std::function;
 using std::pair;
 using std::unique_ptr;
 
@@ -98,8 +99,8 @@
 	/** Send the file f over this socket. */
 	void transmitFile(InputStream* f) { Lock l(cs); addTask(SEND_FILE, new SendFileInfo(f)); }
 
-	/** Send an updated signal to all listeners */
-	void updated() { Lock l(cs); addTask(UPDATED, 0); }
+	/** Call a function from the socket's thread. */
+	void callAsync(function<void ()> f) { Lock l(cs); addTask(ASYNC_CALL, new CallData(f)); }
 
 	void disconnect(bool graceless = false) noexcept { Lock l(cs); if(graceless) disconnecting = true; addTask(DISCONNECT, 0); }
 
@@ -115,7 +116,7 @@
 		SEND_FILE,
 		SHUTDOWN,
 		ACCEPTED,
-		UPDATED
+		ASYNC_CALL
 	};
 
 	enum State {
@@ -139,6 +140,10 @@
 		SendFileInfo(InputStream* stream_) : stream(stream_) { }
 		InputStream* stream;
 	};
+	struct CallData : public TaskData {
+		CallData(function<void ()> f) : f(f) { }
+		function<void ()> f;
+	};
 
 	BufferedSocket(char aSeparator, bool v4only);
 

=== modified file 'dcpp/BufferedSocketListener.h'
--- dcpp/BufferedSocketListener.h	2012-01-13 20:55:20 +0000
+++ dcpp/BufferedSocketListener.h	2012-06-22 14:46:44 +0000
@@ -39,7 +39,6 @@
 	typedef X<5> ModeChange;
 	typedef X<6> TransmitDone;
 	typedef X<7> Failed;
-	typedef X<8> Updated;
 
 	virtual void on(Connecting) noexcept { }
 	virtual void on(Connected) noexcept { }
@@ -49,7 +48,6 @@
 	virtual void on(ModeChange) noexcept { }
 	virtual void on(TransmitDone) noexcept { }
 	virtual void on(Failed, const string&) noexcept { }
-	virtual void on(Updated) noexcept { }
 };
 
 } // namespace dcpp

=== modified file 'dcpp/Client.cpp'
--- dcpp/Client.cpp	2012-06-21 18:52:47 +0000
+++ dcpp/Client.cpp	2012-06-22 14:46:44 +0000
@@ -206,6 +206,11 @@
 		counts[COUNT_NORMAL].load(), counts[COUNT_REGISTERED].load(), counts[COUNT_OP].load()));
 }
 
+void Client::updateUsers() {
+	// this is a public call, can come from any thread. bring it to this hub's thread.
+	if(sock) sock->callAsync([this] { auto users = getUsers(); updated(users); });
+}
+
 void Client::updated(OnlineUser& user) {
 	UserMatchManager::getInstance()->match(user);
 

=== modified file 'dcpp/Client.h'
--- dcpp/Client.h	2012-05-24 17:47:25 +0000
+++ dcpp/Client.h	2012-06-22 14:46:44 +0000
@@ -21,15 +21,16 @@
 
 #include "compiler.h"
 
+#include "atomic.h"
+#include "BufferedSocketListener.h"
+#include "ClientListener.h"
 #include "forward.h"
-
 #include "HubSettings.h"
+#include "OnlineUser.h"
 #include "Speaker.h"
-#include "BufferedSocketListener.h"
 #include "TimerManager.h"
-#include "ClientListener.h"
-#include "OnlineUser.h"
-#include "atomic.h"
+
+#include <boost/noncopyable.hpp>
 
 namespace dcpp {
 
@@ -38,7 +39,8 @@
 	public Speaker<ClientListener>,
 	public BufferedSocketListener,
 	protected TimerManagerListener,
-	public HubSettings
+	public HubSettings,
+	private boost::noncopyable
 {
 public:
 	virtual void connect();
@@ -71,8 +73,8 @@
 	const string& getIp() const { return ip; }
 	string getIpPort() const { return getIp() + ':' + port; }
 
-	void updated(OnlineUser& user);
-	void updated(OnlineUserList& users);
+	/** Send a ClientListener::Updated signal for every connected user. */
+	void updateUsers();
 
 	static string getCounts();
 
@@ -128,6 +130,9 @@
 	void updateCounts(bool aRemove);
 	void updateActivity() { lastActivity = GET_TICK(); }
 
+	void updated(OnlineUser& user);
+	void updated(OnlineUserList& users);
+
 	/** Reload details from favmanager or settings */
 	void reloadSettings(bool updateNick);
 	/// Get the external IP the user has defined for this hub, if any.
@@ -137,6 +142,7 @@
 
 	// TimerManagerListener
 	virtual void on(Second, uint64_t aTick) noexcept;
+
 	// BufferedSocketListener
 	virtual void on(Connecting) noexcept { fire(ClientListener::Connecting(), this); }
 	virtual void on(Connected) noexcept;
@@ -144,10 +150,9 @@
 	virtual void on(Failed, const string&) noexcept;
 
 	virtual bool v4only() const = 0;
+
 private:
-
-	Client(const Client&);
-	Client& operator=(const Client&);
+	virtual OnlineUserList getUsers() const = 0;
 
 	string hubUrl;
 	string address;

=== modified file 'dcpp/DownloadManager.cpp'
--- dcpp/DownloadManager.cpp	2012-06-21 18:52:47 +0000
+++ dcpp/DownloadManager.cpp	2012-06-22 14:46:44 +0000
@@ -106,12 +106,24 @@
 	Lock l(cs);
 	for(auto uc: idlers) {
 		if(uc->getUser() == user) {
-			uc->updated();
+			uc->callAsync([this, uc] { revive(uc); });
 			return;
 		}
 	}
 }
 
+void DownloadManager::revive(UserConnection* uc) {
+	{
+		Lock l(cs);
+		auto i = find(idlers.begin(), idlers.end(), uc);
+		if(i == idlers.end())
+			return;
+		idlers.erase(i);
+	}
+
+	checkDownloads(uc);
+}
+
 void DownloadManager::addConnection(UserConnectionPtr conn) {
 	if(!conn->isSet(UserConnection::FLAG_SUPPORTS_TTHF) || !conn->isSet(UserConnection::FLAG_SUPPORTS_ADCGET)) {
 		// Can't download from these...
@@ -432,18 +444,6 @@
 	aSource->disconnect();
 }
 
-void DownloadManager::on(UserConnectionListener::Updated, UserConnection* aSource) noexcept {
-	{
-		Lock l(cs);
-		auto i = find(idlers.begin(), idlers.end(), aSource);
-		if(i == idlers.end())
-			return;
-		idlers.erase(i);
-	}
-
-	checkDownloads(aSource);
-}
-
 void DownloadManager::fileNotAvailable(UserConnection* aSource) {
 	if(aSource->getState() != UserConnection::STATE_SND) {
 		dcdebug("DM::fileNotAvailable Invalid state, disconnecting");

=== modified file 'dcpp/DownloadManager.h'
--- dcpp/DownloadManager.h	2012-01-13 20:55:20 +0000
+++ dcpp/DownloadManager.h	2012-06-22 14:46:44 +0000
@@ -78,6 +78,7 @@
 	virtual ~DownloadManager();
 
 	void checkDownloads(UserConnection* aConn);
+	void revive(UserConnection* uc);
 	void startData(UserConnection* aSource, int64_t start, int64_t newSize, bool z);
 	void endData(UserConnection* aSource);
 
@@ -89,7 +90,6 @@
 	virtual void on(ProtocolError, UserConnection* aSource, const string& aError) noexcept { onFailed(aSource, aError); }
 	virtual void on(MaxedOut, UserConnection*) noexcept;
 	virtual	void on(FileNotAvailable, UserConnection*) noexcept;
-	virtual void on(Updated, UserConnection*) noexcept;
 
 	virtual void on(AdcCommand::SND, UserConnection*, const AdcCommand&) noexcept;
 	virtual void on(AdcCommand::STA, UserConnection*, const AdcCommand&) noexcept;

=== modified file 'dcpp/NmdcHub.cpp'
--- dcpp/NmdcHub.cpp	2012-06-08 15:27:48 +0000
+++ dcpp/NmdcHub.cpp	2012-06-22 14:46:44 +0000
@@ -128,7 +128,7 @@
 }
 
 void NmdcHub::clearUsers() {
-	NickMap u2;
+	decltype(users) u2;
 
 	{
 		Lock l(cs);
@@ -1017,4 +1017,13 @@
 	}
 }
 
+OnlineUserList NmdcHub::getUsers() const {
+	Lock l(cs);
+	OnlineUserList ret;
+	ret.reserve(users.size());
+	for(auto& i: users)
+		ret.push_back(i.second);
+	return ret;
+}
+
 } // namespace dcpp

=== modified file 'dcpp/NmdcHub.h'
--- dcpp/NmdcHub.h	2012-05-23 22:05:22 +0000
+++ dcpp/NmdcHub.h	2012-06-22 14:46:44 +0000
@@ -67,10 +67,7 @@
 
 	mutable CriticalSection cs;
 
-	typedef unordered_map<string, OnlineUser*, noCaseStringHash, noCaseStringEq> NickMap;
-	typedef NickMap::iterator NickIter;
-
-	NickMap users;
+	unordered_map<string, OnlineUser*, noCaseStringHash, noCaseStringEq> users;
 
 	int supportFlags;
 
@@ -92,10 +89,6 @@
 	NmdcHub(const string& aHubURL);
 	virtual ~NmdcHub();
 
-	// Dummy
-	NmdcHub(const NmdcHub&);
-	NmdcHub& operator=(const NmdcHub&);
-
 	void clearUsers();
 	void onLine(const string& aLine) noexcept;
 
@@ -132,6 +125,7 @@
 	virtual void on(Line, const string& l) noexcept;
 	virtual void on(Failed, const string&) noexcept;
 
+	OnlineUserList getUsers() const;
 };
 
 } // namespace dcpp

=== modified file 'dcpp/UserConnection.cpp'
--- dcpp/UserConnection.cpp	2012-06-21 18:52:47 +0000
+++ dcpp/UserConnection.cpp	2012-06-22 14:46:44 +0000
@@ -212,10 +212,6 @@
 	fire(UserConnectionListener::TransmitDone(), this);
 }
 
-void UserConnection::on(Updated) noexcept {
-	fire(UserConnectionListener::Updated(), this);
-}
-
 void UserConnection::on(Failed, const string& aLine) noexcept {
 	setState(STATE_UNCONNECTED);
 	fire(UserConnectionListener::Failed(), this, aLine);

=== modified file 'dcpp/UserConnection.h'
--- dcpp/UserConnection.h	2012-01-13 20:55:20 +0000
+++ dcpp/UserConnection.h	2012-06-22 14:46:44 +0000
@@ -126,7 +126,8 @@
 	void connect(const string& aServer, const string& aPort, const string& localPort, const BufferedSocket::NatRoles natRole);
 	void accept(const Socket& aServer);
 
-	void updated() { if(socket) socket->updated(); }
+	template<typename F>
+	void callAsync(F f) { if(socket) socket->callAsync(f); }
 
 	void disconnect(bool graceless = false) { if(socket) socket->disconnect(graceless); }
 	void transmitFile(InputStream* f) { socket->transmitFile(f); }
@@ -212,7 +213,6 @@
 	virtual void on(ModeChange) noexcept;
 	virtual void on(TransmitDone) noexcept;
 	virtual void on(Failed, const string&) noexcept;
-	virtual void on(Updated) noexcept;
 };
 
 } // namespace dcpp

=== modified file 'dcpp/UserMatchManager.cpp'
--- dcpp/UserMatchManager.cpp	2012-06-18 15:56:01 +0000
+++ dcpp/UserMatchManager.cpp	2012-06-22 14:46:44 +0000
@@ -51,8 +51,8 @@
 	const_cast<UserMatches&>(list) = move(newList);
 
 	// refresh user matches.
-	for(auto& i: cm->getOnlineUsers()) {
-		i.second->getClient().updated(*i.second);
+	for(auto i: cm->getClients()) {
+		i->updateUsers();
 	}
 }
 
@@ -89,6 +89,18 @@
 }
 
 void UserMatchManager::ignoreChat(const HintedUser& user, bool ignore) {
+	setList(ignoreChatImpl(list, user, ignore));
+}
+
+void UserMatchManager::ignoreChat(const HintedUserList& users, bool ignore) {
+	auto newList = list;
+	for(auto& user: users) {
+		newList = ignoreChatImpl(newList, user, ignore);
+	}
+	setList(move(newList));
+}
+
+UserMatchManager::UserMatches UserMatchManager::ignoreChatImpl(const UserMatches& list, const HintedUser& user, bool ignore) {
 	auto nick = ClientManager::getInstance()->getNicks(user)[0];
 
 	UserMatch matcher;
@@ -132,7 +144,7 @@
 	}
 
 	newList.insert(newList.begin(), std::move(matcher));
-	setList(std::move(newList));
+	return newList;
 }
 
 void UserMatchManager::on(SettingsManagerListener::Load, SimpleXML& xml) noexcept {

=== modified file 'dcpp/UserMatchManager.h'
--- dcpp/UserMatchManager.h	2012-01-13 20:55:20 +0000
+++ dcpp/UserMatchManager.h	2012-06-22 14:46:44 +0000
@@ -44,6 +44,7 @@
 	void match(OnlineUser& user) const;
 
 	void ignoreChat(const HintedUser& user, bool ignore);
+	void ignoreChat(const HintedUserList& users, bool ignore);
 
 private:
 	friend class Singleton<UserMatchManager>;
@@ -53,6 +54,8 @@
 	UserMatchManager();
 	virtual ~UserMatchManager();
 
+	static UserMatches ignoreChatImpl(const UserMatches& list, const HintedUser& user, bool ignore);
+
 	void on(SettingsManagerListener::Load, SimpleXML& xml) noexcept;
 	void on(SettingsManagerListener::Save, SimpleXML& xml) noexcept;
 };

=== modified file 'win32/UserInfoBase.cpp'
--- win32/UserInfoBase.cpp	2012-05-30 17:28:37 +0000
+++ win32/UserInfoBase.cpp	2012-06-22 14:46:44 +0000
@@ -80,6 +80,10 @@
 	UserMatchManager::getInstance()->ignoreChat(user, ignore);
 }
 
+void UserInfoBase::ignoreChat(const HintedUserList& users, bool ignore) {
+	UserMatchManager::getInstance()->ignoreChat(users, ignore);
+}
+
 tstring UserInfoBase::getTooltip() const {
 	static const size_t maxChars = 100; // max chars per tooltip line
 

=== modified file 'win32/UserInfoBase.h'
--- win32/UserInfoBase.h	2012-06-08 15:27:48 +0000
+++ win32/UserInfoBase.h	2012-06-22 14:46:44 +0000
@@ -47,6 +47,7 @@
 	virtual void removeFromQueue();
 	virtual void connectFav(TabViewPtr);
 	virtual void ignoreChat(bool ignore);
+	static void ignoreChat(const HintedUserList& users, bool ignore);
 
 	tstring getTooltip() const;
 
@@ -127,7 +128,10 @@
 		handleUserFunction([&](UserInfoBase* u) { u->connectFav(parent); });
 	}
 	void handleIgnoreChat(bool ignore) {
-		handleUserFunction([ignore](UserInfoBase* u) { u->ignoreChat(ignore); });
+		// group multiple ignore calls into a single one.
+		HintedUserList users;
+		handleUserFunction([&users](UserInfoBase* u) { users.push_back(u->getUser()); });
+		UserInfoBase::ignoreChat(users, ignore);
 	}
 
 	void appendUserItems(TabViewPtr parent, Menu* menu, bool defaultIsGetList = true, bool includeSendPM = true) {