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