1 /* 2 * Copyright (C) 2015 Nicolas Bonnefon and other contributors 3 * 4 * This file is part of glogg. 5 * 6 * glogg is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * glogg is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with glogg. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #ifndef WINWATCHTOWERDRIVER_H 21 #define WINWATCHTOWERDRIVER_H 22 23 #include <atomic> 24 #include <thread> 25 #include <mutex> 26 #include <condition_variable> 27 #include <iterator> 28 #include <vector> 29 30 #define WIN32_LEAN_AND_MEAN 31 #include <windows.h> 32 33 // Utility class 34 35 // Encapsulate a directory notification returned by Windows' 36 // ReadDirectoryChangesW. 37 class WinNotificationInfo { 38 public: 39 enum class Action { UNDEF, 40 ADDED, 41 REMOVED, 42 MODIFIED, 43 RENAMED_OLD_NAME, 44 RENAMED_NEW_NAME }; 45 WinNotificationInfo()46 WinNotificationInfo() { action_ = Action::UNDEF; } WinNotificationInfo(Action action,std::wstring file_name)47 WinNotificationInfo( Action action, std::wstring file_name ) 48 { action_ = action; file_name_ = file_name; } 49 action()50 Action action() const { return action_; } fileName()51 std::wstring fileName() const { return file_name_; } 52 53 private: 54 Action action_; 55 std::wstring file_name_; 56 }; 57 58 class WinNotificationInfoList { 59 public: 60 WinNotificationInfoList( const char* buffer, size_t buffer_size ); 61 62 // Iterator 63 class iterator : std::iterator<std::input_iterator_tag, WinNotificationInfo> { 64 public: iterator(WinNotificationInfoList * list,const char * position)65 iterator( WinNotificationInfoList* list, const char* position ) 66 { list_ = list; position_ = position; } 67 68 const WinNotificationInfo& operator*() const 69 { return list_->current_notification_; } 70 71 const WinNotificationInfo* operator->() const 72 { return &( list_->current_notification_ ); } 73 74 const WinNotificationInfo& operator++() { 75 position_ = list_->advanceToNext(); 76 return list_->current_notification_; 77 } 78 79 WinNotificationInfo operator++( int ) { 80 WinNotificationInfo tmp { list_->current_notification_ }; 81 operator++(); 82 return tmp; 83 } 84 85 bool operator!=( const iterator& other ) const { 86 return ( list_ != other.list_ ) || ( position_ != other.position_ ); 87 } 88 89 private: 90 WinNotificationInfoList* list_; 91 const char* position_; 92 }; 93 begin()94 iterator begin() { return iterator( this, pointer_ ); } end()95 iterator end() { return iterator( this, nullptr ); } 96 97 private: 98 const char* advanceToNext(); 99 100 // Current notification (in the byte stream) 101 const char* pointer_; 102 // Next notification (in the byte stream) 103 const char* next_; 104 WinNotificationInfo current_notification_; 105 106 const char* updateCurrentNotification( const char* new_position ); 107 }; 108 109 template <typename Driver> 110 class ObservedFile; 111 template <typename Driver> 112 class ObservedFileList; 113 114 class WinWatchTowerDriver { 115 public: 116 struct WinWatchedDirRecord { WinWatchedDirRecordWinWatchedDirRecord117 WinWatchedDirRecord( const std::string& file_name ) 118 : path_( file_name ) { } 119 120 static const int READ_DIR_CHANGE_BUFFER_SIZE = 4096; 121 122 std::string path_; 123 void* handle_ = nullptr; 124 static const unsigned long buffer_length_ = READ_DIR_CHANGE_BUFFER_SIZE; 125 char buffer_[buffer_length_]; 126 }; 127 128 class FileId { }; 129 class SymlinkId { }; 130 class DirId { 131 public: 132 friend class WinWatchTowerDriver; 133 DirId()134 DirId() {} 135 bool operator==( const DirId& other ) const 136 { return dir_record_ == other.dir_record_; } valid()137 bool valid() const 138 { return ( dir_record_ != nullptr ); } 139 private: 140 std::shared_ptr<WinWatchedDirRecord> dir_record_; 141 }; 142 143 // On Windows, the token is the "last write" time 144 class FileChangeToken { 145 public: FileChangeToken()146 FileChangeToken() {} FileChangeToken(const std::string & file_name)147 FileChangeToken( const std::string& file_name ) 148 { readFromFile( file_name ); } 149 150 void readFromFile( const std::string& file_name ); 151 152 bool operator==( const FileChangeToken& other ) 153 { return ( low_date_time_ == other.low_date_time_ ) && 154 ( high_date_time_ == other.high_date_time_ ) && 155 ( low_file_size_ == other.low_file_size_ ) && 156 ( high_file_size_ == other.high_file_size_); } 157 bool operator!=( const FileChangeToken& other ) 158 { return ! operator==( other ); } 159 160 private: 161 DWORD low_date_time_ = 0; 162 DWORD high_date_time_ = 0; 163 DWORD low_file_size_ = 0; 164 DWORD high_file_size_ = 0; 165 }; 166 167 // Default constructor 168 WinWatchTowerDriver(); 169 ~WinWatchTowerDriver(); 170 171 FileId addFile( const std::string& file_name ); 172 SymlinkId addSymlink( const std::string& file_name ); 173 DirId addDir( const std::string& file_name ); 174 175 void removeFile( const FileId& file_id ); 176 void removeSymlink( const SymlinkId& symlink_id ); 177 void removeDir( const DirId& dir_id ); 178 179 std::vector<ObservedFile<WinWatchTowerDriver>*> waitAndProcessEvents( 180 ObservedFileList<WinWatchTowerDriver>* list, 181 std::unique_lock<std::mutex>* lock, 182 std::vector<ObservedFile<WinWatchTowerDriver>*>* files_needing_readding, 183 int timout_ms ); 184 185 // Interrupt waitAndProcessEvents 186 void interruptWait(); 187 188 private: 189 // An action which will be picked up by the worker thread. 190 class Action { 191 public: Action(std::function<void ()> function)192 Action( std::function<void()> function ) : function_ { function } {} ~Action()193 ~Action() {} 194 operator()195 void operator()() { function_(); } 196 197 private: 198 std::function<void()> function_; 199 }; 200 201 // Action 202 std::mutex action_mutex_; 203 std::condition_variable action_done_cv_; 204 std::unique_ptr<Action> scheduled_action_ = nullptr; 205 206 // Win32 notification variables 207 HANDLE hCompPort_; 208 OVERLAPPED overlapped_; 209 unsigned long buffer_length_; 210 211 // List of directory records 212 // Accessed exclusively in the worker thread context 213 std::vector<std::weak_ptr<WinWatchedDirRecord>> dir_records_ { }; 214 215 // Private member functions 216 void serialisedAddDir( 217 const std::string& file_name, 218 DirId& dir_id ); 219 }; 220 221 #endif 222