QNX: Use extra information in dirent to avoid stat() calls
authorThomas McGuire <thomas.mcguire.qnx@kdab.com>
Wed, 26 Sep 2012 15:57:14 +0000 (17:57 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 19 Oct 2012 11:02:31 +0000 (13:02 +0200)
This improves iterating over /usr/bin with QDirIterator by more
than half, from 36 to 13 milliseconds.

Change-Id: Ib3a9271c3a6f81c1ea3c21d012c875c7e9bad2ad
Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
Reviewed-by: Kevin Krammer <kevin.krammer@kdab.com>
Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
Reviewed-by: Rafael Roquetto <rafael.roquetto@kdab.com>
Reviewed-by: Stephen Kelly <stephen.kelly@kdab.com>
src/corelib/io/qfilesystemengine.cpp
src/corelib/io/qfilesystemiterator_p.h
src/corelib/io/qfilesystemiterator_unix.cpp

index 74ceeb1..fe06210 100644 (file)
@@ -224,6 +224,19 @@ bool QFileSystemEngine::fillMetaData(int fd, QFileSystemMetaData &data)
     return false;
 }
 
+#if defined(Q_OS_QNX)
+static void fillStat64fromStat32(struct stat64 *statBuf64, const struct stat &statBuf32)
+{
+    statBuf64->st_mode = statBuf32.st_mode;
+    statBuf64->st_size = statBuf32.st_size;
+    statBuf64->st_ctime = statBuf32.st_ctime;
+    statBuf64->st_mtime = statBuf32.st_mtime;
+    statBuf64->st_atime = statBuf32.st_atime;
+    statBuf64->st_uid = statBuf32.st_uid;
+    statBuf64->st_gid = statBuf32.st_gid;
+}
+#endif
+
 void QFileSystemMetaData::fillFromStatBuf(const QT_STATBUF &statBuffer)
 {
     // Permissions
@@ -277,7 +290,41 @@ void QFileSystemMetaData::fillFromStatBuf(const QT_STATBUF &statBuffer)
 
 void QFileSystemMetaData::fillFromDirEnt(const QT_DIRENT &entry)
 {
-#if defined(_DIRENT_HAVE_D_TYPE) || defined(Q_OS_BSD4)
+#if defined(Q_OS_QNX)
+    knownFlagsMask = 0;
+    entryFlags = 0;
+    for (dirent_extra *extra = _DEXTRA_FIRST(&entry); _DEXTRA_VALID(extra, &entry);
+         extra = _DEXTRA_NEXT(extra)) {
+        if (extra->d_type == _DTYPE_STAT || extra->d_type == _DTYPE_LSTAT) {
+
+            const struct dirent_extra_stat * const extra_stat =
+                    reinterpret_cast<struct dirent_extra_stat *>(extra);
+
+            // For symlinks, the extra type _DTYPE_LSTAT doesn't work for filling out the meta data,
+            // as we need the stat() information there, not the lstat() information.
+            // In this case, don't use the extra information.
+            // Unfortunately, readdir() never seems to return extra info of type _DTYPE_STAT, so for
+            // symlinks, we always incur the cost of an extra stat() call later.
+            if (S_ISLNK(extra_stat->d_stat.st_mode) && extra->d_type == _DTYPE_LSTAT)
+                continue;
+
+#if defined(__EXT_LF64SRC)
+            // Even with large file support, d_stat is always of type struct stat, not struct stat64,
+            // so it needs to be converted
+            struct stat64 statBuf;
+            fillStat64fromStat32(&statBuf, extra_stat->d_stat);
+            fillFromStatBuf(statBuf);
+#else
+            fillFromStatBuf(extra_stat->d_stat);
+#endif
+            knownFlagsMask |= QFileSystemMetaData::PosixStatFlags;
+            if (!S_ISLNK(extra_stat->d_stat.st_mode)) {
+                knownFlagsMask |= QFileSystemMetaData::ExistsAttribute;
+                entryFlags |= QFileSystemMetaData::ExistsAttribute;
+            }
+        }
+    }
+#elif defined(_DIRENT_HAVE_D_TYPE) || defined(Q_OS_BSD4)
     // BSD4 includes Mac OS X
 
     // ### This will clear all entry flags and knownFlagsMask
index 87395ec..a68615c 100644 (file)
@@ -98,6 +98,10 @@ private:
 #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN)
     // for readdir_r
     QScopedPointer<QT_DIRENT, QScopedPointerPodDeleter> mt_file;
+#if defined(Q_OS_QNX) && defined(__EXT_QNX__READDIR_R)
+    // for _readdir_r
+    size_t direntSize;
+#endif
 #endif
     int lastError;
 #endif
index 9ab186d..28f5e36 100644 (file)
@@ -54,6 +54,9 @@ QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Fi
     : nativePath(entry.nativeFilePath())
     , dir(0)
     , dirEntry(0)
+#if defined(Q_OS_QNX) && defined(__EXT_QNX__READDIR_R)
+    , direntSize(0)
+#endif
     , lastError(0)
 {
     Q_UNUSED(filters)
@@ -78,6 +81,15 @@ QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Fi
         Q_CHECK_PTR(p);
 
         mt_file.reset(p);
+#if defined(Q_OS_QNX) && defined(__EXT_QNX__READDIR_R)
+        direntSize = maxPathName;
+
+        // Include extra stat information in the readdir() call (d_stat member of dirent_extra_stat).
+        // This is used in QFileSystemMetaData::fillFromDirEnt() to avoid extra stat() calls when iterating
+        // over directories
+        if (dircntl(dir, D_SETFLAG, D_FLAG_STAT) == -1)
+            lastError = errno;
+#endif
 #endif
     }
 }
@@ -93,7 +105,11 @@ bool QFileSystemIterator::advance(QFileSystemEntry &fileEntry, QFileSystemMetaDa
     if (!dir)
         return false;
 
-#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN)
+#if defined(Q_OS_QNX) && defined(__EXT_QNX__READDIR_R)
+    lastError = _readdir_r(dir, mt_file.data(), &dirEntry, direntSize);
+    if (lastError)
+        return false;
+#elif defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN)
     lastError = QT_READDIR_R(dir, mt_file.data(), &dirEntry);
     if (lastError)
         return false;