# # # add_file "src/util/LRUCache.h" # content [bc4dc5c7b84b9353bd08d543bd0c2080188309fe] # # patch "guitone.pro" # from [6f299c589c6eefae31cf4e612f0e021ebf104903] # to [427722d346990785a3b3598c7bc49d40f606c179] # # patch "src/model/InventoryModel.cpp" # from [6497e49ed857bcace63fa6191430ac4fffd956a6] # to [b306a75c202e78afa1726f3706ed7363bb17debf] # # patch "src/model/InventoryWatcher.cpp" # from [f84de58ddd0f6adb881e5c0cf36ed755dfcb718e] # to [d0a2d88d832ccd43b95b15642a89fe2454171a38] # # patch "src/model/InventoryWatcher.h" # from [1de9c4f05371358e6d73844c44f4c819ceb04e35] # to [ae3f783cea8382a2592f58468a39a76f33b16036] # ============================================================ --- src/util/LRUCache.h bc4dc5c7b84b9353bd08d543bd0c2080188309fe +++ src/util/LRUCache.h bc4dc5c7b84b9353bd08d543bd0c2080188309fe @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (C) 2010 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifndef LRU_CACHE_H +#define LRU_CACHE_H + +#include "vocab.h" + +#include +#include + +template +class LRUCache +{ +public: + LRUCache(int max) + { + I(max > 0); + maxItems = max; + } + + T add(const T & key, int initial = 1, int update = 1) + { + T removed; + if (!contains(key)) + { + if (size() == maxItems) + { + removed = removeOldest(); + } + counter.insert(key, initial); + revCounter.insert(initial, key); + } + else + { + int oldHeat = counter.value(key); + counter[key] = oldHeat + update; + revCounter.remove(oldHeat, key); + revCounter.insert(oldHeat + update, key); + } + return removed; + } + + void remove(const T & key) + { + if (!contains(key)) + return; + + int heat = counter.take(key); + I(revCounter.remove(heat, key) == 1); + } + + T removeOldest() + { + // the keys already come in ascending order + QList keys = revCounter.uniqueKeys(); + I(keys.size() > 0); + + int heat = keys.at(0); + + QList vals = revCounter.values(heat); + I(vals.size() > 0); + + I(counter.remove(vals.last()) == 1); + I(revCounter.remove(heat, vals.last()) == 1); + return vals.last(); + } + + bool contains(const T & key) const + { + return counter.contains(key); + } + + int size() const + { + return counter.size(); + } + + void clear() + { + counter.clear(); + revCounter.clear(); + } + +private: + int maxItems; + QMap counter; + QMultiMap revCounter; +}; + +#endif + ============================================================ --- guitone.pro 6f299c589c6eefae31cf4e612f0e021ebf104903 +++ guitone.pro 427722d346990785a3b3598c7bc49d40f606c179 @@ -109,6 +109,7 @@ HEADERS = src/view/widgets/TreeView.h \ src/util/TreeBuilder.h \ src/util/DebugLog.h \ src/util/StdioParser.h \ + src/util/LRUCache.h \ src/GuitoneCore.h \ src/GuitoneStandalone.h \ src/GuitoneDriver.h \ ============================================================ --- src/model/InventoryModel.cpp 6497e49ed857bcace63fa6191430ac4fffd956a6 +++ src/model/InventoryModel.cpp b306a75c202e78afa1726f3706ed7363bb17debf @@ -330,7 +330,10 @@ void InventoryModel::triggerEndInsertRow if (invItem->getFSType() == InventoryItem::None) continue; // a little optimization: we don't care about / monitor ignored files if (invItem->hasStatus(InventoryItem::Ignored)) continue; - paths.push_back(invItem->getPath()); + + QString path = invItem->getPath(); + if (invItem->isDirectory()) path += "/"; + paths.push_back(path); } delete insertion; @@ -349,7 +352,10 @@ void InventoryModel::triggerBeginRemoveR ModelItem * child = parent->child(i); InventoryItem * invItem = qobject_cast(child); if (!invItem) continue; - paths.push_back(invItem->getPath()); + + QString path = invItem->getPath(); + if (invItem->isDirectory()) path += "/"; + paths.push_back(path); } emit pathsRemoved(paths); ============================================================ --- src/model/InventoryWatcher.cpp f84de58ddd0f6adb881e5c0cf36ed755dfcb718e +++ src/model/InventoryWatcher.cpp d0a2d88d832ccd43b95b15642a89fe2454171a38 @@ -21,7 +21,9 @@ InventoryWatcher::InventoryWatcher(QObje #include "BasicIOParser.h" InventoryWatcher::InventoryWatcher(QObject * parent) - : QFileSystemWatcher(parent) + : QFileSystemWatcher(parent), + // do not watch more than 200 paths at once + lruCache(200) { connect( this, SIGNAL(directoryChanged(const QString &)), @@ -72,9 +74,25 @@ void InventoryWatcher::watchPaths(const foreach (const QString & path, paths) { QString fullPath(workspace); - if (!path.isEmpty()) fullPath += "/" + path; + if (path != "/") fullPath += "/" + path; if (watchedPaths.indexOf(fullPath) != -1) continue; - addPath(fullPath); + D(QString("adding %1").arg(fullPath)); + removePath(fullPath); + + // directories end with a slash and should keep a little longer + // around, thats why we give them one additional heat point + // more initially + QString removedPath = lruCache.add( + path, path.right(1) == "/" ? 2 : 1, 1 + ); + + if (!removedPath.isEmpty()) + { + QString fullRemovedPath(workspace); + if (removedPath != "/") fullRemovedPath += "/" + removedPath; + D(QString("removing %1 because of LRU").arg(fullRemovedPath)); + removePath(fullRemovedPath); + } } } @@ -85,9 +103,10 @@ void InventoryWatcher::unwatchPaths(cons foreach (const QString & path, paths) { QString fullPath(workspace); - if (!path.isEmpty()) fullPath += "/" + path; + if (path != "/") fullPath += "/" + path; if (watchedPaths.indexOf(fullPath) == -1) continue; removePath(fullPath); + lruCache.remove(path); } } @@ -104,12 +123,12 @@ void InventoryWatcher::pathChanged(const return; } - addPathAndStartTimer( + markPathForNotification( path.mid(path.indexOf(workspace) + workspace.length() + 1) ); } -void InventoryWatcher::addPathAndStartTimer(const QString & newPath) +void InventoryWatcher::markPathForNotification(const QString & newPath) { // // The following algorithm only picks those paths for an update @@ -300,14 +319,14 @@ void InventoryWatcher::checkForBookkeepC if (foundOrAlreadyHandled) continue; - addPathAndStartTimer(newStanza.at(0).vals.at(0)); + markPathForNotification(newStanza.at(0).vals.at(0)); } // now loop through all the left old items; // we should only have a subset of items in this list now foreach (const Stanza & oldStanza, oldRevisionEntries) { - addPathAndStartTimer(oldStanza.at(0).vals.at(0)); + markPathForNotification(oldStanza.at(0).vals.at(0)); } oldRevisionEntries = newRevisionEntries; @@ -319,5 +338,6 @@ void InventoryWatcher::clearAllWatches() changedPaths.clear(); removePaths(directories()); removePaths(files()); + lruCache.clear(); } ============================================================ --- src/model/InventoryWatcher.h 1de9c4f05371358e6d73844c44f4c819ceb04e35 +++ src/model/InventoryWatcher.h ae3f783cea8382a2592f58468a39a76f33b16036 @@ -20,6 +20,8 @@ #define INVENTORY_WATCHER_H #include "vocab.h" +#include "LRUCache.h" + #include #include #include @@ -42,13 +44,14 @@ private: void clearAllWatches(); private: - void addPathAndStartTimer(const QString &); + void markPathForNotification(const QString &); void checkForBookkeepChanges(); WorkspacePath workspace; StanzaList oldRevisionEntries; QStringList changedPaths; QTimer * notifyTimer; + LRUCache lruCache; private slots: void pathChanged(const QString &);