Commits

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.

  • Participants
  • Parent commits f8d99d0

Comments (0)

Files changed (3)

File 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));
+}

File 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;
 };

File 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;
 		}
 	}