From kde-commits Tue Apr 21 21:56:33 2009 From: David Faure Date: Tue, 21 Apr 2009 21:56:33 +0000 To: kde-commits Subject: branches/KDE/4.2/kdelibs/kio/kio Message-Id: <1240350993.927639.1270.nullmailer () svn ! kde ! org> X-MARC-Message: https://marc.info/?l=kde-commits&m=124035100820912 SVN commit 957379 by dfaure: Backport r907250 by sstjames: "Make proper use of inotify's implicit "WatchFiles" ability. Immediate effect: Dolphin etc should now report changes to file attributes, rather than just creations and deletions, when KDirWatch uses inotify as its backend. Secondary effect: Assuming no bugs, we should consume at most the same number of inotify watches as before (and often much less)." CCBUG: 154676 As a third effect, it fixes kdirlistertest being stuck in KDirListerTest::testRenameAndOverwrite() because kdirwatch wasn't informing kdirlister of the newly recreated file. M +63 -10 kdirwatch.cpp M +9 -3 kdirwatch_p.h --- branches/KDE/4.2/kdelibs/kio/kio/kdirwatch.cpp #957378:957379 @@ -339,7 +339,54 @@ } } } + if (event->mask & (IN_DELETE|IN_MOVED_FROM)) { + if ((e->isDir) && (!e->m_clients.empty())) { + Client* client = 0; + // A file in this directory has been removed. It wasn't an explicitly + // watched file as it would have its own watch descriptor, so + // no addEntry/ removeEntry bookkeeping should be required. Emit + // the event immediately if any clients are interested. + KDE_struct_stat stat_buf; + QByteArray tpath = QFile::encodeName(e->path + QLatin1Char('/') + path); + // Unlike clientsForFileOrDir, the stat can fail here (item deleted), + // so in that case we'll just take both kinds of clients and emit Deleted. + KDirWatch::WatchModes flag = KDirWatch::WatchSubDirs | KDirWatch::WatchFiles; + if (KDE_stat(tpath, &stat_buf) == 0) { + bool isDir = S_ISDIR(stat_buf.st_mode); + flag = isDir ? KDirWatch::WatchSubDirs : KDirWatch::WatchFiles; + } + int counter = 0; + Q_FOREACH(client, e->m_clients) { + if (client->m_watchModes & flag) { + counter++; + } + } + if (counter != 0) { + emitEvent (e, Deleted, e->path+'/'+path); + } + } + } + if (event->mask & (IN_MODIFY|IN_ATTRIB)) { + if ((e->isDir) && (!e->m_clients.empty())) { + // A file in this directory has been changed. No + // addEntry/ removeEntry bookkeeping should be required. + // Add the path to the list of pending file changes if + // there are any interested clients. + //KDE_struct_stat stat_buf; + //QByteArray tpath = QFile::encodeName(e->path+'/'+path); + //KDE_stat(tpath, &stat_buf); + //bool isDir = S_ISDIR(stat_buf.st_mode); + // The API doc is somewhat vague as to whether we should emit + // dirty() for implicitly watched files when WatchFiles has + // not been specified - we'll assume they are always interested, + // regardless. + // Don't worry about duplicates for the time + // being; this is handled in slotRescan. + e->m_pendingFileChanges.append(e->path+'/'+path); + } + } + if (!rescan_timer.isActive()) rescan_timer.start(m_PollInterval); // singleshot @@ -533,17 +580,9 @@ return true; } - int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW; - if(!e->isDir) - mask |= IN_MODIFY|IN_ATTRIB; - else - mask |= IN_ONLYDIR; + // May as well register for almost everything - it's free! + int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW|IN_MOVED_FROM|IN_MODIFY|IN_ATTRIB; - // if dependant is a file watch, we check for MODIFY & ATTRIB too - foreach(Entry *dep, e->m_entries) { - if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB; break; } - } - if ( ( e->wd = inotify_add_watch( m_inotify_fd, QFile::encodeName( e->path ), mask) ) > 0) { @@ -1236,6 +1275,20 @@ useStat( &(*it) ); } } + + if ((*it).isDir) + { + // Report and clear the the list of files that have changed in this directory. + // Remove duplicates by changing to set and back again: + // we don't really care about preserving the order of the + // original changes. + QList pendingFileChanges = (*it).m_pendingFileChanges.toSet().toList(); + Q_FOREACH(QString changedFilename, pendingFileChanges ) + { + emitEvent(&(*it), Changed, changedFilename); + } + (*it).m_pendingFileChanges.clear(); + } #endif if ( ev != NoChange ) --- branches/KDE/4.2/kdelibs/kio/kio/kdirwatch_p.h #957378:957379 @@ -72,11 +72,11 @@ * the maximum number of object handles is MAXIMUM_WAIT_OBJECTS (64) per thread. * * From http://msdn.microsoft.com/en-us/library/ms687025(VS.85).aspx - * "To wait on more than MAXIMUM_WAIT_OBJECTS handles, create a thread to wait - * on MAXIMUM_WAIT_OBJECTS handles, then wait on that thread plus the other handles. + * "To wait on more than MAXIMUM_WAIT_OBJECTS handles, create a thread to wait + * on MAXIMUM_WAIT_OBJECTS handles, then wait on that thread plus the other handles. * Use this technique to break the handles into groups of MAXIMUM_WAIT_OBJECTS." * - * QFileSystemWatcher is implemented as thread, so KFileSystemWatcher + * QFileSystemWatcher is implemented as thread, so KFileSystemWatcher * allocates more QFileSystemWatcher instances on demand (and deallocates them later). */ class KFileSystemWatcher : public QObject @@ -161,6 +161,12 @@ #ifdef HAVE_SYS_INOTIFY_H int wd; + // Creation and Deletion of files happens infrequently, so + // can safely be reported as they occur. File changes i.e. those that emity "dirty()" can + // happen many times per second, though, so maintain a list of files in this directory + // that can be emitted and flushed at the next slotRescan(...). + // This will be unused if the Entry is not a directory. + QList m_pendingFileChanges; #endif };