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