xref: /glogg/src/winwatchtowerdriver.h (revision 6e2e573c451ec24d720c967fab68538eaa3f3ab5)
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     // Default constructor
125     WinWatchTowerDriver();
126     ~WinWatchTowerDriver();
127 
128     FileId addFile( const std::string& file_name );
129     SymlinkId addSymlink( const std::string& file_name );
130     DirId addDir( const std::string& file_name );
131 
132     void removeFile( const FileId& file_id );
133     void removeSymlink( const SymlinkId& symlink_id );
134     void removeDir( const DirId& dir_id );
135 
136     std::vector<ObservedFile<WinWatchTowerDriver>*> waitAndProcessEvents(
137             ObservedFileList<WinWatchTowerDriver>* list,
138             std::unique_lock<std::mutex>* lock,
139             std::vector<ObservedFile<WinWatchTowerDriver>*>* files_needing_readding );
140 
141     void interruptWait();
142 
143   private:
144     // An action which will be picked up by the worker thread.
145     class Action {
146       public:
147         Action( std::function<void()> function ) : function_ { function } {}
148         ~Action() {}
149 
150         void operator()() { function_(); }
151 
152       private:
153         std::function<void()> function_;
154     };
155 
156     // Action
157     std::mutex action_mutex_;
158     std::condition_variable action_done_cv_;
159     std::unique_ptr<Action> scheduled_action_ = nullptr;
160 
161     // Win32 notification variables
162     HANDLE     hCompPort_;
163     OVERLAPPED overlapped_;
164     unsigned long buffer_length_;
165 
166     // List of directory records
167     // Accessed exclusively in the worker thread context
168     std::vector<std::weak_ptr<WinWatchedDirRecord>> dir_records_ { };
169 
170     // Private member functions
171     void serialisedAddDir(
172             const std::string& file_name,
173             DirId& dir_id );
174 };
175 
176 #endif
177