1 #ifndef WINWATCHTOWERDRIVER_H 2 #define WINWATCHTOWERDRIVER_H 3 4 #include <atomic> 5 #include <thread> 6 #include <mutex> 7 #include <condition_variable> 8 #include <iterator> 9 #include <vector> 10 11 #define WIN32_LEAN_AND_MEAN 12 #include <windows.h> 13 14 // Utility class 15 16 // Encapsulate a directory notification returned by Windows' 17 // ReadDirectoryChangesW. 18 class WinNotificationInfo { 19 public: 20 enum class Action { UNDEF, 21 ADDED, 22 REMOVED, 23 MODIFIED, 24 RENAMED_OLD_NAME, 25 RENAMED_NEW_NAME }; 26 27 WinNotificationInfo() { action_ = Action::UNDEF; } 28 WinNotificationInfo( Action action, std::wstring file_name ) 29 { action_ = action; file_name_ = file_name; } 30 31 Action action() const { return action_; } 32 std::wstring fileName() const { return file_name_; } 33 34 private: 35 Action action_; 36 std::wstring file_name_; 37 }; 38 39 class WinNotificationInfoList { 40 public: 41 WinNotificationInfoList( const char* buffer, size_t buffer_size ); 42 43 // Iterator 44 class iterator : std::iterator<std::input_iterator_tag, WinNotificationInfo> { 45 public: 46 iterator( WinNotificationInfoList* list, const char* position ) 47 { list_ = list; position_ = position; } 48 49 const WinNotificationInfo& operator*() const 50 { return list_->current_notification_; } 51 52 const WinNotificationInfo* operator->() const 53 { return &( list_->current_notification_ ); } 54 55 const WinNotificationInfo& operator++() { 56 position_ = list_->advanceToNext(); 57 return list_->current_notification_; 58 } 59 60 WinNotificationInfo operator++( int ) { 61 WinNotificationInfo tmp { list_->current_notification_ }; 62 operator++(); 63 return tmp; 64 } 65 66 bool operator!=( const iterator& other ) const { 67 return ( list_ != other.list_ ) || ( position_ != other.position_ ); 68 } 69 70 private: 71 WinNotificationInfoList* list_; 72 const char* position_; 73 }; 74 75 iterator begin() { return iterator( this, pointer_ ); } 76 iterator end() { return iterator( this, nullptr ); } 77 78 private: 79 const char* advanceToNext(); 80 81 // Current notification (in the byte stream) 82 const char* pointer_; 83 // Next notification (in the byte stream) 84 const char* next_; 85 WinNotificationInfo current_notification_; 86 87 const char* updateCurrentNotification( const char* new_position ); 88 }; 89 90 template <typename Driver> 91 class ObservedFile; 92 template <typename Driver> 93 class ObservedFileList; 94 95 class WinWatchTowerDriver { 96 public: 97 struct WinWatchedDirRecord { 98 WinWatchedDirRecord( const std::string& file_name ) 99 : path_( file_name ) { } 100 101 static const int READ_DIR_CHANGE_BUFFER_SIZE = 4096; 102 103 std::string path_; 104 void* handle_ = nullptr; 105 static const unsigned long buffer_length_ = READ_DIR_CHANGE_BUFFER_SIZE; 106 char buffer_[buffer_length_]; 107 }; 108 109 class FileId { }; 110 class SymlinkId { }; 111 class DirId { 112 public: 113 friend class WinWatchTowerDriver; 114 115 DirId() {} 116 bool operator==( const DirId& other ) const 117 { return dir_record_ == other.dir_record_; } 118 bool valid() const 119 { return ( dir_record_ != nullptr ); } 120 private: 121 std::shared_ptr<WinWatchedDirRecord> dir_record_; 122 }; 123 124 // On Windows, the token is the "last write" time 125 class FileChangeToken { 126 public: 127 FileChangeToken() {} 128 FileChangeToken( const std::string& file_name ) 129 { readFromFile( file_name ); } 130 131 void readFromFile( const std::string& file_name ); 132 133 bool operator==( const FileChangeToken& other ) 134 { return ( low_date_time_ == other.low_date_time_ ) && 135 ( high_date_time_ == other.high_date_time_ ); } 136 bool operator!=( const FileChangeToken& other ) 137 { return ! operator==( other ); } 138 139 private: 140 DWORD low_date_time_ = 0; 141 DWORD high_date_time_ = 0; 142 }; 143 144 // Default constructor 145 WinWatchTowerDriver(); 146 ~WinWatchTowerDriver(); 147 148 FileId addFile( const std::string& file_name ); 149 SymlinkId addSymlink( const std::string& file_name ); 150 DirId addDir( const std::string& file_name ); 151 152 void removeFile( const FileId& file_id ); 153 void removeSymlink( const SymlinkId& symlink_id ); 154 void removeDir( const DirId& dir_id ); 155 156 std::vector<ObservedFile<WinWatchTowerDriver>*> waitAndProcessEvents( 157 ObservedFileList<WinWatchTowerDriver>* list, 158 std::unique_lock<std::mutex>* lock, 159 std::vector<ObservedFile<WinWatchTowerDriver>*>* files_needing_readding, 160 int timout_ms ); 161 162 void interruptWait(); 163 164 private: 165 // An action which will be picked up by the worker thread. 166 class Action { 167 public: 168 Action( std::function<void()> function ) : function_ { function } {} 169 ~Action() {} 170 171 void operator()() { function_(); } 172 173 private: 174 std::function<void()> function_; 175 }; 176 177 // Action 178 std::mutex action_mutex_; 179 std::condition_variable action_done_cv_; 180 std::unique_ptr<Action> scheduled_action_ = nullptr; 181 182 // Win32 notification variables 183 HANDLE hCompPort_; 184 OVERLAPPED overlapped_; 185 unsigned long buffer_length_; 186 187 // List of directory records 188 // Accessed exclusively in the worker thread context 189 std::vector<std::weak_ptr<WinWatchedDirRecord>> dir_records_ { }; 190 191 // Private member functions 192 void serialisedAddDir( 193 const std::string& file_name, 194 DirId& dir_id ); 195 }; 196 197 #endif 198