[Pkg-owncloud-commits] [owncloud-client] 258/470: Graceful termination of folderwatcher_win #4620

Sandro Knauß hefee-guest at moszumanska.debian.org
Thu May 12 16:25:11 UTC 2016


This is an automated email from the git hooks/post-receive script.

hefee-guest pushed a commit to branch master
in repository owncloud-client.

commit fa1bb309caca72333459c08808d4adf0b81d62bf
Author: Christian Kamm <mail at ckamm.de>
Date:   Thu Mar 31 14:04:53 2016 +0200

    Graceful termination of folderwatcher_win #4620
---
 src/gui/folderwatcher_win.cpp | 173 +++++++++++++++++++++++++++---------------
 src/gui/folderwatcher_win.h   |  17 ++++-
 2 files changed, 125 insertions(+), 65 deletions(-)

diff --git a/src/gui/folderwatcher_win.cpp b/src/gui/folderwatcher_win.cpp
index f815b7c..c7261ab 100644
--- a/src/gui/folderwatcher_win.cpp
+++ b/src/gui/folderwatcher_win.cpp
@@ -31,24 +31,27 @@ void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
     *increaseBufferSize = false;
     QString longPath = FileSystem::longWinPath(_path);
 
-    _handle = CreateFileW(
+    _directory = CreateFileW(
         (wchar_t*) longPath.utf16(),
         FILE_LIST_DIRECTORY,
         FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
         NULL,
         OPEN_EXISTING,
-        FILE_FLAG_BACKUP_SEMANTICS,
+        FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
         NULL
         );
 
-    if (_handle == INVALID_HANDLE_VALUE)
+    if (_directory == INVALID_HANDLE_VALUE)
     {
         DWORD errorCode = GetLastError();
         qDebug() << Q_FUNC_INFO << "Failed to create handle for" << _path << ", error:" << errorCode;
-        _handle = 0;
+        _directory = 0;
         return;
     }
 
+    OVERLAPPED overlapped;
+    overlapped.hEvent = _resultEvent;
+
     // QVarLengthArray ensures the stack-buffer is aligned like double and qint64.
     QVarLengthArray<char, 4096*10> fileNotifyBuffer;
     fileNotifyBuffer.resize(fileNotifyBufferSize);
@@ -56,88 +59,129 @@ void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
     const size_t fileNameBufferSize = 4096;
     TCHAR fileNameBuffer[fileNameBufferSize];
 
-    forever {
+
+    while (!_done) {
+        ResetEvent(_resultEvent);
+
         FILE_NOTIFY_INFORMATION *pFileNotifyBuffer =
                 (FILE_NOTIFY_INFORMATION*)fileNotifyBuffer.data();
         DWORD dwBytesReturned = 0;
         SecureZeroMemory(pFileNotifyBuffer, fileNotifyBufferSize);
-        if(ReadDirectoryChangesW( _handle, (LPVOID)pFileNotifyBuffer,
-                                  fileNotifyBufferSize, true,
-                                  FILE_NOTIFY_CHANGE_FILE_NAME |
-                                  FILE_NOTIFY_CHANGE_DIR_NAME |
-                                  FILE_NOTIFY_CHANGE_LAST_WRITE,
-                                  &dwBytesReturned, NULL, NULL))
+        if(! ReadDirectoryChangesW( _directory, (LPVOID)pFileNotifyBuffer,
+                                    fileNotifyBufferSize, true,
+                                    FILE_NOTIFY_CHANGE_FILE_NAME |
+                                    FILE_NOTIFY_CHANGE_DIR_NAME |
+                                    FILE_NOTIFY_CHANGE_LAST_WRITE,
+                                    &dwBytesReturned,
+                                    &overlapped,
+                                    NULL))
         {
-            FILE_NOTIFY_INFORMATION *curEntry = pFileNotifyBuffer;
-            forever {
-                size_t len = curEntry->FileNameLength / 2;
-                QString file = _path + "\\" + QString::fromWCharArray(curEntry->FileName, len);
-
-                // Unless the file was removed or renamed, get its full long name
-                // TODO: We could still try expanding the path in the tricky cases...
-                QString longfile = file;
-                if (curEntry->Action != FILE_ACTION_REMOVED
-                        && curEntry->Action != FILE_ACTION_RENAMED_OLD_NAME) {
-                    size_t longNameSize = GetLongPathNameW(reinterpret_cast<LPCWSTR>(file.utf16()), fileNameBuffer, fileNameBufferSize);
-                    if (longNameSize > 0) {
-                        longfile = QString::fromUtf16(reinterpret_cast<const ushort *>(fileNameBuffer), longNameSize);
-                    } else {
-                        qDebug() << Q_FUNC_INFO << "Error converting file name to full length, keeping original name.";
-                    }
-                }
-                longfile = QDir::cleanPath(longfile);
-
-                // Skip modifications of folders: One of these is triggered for changes
-                // and new files in a folder, probably because of the folder's mtime
-                // changing. We don't need them.
-                bool skip = curEntry->Action == FILE_ACTION_MODIFIED
-                        && QFileInfo(longfile).isDir();
-
-                if (!skip) {
-                    //qDebug() << Q_FUNC_INFO << "Found change in" << longfile
-                    //         << "action:" << curEntry->Action;
-                    emit changed(longfile);
-                }
-
-                if (curEntry->NextEntryOffset == 0) {
-                    break;
-                }
-                curEntry = (FILE_NOTIFY_INFORMATION*)(
-                                (char*)curEntry + curEntry->NextEntryOffset);
+            DWORD errorCode = GetLastError();
+            if (errorCode == ERROR_NOTIFY_ENUM_DIR) {
+                qDebug() << Q_FUNC_INFO << "The buffer for changes overflowed! Triggering a generic change and resizing";
+                emit changed(_path);
+                *increaseBufferSize = true;
+            } else {
+                qDebug() << Q_FUNC_INFO << "ReadDirectoryChangesW error" << errorCode;
             }
-        } else {
+            break;
+        }
+
+        HANDLE handles[] = {_resultEvent, _stopEvent};
+        DWORD result = WaitForMultipleObjects(
+                    2, handles,
+                    false, // awake once one of them arrives
+                    INFINITE);
+        if (result == 1) {
+            qDebug() << "Received stop event, aborting folder watcher thread";
+            break;
+        }
+        if (result != 0) {
+            qDebug() << "WaitForMultipleObjects failed" << result << GetLastError();
+            break;
+        }
+
+        bool ok = GetOverlappedResult(_directory, &overlapped, &dwBytesReturned, false);
+        if (! ok) {
             DWORD errorCode = GetLastError();
-            switch(errorCode) {
-            case ERROR_NOTIFY_ENUM_DIR:
+            if (errorCode == ERROR_NOTIFY_ENUM_DIR) {
                 qDebug() << Q_FUNC_INFO << "The buffer for changes overflowed! Triggering a generic change and resizing";
                 emit changed(_path);
                 *increaseBufferSize = true;
-                break;
-            default:
-                qDebug() << Q_FUNC_INFO << "General error" << errorCode << "while watching. Exiting.";
+            } else {
+                qDebug() << Q_FUNC_INFO << "GetOverlappedResult error" << errorCode;
+            }
+            break;
+        }
+
+        FILE_NOTIFY_INFORMATION *curEntry = pFileNotifyBuffer;
+        forever {
+            size_t len = curEntry->FileNameLength / 2;
+            QString file = _path + "\\" + QString::fromWCharArray(curEntry->FileName, len);
+
+            // Unless the file was removed or renamed, get its full long name
+            // TODO: We could still try expanding the path in the tricky cases...
+            QString longfile = file;
+            if (curEntry->Action != FILE_ACTION_REMOVED
+                    && curEntry->Action != FILE_ACTION_RENAMED_OLD_NAME) {
+                size_t longNameSize = GetLongPathNameW(reinterpret_cast<LPCWSTR>(file.utf16()), fileNameBuffer, fileNameBufferSize);
+                if (longNameSize > 0) {
+                    longfile = QString::fromUtf16(reinterpret_cast<const ushort *>(fileNameBuffer), longNameSize);
+                } else {
+                    qDebug() << Q_FUNC_INFO << "Error converting file name to full length, keeping original name.";
+                }
+            }
+            longfile = QDir::cleanPath(longfile);
+
+            // Skip modifications of folders: One of these is triggered for changes
+            // and new files in a folder, probably because of the folder's mtime
+            // changing. We don't need them.
+            bool skip = curEntry->Action == FILE_ACTION_MODIFIED
+                    && QFileInfo(longfile).isDir();
+
+            if (!skip) {
+                //qDebug() << Q_FUNC_INFO << "Found change in" << longfile
+                //         << "action:" << curEntry->Action;
+                emit changed(longfile);
+            }
+
+            if (curEntry->NextEntryOffset == 0) {
                 break;
             }
-            CloseHandle(_handle);
-            _handle = NULL;
-            return;
+            curEntry = (FILE_NOTIFY_INFORMATION*)(
+                            (char*)curEntry + curEntry->NextEntryOffset);
         }
     }
+
+    CancelIo(_directory);
+    closeHandle();
+}
+
+void WatcherThread::closeHandle()
+{
+    if (_directory) {
+        CloseHandle(_directory);
+        _directory = NULL;
+    }
 }
 
 void WatcherThread::run()
 {
+    _resultEvent = CreateEvent(NULL, true, false, NULL);
+    _stopEvent = CreateEvent(NULL, true, false, NULL);
+
     // If this buffer fills up before we've extracted its data we will lose
     // change information. Therefore start big.
     size_t bufferSize = 4096*10;
     size_t maxBuffer = 64*1024;
 
-    forever {
+    while (!_done) {
         bool increaseBufferSize = false;
         watchChanges(bufferSize, &increaseBufferSize);
 
         if (increaseBufferSize) {
             bufferSize = qMin(bufferSize*2, maxBuffer);
-        } else {
+        } else if (!_done) {
             // Other errors shouldn't actually happen,
             // so sleep a bit to avoid running into the same error case in a
             // tight loop.
@@ -148,10 +192,13 @@ void WatcherThread::run()
 
 WatcherThread::~WatcherThread()
 {
-    if (_handle) {
-        CloseHandle(_handle);
-        _handle = NULL;
-    }
+    closeHandle();
+}
+
+void WatcherThread::stop()
+{
+    _done = 1;
+    SetEvent(_stopEvent);
 }
 
 FolderWatcherPrivate::FolderWatcherPrivate(FolderWatcher *p, const QString& path)
@@ -165,7 +212,7 @@ FolderWatcherPrivate::FolderWatcherPrivate(FolderWatcher *p, const QString& path
 
 FolderWatcherPrivate::~FolderWatcherPrivate()
 {
-    _thread->terminate();
+    _thread->stop();
     _thread->wait();
     delete _thread;
 }
diff --git a/src/gui/folderwatcher_win.h b/src/gui/folderwatcher_win.h
index 4a72be4..9e5e941 100644
--- a/src/gui/folderwatcher_win.h
+++ b/src/gui/folderwatcher_win.h
@@ -15,6 +15,7 @@
 #define MIRALL_FOLDERWATCHER_WIN_H
 
 #include <QThread>
+#include <QAtomicInt>
 #include <windows.h>
 
 namespace OCC {
@@ -29,21 +30,33 @@ class WatcherThread : public QThread {
     Q_OBJECT
 public:
     WatcherThread(const QString &path) :
-        QThread(), _path(path), _handle(0) {}
+        QThread(),
+        _path(path),
+        _directory(0),
+        _resultEvent(0),
+        _stopEvent(0),
+        _done(false)
+    {}
 
     ~WatcherThread();
 
+    void stop();
+
 protected:
     void run();
     void watchChanges(size_t fileNotifyBufferSize,
                       bool* increaseBufferSize);
+    void closeHandle();
 
 signals:
     void changed(const QString &path);
 
 private:
     QString _path;
-    HANDLE _handle;
+    HANDLE _directory;
+    HANDLE _resultEvent;
+    HANDLE _stopEvent;
+    QAtomicInt _done;
 };
 
 /**

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-owncloud/owncloud-client.git



More information about the Pkg-owncloud-commits mailing list