← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 2962: rework user matching synchronization

 

------------------------------------------------------------
revno: 2962
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Sat 2012-06-23 12:44:48 +0200
message:
  rework user matching synchronization
modified:
  dcpp/ClientManager.cpp
  dcpp/ClientManager.h
  dcpp/UserMatchManager.cpp
  dcpp/UserMatchManager.h
  win32/UserInfoBase.cpp
  win32/UserInfoBase.h
  win32/WinUtil.cpp


--
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 'dcpp/ClientManager.cpp'
--- dcpp/ClientManager.cpp	2012-06-21 18:52:47 +0000
+++ dcpp/ClientManager.cpp	2012-06-23 10:44:48 +0000
@@ -303,6 +303,13 @@
 	return CID(th.finalize());
 }
 
+void ClientManager::updateUsers() {
+	Lock l(cs);
+	for(auto client: clients) {
+		client->updateUsers();
+	}
+}
+
 void ClientManager::putOnline(OnlineUser* ou) noexcept {
 	{
 		Lock l(cs);
@@ -359,11 +366,15 @@
 	return 0;
 }
 
-OnlineUser* ClientManager::findOnlineUser(const HintedUser& user) {
+OnlineUser* ClientManager::findOnlineUser(const HintedUser& user) const {
 	return findOnlineUser(user.user->getCID(), user.hint);
 }
 
-OnlineUser* ClientManager::findOnlineUser(const CID& cid, const string& hintUrl) {
+OnlineUser* ClientManager::findOnlineUserHint(const HintedUser& user) const {
+	return findOnlineUserHint(user.user->getCID(), user.hint);
+}
+
+OnlineUser* ClientManager::findOnlineUser(const CID& cid, const string& hintUrl) const {
 	OnlinePairC p;
 	OnlineUser* u = findOnlineUserHint(cid, hintUrl, p);
 	if(u) // found an exact match (CID + hint).

=== modified file 'dcpp/ClientManager.h'
--- dcpp/ClientManager.h	2012-05-15 23:26:22 +0000
+++ dcpp/ClientManager.h	2012-06-23 10:44:48 +0000
@@ -92,9 +92,10 @@
 	/** Get an OnlineUser object - lock it with lock()!
 	* @return OnlineUser* found by CID, using the hub URL as a hint.
 	*/
-	OnlineUser* findOnlineUser(const HintedUser& user);
-	OnlineUser* findOnlineUser(const CID& cid, const string& hintUrl);
+	OnlineUser* findOnlineUser(const HintedUser& user) const;
+	OnlineUser* findOnlineUser(const CID& cid, const string& hintUrl) const;
 	/// @return OnlineUser* found by CID and hint; discard any user that doesn't match the hint.
+	OnlineUser* findOnlineUserHint(const HintedUser& user) const;
 	OnlineUser* findOnlineUserHint(const CID& cid, const string& hintUrl) const;
 
 	UserPtr findUser(const string& aNick, const string& aHubUrl) const noexcept { return findUser(makeCid(aNick, aHubUrl)); }
@@ -111,6 +112,8 @@
 	/** Constructs a synthetic, hopefully unique CID */
 	CID makeCid(const string& nick, const string& hubUrl) const noexcept;
 
+	/** Send a ClientListener::Updated signal for every connected user. */
+	void updateUsers();
 	void putOnline(OnlineUser* ou) noexcept;
 	void putOffline(OnlineUser* ou, bool disconnect = false) noexcept;
 

=== modified file 'dcpp/UserMatchManager.cpp'
--- dcpp/UserMatchManager.cpp	2012-06-22 14:46:44 +0000
+++ dcpp/UserMatchManager.cpp	2012-06-23 10:44:48 +0000
@@ -35,28 +35,30 @@
 	SettingsManager::getInstance()->removeListener(this);
 }
 
-const UserMatchManager::UserMatches& UserMatchManager::getList() const {
+UserMatchManager::UserMatches UserMatchManager::getList() const {
+	boost::shared_lock<boost::shared_mutex> lock(mutex);
 	return list;
 }
 
-void UserMatchManager::setList(UserMatches&& newList) {
+void UserMatchManager::setList(UserMatches&& newList, bool updateUsers) {
 	if(list.empty() && newList.empty())
 		return;
 
-	// operate within the ClientManager lock.
-	auto cm = ClientManager::getInstance();
-	auto lock = cm->lock();
-
-	// assign the new list.
-	const_cast<UserMatches&>(list) = move(newList);
-
-	// refresh user matches.
-	for(auto i: cm->getClients()) {
-		i->updateUsers();
+	{
+		boost::unique_lock<boost::shared_mutex> lock(mutex);
+		const_cast<UserMatches&>(list) = move(newList);
+	}
+
+	if(updateUsers) {
+		/* this is here as a convenience because user properties have to be refreshed most of the
+		time when the matching list has been modified. */
+		ClientManager::getInstance()->updateUsers();
 	}
 }
 
 void UserMatchManager::match(OnlineUser& user) const {
+	boost::shared_lock<boost::shared_mutex> lock(mutex);
+
 	auto& identity = user.getIdentity();
 
 	bool chatSet = false;
@@ -88,20 +90,40 @@
 	identity.setStyle(std::move(style));
 }
 
+pair<bool, bool> UserMatchManager::checkPredef() const {
+	boost::shared_lock<boost::shared_mutex> lock(mutex);
+	pair<bool, bool> ret(false, false);
+	for(auto& i: list) {
+		if(i.isSet(UserMatch::PREDEFINED)) {
+			if(i.isSet(UserMatch::FAVS)) { ret.first = true; }
+			else if(i.isSet(UserMatch::OPS)) { ret.second = true; }
+			if(ret.first && ret.second) { break; }
+		}
+	}
+	return ret;
+}
+
+unordered_set<string> UserMatchManager::getFonts() const {
+	boost::shared_lock<boost::shared_mutex> lock(mutex);
+	unordered_set<string> ret;
+	for(auto& i: list) {
+		if(!i.style.font.empty()) {
+			ret.insert(i.style.font);
+		}
+	}
+	return ret;
+}
+
 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);
+	auto lock = ClientManager::getInstance()->lock();
+	auto ou = ClientManager::getInstance()->findOnlineUserHint(user);
+	if(!ou) {
+		return;
 	}
-	setList(move(newList));
-}
-
-UserMatchManager::UserMatches UserMatchManager::ignoreChatImpl(const UserMatches& list, const HintedUser& user, bool ignore) {
-	auto nick = ClientManager::getInstance()->getNicks(user)[0];
+	auto nick = ou->getIdentity().getNick();
+	// no need to re-match all users; just update this one.
+	ou->getIdentity().setNoChat(ignore);
+	lock.unlock();
 
 	UserMatch matcher;
 	matcher.setFlag(UserMatch::GENERATED);
@@ -144,7 +166,7 @@
 	}
 
 	newList.insert(newList.begin(), std::move(matcher));
-	return newList;
+	setList(move(newList), false);
 }
 
 void UserMatchManager::on(SettingsManagerListener::Load, SimpleXML& xml) noexcept {
@@ -196,6 +218,8 @@
 }
 
 void UserMatchManager::on(SettingsManagerListener::Save, SimpleXML& xml) noexcept {
+	boost::shared_lock<boost::shared_mutex> lock(mutex);
+
 	xml.addTag("UserMatches");
 	xml.stepIn();
 	for(auto& i: list) {

=== modified file 'dcpp/UserMatchManager.h'
--- dcpp/UserMatchManager.h	2012-06-22 14:46:44 +0000
+++ dcpp/UserMatchManager.h	2012-06-23 10:44:48 +0000
@@ -19,43 +19,61 @@
 #ifndef DCPLUSPLUS_DCPP_USER_MATCH_MANAGER_H
 #define DCPLUSPLUS_DCPP_USER_MATCH_MANAGER_H
 
+#include <string>
+#include <unordered_set>
+#include <vector>
+
 #include "forward.h"
 #include "SettingsManager.h"
+#include "Singleton.h"
 #include "UserMatch.h"
 
+#include <boost/thread/locks.hpp>
+#include <boost/thread/shared_mutex.hpp>
+
 namespace dcpp {
 
-/** This class manages user matching definitions. The list doesn't have to be locked; instead,
-special care is taken when updating it. */
+using std::string;
+using std::unordered_set;
+using std::vector;
+
+/** This class manages user matching definitions. Online users are matched against these
+definitions to find properties to apply (such as chat ignoring, custom colors...). */
 class UserMatchManager :
 	public Singleton<UserMatchManager>,
 	private SettingsManagerListener
 {
-	typedef std::vector<UserMatch> UserMatches;
+	typedef vector<UserMatch> UserMatches;
 
 public:
-	/// Retrieve the list of user matching definitions.
-	const UserMatches& getList() const;
-	/// Assign a new list of user matching definitions. All current users will be re-matched.
-	void setList(UserMatches&& newList);
+	/** Retrieve the list of user matching definitions. */
+	UserMatches getList() const;
+	/** Assign a new list of user matching definitions. */
+	void setList(UserMatches&& newList, bool updateUsers = true);
 
 	/** Match the given user against current user matching definitions. The user's identity object
 	will be modified accordingly. */
 	void match(OnlineUser& user) const;
 
+	/** Helper function that tells whether predefined definitions for favorites (pair.first) and
+	operators (pair.second) are existing. */
+	pair<bool, bool> checkPredef() const;
+	/** Get a list of the fonts required by current user matching definitions. */
+	unordered_set<string> getFonts() const;
+	/** Add a user matching definition at the top of the list to match the given user and force her
+	to be ignored or un-ignored. If an untampered previously generated definition is already
+	existing, it is removed. */
 	void ignoreChat(const HintedUser& user, bool ignore);
-	void ignoreChat(const HintedUserList& users, bool ignore);
 
 private:
 	friend class Singleton<UserMatchManager>;
 
 	const UserMatches list; // const to make sure only setList can change this.
+	mutable boost::shared_mutex mutex; // shared to allow multiple readers (each hub).
 
 	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-06-22 14:46:44 +0000
+++ win32/UserInfoBase.cpp	2012-06-23 10:44:48 +0000
@@ -80,10 +80,6 @@
 	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-22 14:46:44 +0000
+++ win32/UserInfoBase.h	2012-06-23 10:44:48 +0000
@@ -47,7 +47,6 @@
 	virtual void removeFromQueue();
 	virtual void connectFav(TabViewPtr);
 	virtual void ignoreChat(bool ignore);
-	static void ignoreChat(const HintedUserList& users, bool ignore);
 
 	tstring getTooltip() const;
 
@@ -128,10 +127,7 @@
 		handleUserFunction([&](UserInfoBase* u) { u->connectFav(parent); });
 	}
 	void handleIgnoreChat(bool ignore) {
-		// group multiple ignore calls into a single one.
-		HintedUserList users;
-		handleUserFunction([&users](UserInfoBase* u) { users.push_back(u->getUser()); });
-		UserInfoBase::ignoreChat(users, ignore);
+		handleUserFunction([ignore](UserInfoBase* u) { u->ignoreChat(ignore); });
 	}
 
 	void appendUserItems(TabViewPtr parent, Menu* menu, bool defaultIsGetList = true, bool includeSendPM = true) {

=== modified file 'win32/WinUtil.cpp'
--- win32/WinUtil.cpp	2012-06-21 18:52:47 +0000
+++ win32/WinUtil.cpp	2012-06-23 10:44:48 +0000
@@ -444,20 +444,12 @@
 
 void WinUtil::initUserMatching() {
 	// make sure predefined definitions are here.
-	bool favDefHere = false, opDefHere = false;
-	const auto& list = UserMatchManager::getInstance()->getList();
-	for(auto& i: list) {
-		if(i.isSet(UserMatch::PREDEFINED)) {
-			if(i.isSet(UserMatch::FAVS)) { favDefHere = true; }
-			else if(i.isSet(UserMatch::OPS)) { opDefHere = true; }
-			if(favDefHere && opDefHere) { break; }
-		}
-	}
-
-	if(!favDefHere || !opDefHere) {
-		auto newList = list;
-
-		if(!favDefHere) {
+	auto here = UserMatchManager::getInstance()->checkPredef();
+
+	if(!here.first || !here.second) {
+		auto newList = UserMatchManager::getInstance()->getList();
+
+		if(!here.first) {
 			// add a matcher for favs.
 			UserMatch matcher;
 			matcher.setFlag(UserMatch::PREDEFINED);
@@ -468,7 +460,7 @@
 			newList.push_back(std::move(matcher));
 		}
 
-		if(!opDefHere) {
+		if(!here.second) {
 			// add a matcher for ops.
 			UserMatch matcher;
 			matcher.setFlag(UserMatch::PREDEFINED);
@@ -487,12 +479,10 @@
 void WinUtil::updateUserMatchFonts() {
 	userMatchFonts.clear();
 
-	for(auto& i: UserMatchManager::getInstance()->getList()) {
-		if(!i.style.font.empty()) {
-			LOGFONT lf;
-			decodeFont(Text::toT(i.style.font), lf);
-			userMatchFonts[i.style.font] = new dwt::Font(lf);
-		}
+	for(auto& font: UserMatchManager::getInstance()->getFonts()) {
+		LOGFONT lf;
+		decodeFont(Text::toT(font), lf);
+		userMatchFonts[font] = new dwt::Font(lf);
 	}
 }