diff options
author | Christian Kamm <kamm@incasoftware.de> | 2014-11-06 02:36:04 +0300 |
---|---|---|
committer | Christian Kamm <kamm@incasoftware.de> | 2014-11-20 14:36:17 +0300 |
commit | d4e0941c2732508b2d944f19115c2c76c7eb593b (patch) | |
tree | 9277dda39773485e72ee9c6a2808d6395e722bc8 /src | |
parent | 9dc57359b9e74ed6d8f74d17a72e00c73a67ce49 (diff) |
Windows filewatcher: switch to ReadDirectoryChangesW.
Based on danimo's #2454 fix for #2455 and related to #2297.
Diffstat (limited to 'src')
-rw-r--r-- | src/mirall/folderwatcher_win.cpp | 132 | ||||
-rw-r--r-- | src/mirall/folderwatcher_win.h | 2 |
2 files changed, 104 insertions, 30 deletions
diff --git a/src/mirall/folderwatcher_win.cpp b/src/mirall/folderwatcher_win.cpp index c6c8242fa..ced1c2d23 100644 --- a/src/mirall/folderwatcher_win.cpp +++ b/src/mirall/folderwatcher_win.cpp @@ -13,6 +13,7 @@ #include <QThread> #include <QDebug> +#include <QDir> #include "mirall/folderwatcher.h" #include "mirall/folderwatcher_win.h" @@ -23,52 +24,123 @@ namespace Mirall { -void WatcherThread::run() +void WatcherThread::watchChanges(size_t fileNotifyBufferSize, + bool* increaseBufferSize) { - _handle = FindFirstChangeNotification((wchar_t*)_path.utf16(), - true, // recursive watch - FILE_NOTIFY_CHANGE_FILE_NAME | - FILE_NOTIFY_CHANGE_DIR_NAME | - FILE_NOTIFY_CHANGE_LAST_WRITE); + *increaseBufferSize = false; + + _handle = CreateFileW( + (wchar_t*)_path.utf16(), + FILE_LIST_DIRECTORY, + FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL + ); if (_handle == INVALID_HANDLE_VALUE) { - qDebug() << Q_FUNC_INFO << "FindFirstChangeNotification function failed, stopping watcher!"; - FindCloseChangeNotification(_handle); + DWORD errorCode = GetLastError(); + qDebug() << Q_FUNC_INFO << "Failed to create handle for" << _path << ", error:" << errorCode; _handle = 0; return; } - if (_handle == NULL) - { - qDebug() << Q_FUNC_INFO << "FindFirstChangeNotification returned null, stopping watcher!"; - FindCloseChangeNotification(_handle); - _handle = 0; - return; - } + // QVarLengthArray ensures the stack-buffer is aligned like double and qint64. + QVarLengthArray<char, 4096*10> fileNotifyBuffer; + fileNotifyBuffer.resize(fileNotifyBufferSize); + + const size_t fileNameBufferSize = 4096; + TCHAR fileNameBuffer[fileNameBufferSize]; + + forever { + 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)) + { + 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); + + qDebug() << Q_FUNC_INFO << "Found change in" << longfile << "action:" << curEntry->Action; + emit changed(longfile); - while(true) { - switch(WaitForSingleObject(_handle, /*wait*/ INFINITE)) { - case WAIT_OBJECT_0: - if (FindNextChangeNotification(_handle) == false) { - qDebug() << Q_FUNC_INFO << "FindFirstChangeNotification returned FALSE, stopping watcher!"; - FindCloseChangeNotification(_handle); - _handle = 0; - return; + if (curEntry->NextEntryOffset == 0) { + break; + } + curEntry = (FILE_NOTIFY_INFORMATION*)( + (char*)curEntry + curEntry->NextEntryOffset); } - // qDebug() << Q_FUNC_INFO << "Change detected in" << _path << "from" << QThread::currentThread (); - emit changed(_path); - break; - default: - qDebug() << Q_FUNC_INFO << "Error while watching"; + } else { + DWORD errorCode = GetLastError(); + switch(errorCode) { + case 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."; + break; + } + CloseHandle(_handle); + _handle = NULL; + return; + } + } +} + +void WatcherThread::run() +{ + // 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 { + bool increaseBufferSize = false; + watchChanges(bufferSize, &increaseBufferSize); + + if (increaseBufferSize) { + bufferSize = qMin(bufferSize*2, maxBuffer); + } else { + // Other errors shouldn't actually happen, + // so sleep a bit to avoid running into the same error case in a + // tight loop. + sleep(2); } } } WatcherThread::~WatcherThread() { - if (_handle) - FindCloseChangeNotification(_handle); + if (_handle) { + CloseHandle(_handle); + _handle = NULL; + } } FolderWatcherPrivate::FolderWatcherPrivate(FolderWatcher *p, const QString& path) diff --git a/src/mirall/folderwatcher_win.h b/src/mirall/folderwatcher_win.h index 56ddfccd0..465477fd9 100644 --- a/src/mirall/folderwatcher_win.h +++ b/src/mirall/folderwatcher_win.h @@ -33,6 +33,8 @@ public: protected: void run(); + void watchChanges(size_t fileNotifyBufferSize, + bool* increaseBufferSize); signals: void changed(const QString &path); |