xref: /glogg/src/winwatchtowerdriver.h (revision f7ea98673927ac33eb283682e939401077197d3b)
19ebe83d5SNicolas Bonnefon /*
29ebe83d5SNicolas Bonnefon  * Copyright (C) 2015 Nicolas Bonnefon and other contributors
39ebe83d5SNicolas Bonnefon  *
49ebe83d5SNicolas Bonnefon  * This file is part of glogg.
59ebe83d5SNicolas Bonnefon  *
69ebe83d5SNicolas Bonnefon  * glogg is free software: you can redistribute it and/or modify
79ebe83d5SNicolas Bonnefon  * it under the terms of the GNU General Public License as published by
89ebe83d5SNicolas Bonnefon  * the Free Software Foundation, either version 3 of the License, or
99ebe83d5SNicolas Bonnefon  * (at your option) any later version.
109ebe83d5SNicolas Bonnefon  *
119ebe83d5SNicolas Bonnefon  * glogg is distributed in the hope that it will be useful,
129ebe83d5SNicolas Bonnefon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
139ebe83d5SNicolas Bonnefon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
149ebe83d5SNicolas Bonnefon  * GNU General Public License for more details.
159ebe83d5SNicolas Bonnefon  *
169ebe83d5SNicolas Bonnefon  * You should have received a copy of the GNU General Public License
179ebe83d5SNicolas Bonnefon  * along with glogg.  If not, see <http://www.gnu.org/licenses/>.
189ebe83d5SNicolas Bonnefon  */
199ebe83d5SNicolas 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 
WinNotificationInfo()46f09fa651SNicolas Bonnefon     WinNotificationInfo() { action_ = Action::UNDEF; }
WinNotificationInfo(Action action,std::wstring file_name)47f09fa651SNicolas Bonnefon     WinNotificationInfo( Action action, std::wstring file_name )
48f09fa651SNicolas Bonnefon     { action_ = action; file_name_ = file_name; }
49f09fa651SNicolas Bonnefon 
action()50f09fa651SNicolas Bonnefon     Action action() const { return action_; }
fileName()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:
iterator(WinNotificationInfoList * list,const char * position)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 
begin()94f09fa651SNicolas Bonnefon     iterator begin() { return iterator( this, pointer_ ); }
end()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 {
WinWatchedDirRecordWinWatchedDirRecord117f09fa651SNicolas 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 
DirId()134f09fa651SNicolas Bonnefon         DirId() {}
135f09fa651SNicolas Bonnefon         bool operator==( const DirId& other ) const
136f09fa651SNicolas Bonnefon         { return dir_record_ == other.dir_record_; }
valid()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:
FileChangeToken()146fcaa7557SNicolas Bonnefon         FileChangeToken() {}
FileChangeToken(const std::string & file_name)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_ ) &&
154*f7ea9867SNicolas Bonnefon             ( high_date_time_ == other.high_date_time_ ) &&
155*f7ea9867SNicolas Bonnefon             ( low_file_size_  == other.low_file_size_ ) &&
156*f7ea9867SNicolas Bonnefon             ( high_file_size_ == other.high_file_size_); }
157fcaa7557SNicolas Bonnefon         bool operator!=( const FileChangeToken& other )
158fcaa7557SNicolas Bonnefon         { return ! operator==( other ); }
159fcaa7557SNicolas Bonnefon 
160fcaa7557SNicolas Bonnefon       private:
161fcaa7557SNicolas Bonnefon         DWORD low_date_time_  = 0;
162fcaa7557SNicolas Bonnefon         DWORD high_date_time_ = 0;
163*f7ea9867SNicolas Bonnefon         DWORD low_file_size_  = 0;
164*f7ea9867SNicolas Bonnefon         DWORD high_file_size_ = 0;
165fcaa7557SNicolas Bonnefon     };
166fcaa7557SNicolas Bonnefon 
167f09fa651SNicolas Bonnefon     // Default constructor
168f09fa651SNicolas Bonnefon     WinWatchTowerDriver();
169f09fa651SNicolas Bonnefon     ~WinWatchTowerDriver();
170f09fa651SNicolas Bonnefon 
171f09fa651SNicolas Bonnefon     FileId addFile( const std::string& file_name );
172f09fa651SNicolas Bonnefon     SymlinkId addSymlink( const std::string& file_name );
173f09fa651SNicolas Bonnefon     DirId addDir( const std::string& file_name );
174f09fa651SNicolas Bonnefon 
175f09fa651SNicolas Bonnefon     void removeFile( const FileId& file_id );
176f09fa651SNicolas Bonnefon     void removeSymlink( const SymlinkId& symlink_id );
1773104b268SNicolas Bonnefon     void removeDir( const DirId& dir_id );
178f09fa651SNicolas Bonnefon 
179f09fa651SNicolas Bonnefon     std::vector<ObservedFile<WinWatchTowerDriver>*> waitAndProcessEvents(
180f09fa651SNicolas Bonnefon             ObservedFileList<WinWatchTowerDriver>* list,
18191f7c705SNicolas Bonnefon             std::unique_lock<std::mutex>* lock,
182fcaa7557SNicolas Bonnefon             std::vector<ObservedFile<WinWatchTowerDriver>*>* files_needing_readding,
183fcaa7557SNicolas Bonnefon             int timout_ms );
184f09fa651SNicolas Bonnefon 
1859ebe83d5SNicolas Bonnefon     // Interrupt waitAndProcessEvents
186f09fa651SNicolas Bonnefon     void interruptWait();
187f09fa651SNicolas Bonnefon 
188f09fa651SNicolas Bonnefon   private:
189f09fa651SNicolas Bonnefon     // An action which will be picked up by the worker thread.
190f09fa651SNicolas Bonnefon     class Action {
191f09fa651SNicolas Bonnefon       public:
Action(std::function<void ()> function)192f09fa651SNicolas Bonnefon         Action( std::function<void()> function ) : function_ { function } {}
~Action()193f09fa651SNicolas Bonnefon         ~Action() {}
194f09fa651SNicolas Bonnefon 
operator()195f09fa651SNicolas Bonnefon         void operator()() { function_(); }
196f09fa651SNicolas Bonnefon 
197f09fa651SNicolas Bonnefon       private:
198f09fa651SNicolas Bonnefon         std::function<void()> function_;
199f09fa651SNicolas Bonnefon     };
200f09fa651SNicolas Bonnefon 
201f09fa651SNicolas Bonnefon     // Action
202f09fa651SNicolas Bonnefon     std::mutex action_mutex_;
203f09fa651SNicolas Bonnefon     std::condition_variable action_done_cv_;
204f09fa651SNicolas Bonnefon     std::unique_ptr<Action> scheduled_action_ = nullptr;
205f09fa651SNicolas Bonnefon 
206f09fa651SNicolas Bonnefon     // Win32 notification variables
207f09fa651SNicolas Bonnefon     HANDLE     hCompPort_;
208f09fa651SNicolas Bonnefon     OVERLAPPED overlapped_;
209f09fa651SNicolas Bonnefon     unsigned long buffer_length_;
210f09fa651SNicolas Bonnefon 
211f09fa651SNicolas Bonnefon     // List of directory records
212f09fa651SNicolas Bonnefon     // Accessed exclusively in the worker thread context
213f09fa651SNicolas Bonnefon     std::vector<std::weak_ptr<WinWatchedDirRecord>> dir_records_ { };
214f09fa651SNicolas Bonnefon 
215f09fa651SNicolas Bonnefon     // Private member functions
216f09fa651SNicolas Bonnefon     void serialisedAddDir(
217f09fa651SNicolas Bonnefon             const std::string& file_name,
218f09fa651SNicolas Bonnefon             DirId& dir_id );
219f09fa651SNicolas Bonnefon };
220f09fa651SNicolas Bonnefon 
221f09fa651SNicolas Bonnefon #endif
222