9#include "DirectoryScanner.h"
11#include "llvm/ADT/STLExtras.h"
12#include "llvm/Support/ConvertUTF.h"
13#include "llvm/Support/Path.h"
14#include "llvm/Support/Windows/WindowsSupport.h"
15#include <condition_variable>
24using DirectoryWatcherCallback =
31 OVERLAPPED Overlapped;
33 std::vector<DWORD> Notifications;
35 std::thread WatcherThread;
36 std::thread HandlerThread;
42 bool WatcherActive =
false;
43 std::condition_variable Ready;
47 std::queue<DirectoryWatcher::Event> Q;
48 std::condition_variable CV;
53 std::unique_lock<std::mutex> L(M);
54 Q.emplace(Kind, Path);
60 std::unique_lock<std::mutex> L(M);
67 CV.wait(L, [
this]() {
return !Q.empty(); });
73 DirectoryWatcherWindows(HANDLE DirectoryHandle,
bool WaitForInitialSync,
74 DirectoryWatcherCallback Receiver);
76 ~DirectoryWatcherWindows()
override;
79 void WatcherThreadProc(HANDLE DirectoryHandle);
80 void NotifierThreadProc(
bool WaitForInitialSync);
83DirectoryWatcherWindows::DirectoryWatcherWindows(
84 HANDLE DirectoryHandle,
bool WaitForInitialSync,
85 DirectoryWatcherCallback Receiver)
86 : Callback(Receiver), Terminate(INVALID_HANDLE_VALUE) {
90 DWORD
Size = GetFinalPathNameByHandleW(DirectoryHandle,
NULL, 0, 0);
91 std::unique_ptr<WCHAR[]> Buffer{
new WCHAR[
Size + 1]};
92 Size = GetFinalPathNameByHandleW(DirectoryHandle, Buffer.get(), Size, 0);
94 WCHAR *
Data = Buffer.get();
95 if (Size >= 4 && ::memcmp(
Data, L
"\\\\?\\", 8) == 0) {
99 llvm::sys::windows::UTF16ToUTF8(
Data, Size,
Path);
102 size_t EntrySize =
sizeof(FILE_NOTIFY_INFORMATION) + MAX_PATH *
sizeof(WCHAR);
103 Notifications.resize((4 * EntrySize) /
sizeof(DWORD));
105 memset(&Overlapped, 0,
sizeof(Overlapped));
107 CreateEventW(
NULL, FALSE, FALSE,
NULL);
108 assert(Overlapped.hEvent &&
"unable to create event");
111 CreateEventW(
NULL, TRUE, FALSE,
NULL);
113 WatcherThread = std::thread([
this, DirectoryHandle]() {
114 this->WatcherThreadProc(DirectoryHandle);
117 if (WaitForInitialSync)
120 HandlerThread = std::thread([
this, WaitForInitialSync]() {
121 this->NotifierThreadProc(WaitForInitialSync);
125DirectoryWatcherWindows::~DirectoryWatcherWindows() {
128 HandlerThread.join();
129 WatcherThread.join();
130 CloseHandle(Terminate);
131 CloseHandle(Overlapped.hEvent);
134void DirectoryWatcherWindows::InitialScan() {
135 std::unique_lock<std::mutex> lock(Mutex);
136 Ready.wait(lock, [
this] {
return this->WatcherActive; });
141void DirectoryWatcherWindows::WatcherThreadProc(HANDLE DirectoryHandle) {
145 BOOL WatchSubtree = TRUE;
146 DWORD NotifyFilter = FILE_NOTIFY_CHANGE_FILE_NAME
147 | FILE_NOTIFY_CHANGE_DIR_NAME
148 | FILE_NOTIFY_CHANGE_SIZE
149 | FILE_NOTIFY_CHANGE_LAST_WRITE
150 | FILE_NOTIFY_CHANGE_CREATION;
152 DWORD BytesTransferred;
153 if (!ReadDirectoryChangesW(DirectoryHandle, Notifications.data(),
154 Notifications.size() *
sizeof(DWORD),
155 WatchSubtree, NotifyFilter, &BytesTransferred,
156 &Overlapped,
NULL)) {
157 Q.emplace(DirectoryWatcher::Event::EventKind::WatcherGotInvalidated,
162 if (!WatcherActive) {
163 std::unique_lock<std::mutex> lock(Mutex);
164 WatcherActive =
true;
168 HANDLE Handles[2] = { Terminate, Overlapped.hEvent };
169 switch (WaitForMultipleObjects(2, Handles, FALSE, INFINITE)) {
172 Q.emplace(DirectoryWatcher::Event::EventKind::WatcherGotInvalidated,
174 (void)CloseHandle(DirectoryHandle);
178 case WAIT_OBJECT_0 + 1:
182 if (!GetOverlappedResult(DirectoryHandle, &Overlapped, &BytesTransferred,
184 Q.emplace(DirectoryWatcher::Event::EventKind::WatchedDirRemoved,
186 Q.emplace(DirectoryWatcher::Event::EventKind::WatcherGotInvalidated,
193 if (BytesTransferred == 0) {
194 Q.emplace(DirectoryWatcher::Event::EventKind::WatcherGotInvalidated,
199 for (FILE_NOTIFY_INFORMATION *I =
200 (FILE_NOTIFY_INFORMATION *)Notifications.data();
202 I = I->NextEntryOffset
203 ? (FILE_NOTIFY_INFORMATION *)((CHAR *)I + I->NextEntryOffset)
206 DirectoryWatcher::Event::EventKind::WatcherGotInvalidated;
208 case FILE_ACTION_ADDED:
209 case FILE_ACTION_MODIFIED:
210 case FILE_ACTION_RENAMED_NEW_NAME:
211 Kind = DirectoryWatcher::Event::EventKind::Modified;
213 case FILE_ACTION_REMOVED:
214 case FILE_ACTION_RENAMED_OLD_NAME:
215 Kind = DirectoryWatcher::Event::EventKind::Removed;
220 sys::windows::UTF16ToUTF8(I->FileName, I->FileNameLength /
sizeof(WCHAR),
222 Q.emplace(Kind, filename);
226 (void)CloseHandle(DirectoryHandle);
229void DirectoryWatcherWindows::NotifierThreadProc(
bool WaitForInitialSync) {
232 if (!WaitForInitialSync)
238 if (
E.Kind == DirectoryWatcher::Event::EventKind::WatcherGotInvalidated)
243auto error(DWORD ErrorCode) {
244 DWORD Flags = FORMAT_MESSAGE_ALLOCATE_BUFFER
245 | FORMAT_MESSAGE_FROM_SYSTEM
246 | FORMAT_MESSAGE_IGNORE_INSERTS;
249 if (!FormatMessageA(Flags,
NULL, ErrorCode,
250 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&Buffer,
252 return make_error<llvm::StringError>(
"error " + utostr(ErrorCode),
253 inconvertibleErrorCode());
255 std::string Message{Buffer};
257 return make_error<llvm::StringError>(Message, inconvertibleErrorCode());
264 DirectoryWatcherCallback Receiver,
265 bool WaitForInitialSync) {
267 llvm::report_fatal_error(
268 "DirectoryWatcher::create can not accept an empty Path.");
270 if (!sys::fs::is_directory(
Path))
271 llvm::report_fatal_error(
272 "DirectoryWatcher::create can not accept a filepath.");
275 if (sys::windows::UTF8ToUTF16(
Path, WidePath))
276 return llvm::make_error<llvm::StringError>(
277 "unable to convert path to UTF-16", llvm::inconvertibleErrorCode());
279 DWORD DesiredAccess = FILE_LIST_DIRECTORY;
280 DWORD ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
281 DWORD CreationDisposition = OPEN_EXISTING;
282 DWORD FlagsAndAttributes = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
284 HANDLE DirectoryHandle =
285 CreateFileW(WidePath.data(), DesiredAccess, ShareMode,
286 NULL, CreationDisposition,
287 FlagsAndAttributes,
NULL);
288 if (DirectoryHandle == INVALID_HANDLE_VALUE)
289 return error(GetLastError());
294 return std::make_unique<DirectoryWatcherWindows>(
295 DirectoryHandle, WaitForInitialSync, Receiver);
__DEVICE__ void * memset(void *__a, int __b, size_t __c)
Provides notifications for file changes in a directory.
static llvm::Expected< std::unique_ptr< DirectoryWatcher > > create(llvm::StringRef Path, std::function< void(llvm::ArrayRef< DirectoryWatcher::Event > Events, bool IsInitial)> Receiver, bool WaitForInitialSync)
llvm fatal_error if
The JSON file list parser is used to communicate input to InstallAPI.
std::vector< std::string > scanDirectory(StringRef Path)
std::vector< DirectoryWatcher::Event > getAsFileEvents(const std::vector< std::string > &Scan)
Create event with EventKind::Added for every element in Scan.
Diagnostic wrappers for TextAPI types for error reporting.