Anonymous avatar Anonymous committed 53b9d85

Changed file watcher on Windows to be more robust. Uses callback queues on the file watcher. Can now be cleaned up more easily.

Comments (0)

Files changed (3)

Win/FileWatcherTaskWin.cpp

 #include "FileWatcherTaskWin.h"
 #include "variant_list.h"
 
-void FileWatcherTaskWin::Watch(std::string p, FB::JSObjectPtr callback)
-{
-	boost::thread thread = boost::thread(boost::bind(&FileWatcherTaskWin::ListenForFileEvents, this, p, callback));
-	thread.detach();
-}
 
-void FileWatcherTaskWin::ListenForFileEvents(std::string path, FB::JSObjectPtr callback)
+
+FileWatcherTaskWin::FileWatcherTaskWin(std::string path, FB::JSObjectPtr callback)
 {
+	stop = false;
+
 	WCHAR widePathBuf[2048];
 	int wpBufSize = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), -1, widePathBuf, 2048);
-	std::wstring widePath = widePathBuf;
 	
-	handle = CreateFile(widePath.c_str(), 
+	m_path = widePathBuf;
+	m_callback = callback;
+
+	ZeroMemory(&m_ol, sizeof(m_ol));
+
+	m_handle = CreateFile(m_path.c_str(), 
 								FILE_LIST_DIRECTORY, 
 								FILE_SHARE_READ | FILE_SHARE_WRITE,
 								NULL,
 								OPEN_EXISTING,
-								FILE_FLAG_BACKUP_SEMANTICS,
+								FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
 								NULL);
-								//FindFirstChangeNotification (widePath.c_str(), TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE);
-	
-	
-	while (handle != INVALID_HANDLE_VALUE && !this->ShouldStop())
+	m_ol.hEvent = this;
+}
+void FileWatcherTaskWin::StartWatching()
+{
+	m_thread = boost::thread(boost::bind(&FileWatcherTaskWin::StartAlertLoop, this));
+	QueueUserAPC(FileWatcherTaskWin::InitDirectoryWatcher, m_thread.native_handle(), (ULONG_PTR)this);
+}
+void FileWatcherTaskWin::StartAlertLoop()
+{
+	while (!stop)
 	{
-		char buffer[4096];
-		DWORD bytesInBuf = 0;
+		SleepEx(INFINITE, TRUE);
+	}
+}
+void CALLBACK FileWatcherTaskWin::InitDirectoryWatcher(ULONG_PTR arg)
+{
+	FileWatcherTaskWin* task = (FileWatcherTaskWin*)arg;
+	BOOL success = task->ListenForFileEvents();
+}
 
-		ReadDirectoryChangesW(handle, buffer, 4096, TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE, &bytesInBuf, NULL, NULL);
-		
-		if (bytesInBuf > 0)
+BOOL FileWatcherTaskWin::ListenForFileEvents()
+{
+	DWORD dwBytes=0;
+
+	return ReadDirectoryChangesW(m_handle, m_buffer, 4096, TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE, &dwBytes, &m_ol, &WatcherCallback);
+}
+
+void CALLBACK FileWatcherTaskWin::WatcherCallback(DWORD dwErrorCode,DWORD bytesInBuf,LPOVERLAPPED lpOverlapped)
+{
+	FileWatcherTaskWin* task = (FileWatcherTaskWin*)lpOverlapped->hEvent; 
+	if (bytesInBuf > 0)
+	{
+		BOOL moreToRead = true;
+		char* readFrom = task->m_buffer;
+		while(moreToRead)
 		{
-			BOOL moreToRead = true;
-			char* readFrom = buffer;
-			while(moreToRead)
-			{
-				FILE_NOTIFY_INFORMATION* fni = (FILE_NOTIFY_INFORMATION*)readFrom;
-				std::wstring fileName(fni->FileName, fni->FileNameLength/sizeof(WCHAR));
-				std::wstring wideFinalPath = widePath + L"\\" + fileName;
+			FILE_NOTIFY_INFORMATION* fni = (FILE_NOTIFY_INFORMATION*)readFrom;
+			std::wstring fileName(fni->FileName, fni->FileNameLength/sizeof(WCHAR));
+			std::wstring wideFinalPath = task->m_path + L"\\" + fileName;
 
-				char utf8Buf[2048];
-				int pathLength = WideCharToMultiByte(CP_UTF8, 0, wideFinalPath.c_str(), -1, utf8Buf, 2048, NULL, NULL);
-				std::string finalPath = utf8Buf;
-				
-				callback->Invoke("", FB::variant_list_of(finalPath));
-				if (!fni->NextEntryOffset)
-				{
-					moreToRead = false;
-				}
-				else
-				{
-					readFrom += fni->NextEntryOffset;
-				}
+			char utf8Buf[2048];
+			int pathLength = WideCharToMultiByte(CP_UTF8, 0, wideFinalPath.c_str(), -1, utf8Buf, 2048, NULL, NULL);
+			std::string finalPath = utf8Buf;
+			
+			task->m_callback->Invoke("", FB::variant_list_of(finalPath));
+			if (!fni->NextEntryOffset)
+			{
+				moreToRead = false;
+			}
+			else
+			{
+				readFrom += fni->NextEntryOffset;
 			}
 		}
 	}
+	BOOL success = task->ListenForFileEvents();
 }
-
-void FileWatcherTaskWin::StopWatching()
+void CALLBACK FileWatcherTaskWin::TerminateWatch(ULONG_PTR arg)
+{
+	FileWatcherTaskWin* task = (FileWatcherTaskWin*)arg;
+	task->CloseHandlesStopAlertLoop();
+}
+void FileWatcherTaskWin::CloseHandlesStopAlertLoop()
 {
-	BOOL success = CloseHandle(handle);
+	CancelIo(m_handle);
+	CloseHandle(m_handle);
 	this->stop = true;
 }
-
-bool FileWatcherTaskWin::ShouldStop()
+void FileWatcherTaskWin::StopWatching()
 {
-	return stop;
-}
+	QueueUserAPC(FileWatcherTaskWin::TerminateWatch, m_thread.native_handle(), (ULONG_PTR)this);
+	// 5 seconds should be plenty of time
+	m_thread.timed_join(boost::posix_time::seconds(5));
+}

Win/FileWatcherTaskWin.h

 class FileWatcherTaskWin
 {
 public:
-	FileWatcherTaskWin(){stop = false;};
+	FileWatcherTaskWin(std::string path, FB::JSObjectPtr callback);
 	~FileWatcherTaskWin(){};
 
-	void Watch(std::string path, FB::JSObjectPtr callback);
+	void StartWatching();
 	void StopWatching();
 
 private:
 
-	void ListenForFileEvents(std::string path, FB::JSObjectPtr callback);
-	bool ShouldStop();
+	static void CALLBACK WatcherCallback(DWORD dwErrorCode,DWORD dwNumberOfBytesTransfered,	LPOVERLAPPED lpOverlapped);
+
+	static void CALLBACK InitDirectoryWatcher(ULONG_PTR arg);
+
+	static void CALLBACK TerminateWatch(ULONG_PTR arg);
 	
-	HANDLE handle;
-	bool stop;
+	void StartAlertLoop();
+	BOOL ListenForFileEvents();
+	void CloseHandlesStopAlertLoop();
+	//bool ShouldStop();
 	
+	HANDLE m_handle;
+	char m_buffer[4096];
+	
+	bool stop;
+	std::wstring m_path;
+	FB::JSObjectPtr m_callback;
+	OVERLAPPED m_ol;
+	boost::thread m_thread;
 };

Win/FileWatcherWin.cpp

 		boost::filesystem::path p(path);
 		if (is_directory(p))
 		{
-			task = new FileWatcherTaskWin();
-			task->Watch(path, callback);
+			task = new FileWatcherTaskWin(path,callback);
+			task->StartWatching();
 			watchers[key] = task;
 		}
 	}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.