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