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 46 WinNotificationInfo() { action_ = Action::UNDEF; } 47 WinNotificationInfo( Action action, std::wstring file_name ) 48 { action_ = action; file_name_ = file_name; } 49 50 Action action() const { return action_; } 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: 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 94 iterator begin() { return iterator( this, pointer_ ); } 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 { 117 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 134 DirId() {} 135 bool operator==( const DirId& other ) const 136 { return dir_record_ == other.dir_record_; } 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: 146 FileChangeToken() {} 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 bool operator!=( const FileChangeToken& other ) 156 { return ! operator==( other ); } 157 158 private: 159 DWORD low_date_time_ = 0; 160 DWORD high_date_time_ = 0; 161 }; 162 163 // Default constructor 164 WinWatchTowerDriver(); 165 ~WinWatchTowerDriver(); 166 167 FileId addFile( const std::string& file_name ); 168 SymlinkId addSymlink( const std::string& file_name ); 169 DirId addDir( const std::string& file_name ); 170 171 void removeFile( const FileId& file_id ); 172 void removeSymlink( const SymlinkId& symlink_id ); 173 void removeDir( const DirId& dir_id ); 174 175 std::vector<ObservedFile<WinWatchTowerDriver>*> waitAndProcessEvents( 176 ObservedFileList<WinWatchTowerDriver>* list, 177 std::unique_lock<std::mutex>* lock, 178 std::vector<ObservedFile<WinWatchTowerDriver>*>* files_needing_readding, 179 int timout_ms ); 180 181 // Interrupt waitAndProcessEvents 182 void interruptWait(); 183 184 private: 185 // An action which will be picked up by the worker thread. 186 class Action { 187 public: 188 Action( std::function<void()> function ) : function_ { function } {} 189 ~Action() {} 190 191 void operator()() { function_(); } 192 193 private: 194 std::function<void()> function_; 195 }; 196 197 // Action 198 std::mutex action_mutex_; 199 std::condition_variable action_done_cv_; 200 std::unique_ptr<Action> scheduled_action_ = nullptr; 201 202 // Win32 notification variables 203 HANDLE hCompPort_; 204 OVERLAPPED overlapped_; 205 unsigned long buffer_length_; 206 207 // List of directory records 208 // Accessed exclusively in the worker thread context 209 std::vector<std::weak_ptr<WinWatchedDirRecord>> dir_records_ { }; 210 211 // Private member functions 212 void serialisedAddDir( 213 const std::string& file_name, 214 DirId& dir_id ); 215 }; 216 217 #endif 218