xref: /glogg/src/winwatchtowerdriver.h (revision c633ced33b4f2c2cf77c0c80a15fa73a7f13ad9f)
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