xref: /glogg/src/winwatchtowerdriver.h (revision f09fa65124f80be4a92fab17a1cccc63d18936a5) !
1*f09fa651SNicolas Bonnefon #ifndef WINWATCHTOWERDRIVER_H
2*f09fa651SNicolas Bonnefon #define WINWATCHTOWERDRIVER_H
3*f09fa651SNicolas Bonnefon 
4*f09fa651SNicolas Bonnefon #include <atomic>
5*f09fa651SNicolas Bonnefon #include <thread>
6*f09fa651SNicolas Bonnefon #include <mutex>
7*f09fa651SNicolas Bonnefon #include <condition_variable>
8*f09fa651SNicolas Bonnefon #include <iterator>
9*f09fa651SNicolas Bonnefon #include <vector>
10*f09fa651SNicolas Bonnefon 
11*f09fa651SNicolas Bonnefon #define WIN32_LEAN_AND_MEAN
12*f09fa651SNicolas Bonnefon #include <windows.h>
13*f09fa651SNicolas Bonnefon 
14*f09fa651SNicolas Bonnefon // Utility class
15*f09fa651SNicolas Bonnefon 
16*f09fa651SNicolas Bonnefon // Encapsulate a directory notification returned by Windows'
17*f09fa651SNicolas Bonnefon // ReadDirectoryChangesW.
18*f09fa651SNicolas Bonnefon class WinNotificationInfo {
19*f09fa651SNicolas Bonnefon   public:
20*f09fa651SNicolas Bonnefon     enum class Action { UNDEF,
21*f09fa651SNicolas Bonnefon         ADDED,
22*f09fa651SNicolas Bonnefon         REMOVED,
23*f09fa651SNicolas Bonnefon         MODIFIED,
24*f09fa651SNicolas Bonnefon         RENAMED_OLD_NAME,
25*f09fa651SNicolas Bonnefon         RENAMED_NEW_NAME };
26*f09fa651SNicolas Bonnefon 
27*f09fa651SNicolas Bonnefon     WinNotificationInfo() { action_ = Action::UNDEF; }
28*f09fa651SNicolas Bonnefon     WinNotificationInfo( Action action, std::wstring file_name )
29*f09fa651SNicolas Bonnefon     { action_ = action; file_name_ = file_name; }
30*f09fa651SNicolas Bonnefon 
31*f09fa651SNicolas Bonnefon     Action action() const { return action_; }
32*f09fa651SNicolas Bonnefon     std::wstring fileName() const { return file_name_; }
33*f09fa651SNicolas Bonnefon 
34*f09fa651SNicolas Bonnefon   private:
35*f09fa651SNicolas Bonnefon     Action action_;
36*f09fa651SNicolas Bonnefon     std::wstring file_name_;
37*f09fa651SNicolas Bonnefon };
38*f09fa651SNicolas Bonnefon 
39*f09fa651SNicolas Bonnefon class WinNotificationInfoList {
40*f09fa651SNicolas Bonnefon   public:
41*f09fa651SNicolas Bonnefon     WinNotificationInfoList( const char* buffer, size_t buffer_size );
42*f09fa651SNicolas Bonnefon 
43*f09fa651SNicolas Bonnefon     // Iterator
44*f09fa651SNicolas Bonnefon     class iterator : std::iterator<std::input_iterator_tag, WinNotificationInfo> {
45*f09fa651SNicolas Bonnefon       public:
46*f09fa651SNicolas Bonnefon         iterator( WinNotificationInfoList* list, const char* position )
47*f09fa651SNicolas Bonnefon         { list_ = list; position_ = position; }
48*f09fa651SNicolas Bonnefon 
49*f09fa651SNicolas Bonnefon         const WinNotificationInfo& operator*() const
50*f09fa651SNicolas Bonnefon         { return list_->current_notification_; }
51*f09fa651SNicolas Bonnefon 
52*f09fa651SNicolas Bonnefon         const WinNotificationInfo* operator->() const
53*f09fa651SNicolas Bonnefon         { return &( list_->current_notification_ ); }
54*f09fa651SNicolas Bonnefon 
55*f09fa651SNicolas Bonnefon         const WinNotificationInfo& operator++() {
56*f09fa651SNicolas Bonnefon             position_ = list_->advanceToNext();
57*f09fa651SNicolas Bonnefon             return list_->current_notification_;
58*f09fa651SNicolas Bonnefon         }
59*f09fa651SNicolas Bonnefon 
60*f09fa651SNicolas Bonnefon         WinNotificationInfo operator++( int ) {
61*f09fa651SNicolas Bonnefon             WinNotificationInfo tmp { list_->current_notification_ };
62*f09fa651SNicolas Bonnefon             operator++();
63*f09fa651SNicolas Bonnefon             return tmp;
64*f09fa651SNicolas Bonnefon         }
65*f09fa651SNicolas Bonnefon 
66*f09fa651SNicolas Bonnefon         bool operator!=( const iterator& other ) const {
67*f09fa651SNicolas Bonnefon             return ( list_ != other.list_ ) || ( position_ != other.position_ );
68*f09fa651SNicolas Bonnefon         }
69*f09fa651SNicolas Bonnefon 
70*f09fa651SNicolas Bonnefon       private:
71*f09fa651SNicolas Bonnefon         WinNotificationInfoList* list_;
72*f09fa651SNicolas Bonnefon         const char* position_;
73*f09fa651SNicolas Bonnefon     };
74*f09fa651SNicolas Bonnefon 
75*f09fa651SNicolas Bonnefon     iterator begin() { return iterator( this, pointer_ ); }
76*f09fa651SNicolas Bonnefon     iterator end() { return iterator( this, nullptr ); }
77*f09fa651SNicolas Bonnefon 
78*f09fa651SNicolas Bonnefon   private:
79*f09fa651SNicolas Bonnefon     const char* advanceToNext();
80*f09fa651SNicolas Bonnefon 
81*f09fa651SNicolas Bonnefon     // Current notification (in the byte stream)
82*f09fa651SNicolas Bonnefon     const char* pointer_;
83*f09fa651SNicolas Bonnefon     // Next notification (in the byte stream)
84*f09fa651SNicolas Bonnefon     const char* next_;
85*f09fa651SNicolas Bonnefon     WinNotificationInfo current_notification_;
86*f09fa651SNicolas Bonnefon 
87*f09fa651SNicolas Bonnefon     const char* updateCurrentNotification( const char* new_position );
88*f09fa651SNicolas Bonnefon };
89*f09fa651SNicolas Bonnefon 
90*f09fa651SNicolas Bonnefon template <typename Driver>
91*f09fa651SNicolas Bonnefon class ObservedFile;
92*f09fa651SNicolas Bonnefon template <typename Driver>
93*f09fa651SNicolas Bonnefon class ObservedFileList;
94*f09fa651SNicolas Bonnefon 
95*f09fa651SNicolas Bonnefon class WinWatchTowerDriver {
96*f09fa651SNicolas Bonnefon   public:
97*f09fa651SNicolas Bonnefon     struct WinWatchedDirRecord {
98*f09fa651SNicolas Bonnefon         WinWatchedDirRecord( const std::string& file_name )
99*f09fa651SNicolas Bonnefon             : path_( file_name ) { }
100*f09fa651SNicolas Bonnefon 
101*f09fa651SNicolas Bonnefon         static const int READ_DIR_CHANGE_BUFFER_SIZE = 4096;
102*f09fa651SNicolas Bonnefon 
103*f09fa651SNicolas Bonnefon         std::string path_;
104*f09fa651SNicolas Bonnefon         void* handle_ = nullptr;
105*f09fa651SNicolas Bonnefon         static const unsigned long buffer_length_ = READ_DIR_CHANGE_BUFFER_SIZE;
106*f09fa651SNicolas Bonnefon         char buffer_[buffer_length_];
107*f09fa651SNicolas Bonnefon     };
108*f09fa651SNicolas Bonnefon 
109*f09fa651SNicolas Bonnefon     class FileId { };
110*f09fa651SNicolas Bonnefon     class SymlinkId { };
111*f09fa651SNicolas Bonnefon     class DirId {
112*f09fa651SNicolas Bonnefon       public:
113*f09fa651SNicolas Bonnefon         friend class WinWatchTowerDriver;
114*f09fa651SNicolas Bonnefon 
115*f09fa651SNicolas Bonnefon         DirId() {}
116*f09fa651SNicolas Bonnefon         bool operator==( const DirId& other ) const
117*f09fa651SNicolas Bonnefon         { return dir_record_ == other.dir_record_; }
118*f09fa651SNicolas Bonnefon       private:
119*f09fa651SNicolas Bonnefon         std::shared_ptr<WinWatchedDirRecord> dir_record_;
120*f09fa651SNicolas Bonnefon     };
121*f09fa651SNicolas Bonnefon 
122*f09fa651SNicolas Bonnefon     // Default constructor
123*f09fa651SNicolas Bonnefon     WinWatchTowerDriver();
124*f09fa651SNicolas Bonnefon     ~WinWatchTowerDriver();
125*f09fa651SNicolas Bonnefon 
126*f09fa651SNicolas Bonnefon     FileId addFile( const std::string& file_name );
127*f09fa651SNicolas Bonnefon     SymlinkId addSymlink( const std::string& file_name );
128*f09fa651SNicolas Bonnefon     DirId addDir( const std::string& file_name );
129*f09fa651SNicolas Bonnefon 
130*f09fa651SNicolas Bonnefon     void removeFile( const FileId& file_id );
131*f09fa651SNicolas Bonnefon     void removeSymlink( const SymlinkId& symlink_id );
132*f09fa651SNicolas Bonnefon 
133*f09fa651SNicolas Bonnefon     std::vector<ObservedFile<WinWatchTowerDriver>*> waitAndProcessEvents(
134*f09fa651SNicolas Bonnefon             ObservedFileList<WinWatchTowerDriver>* list,
135*f09fa651SNicolas Bonnefon             std::mutex* list_mutex );
136*f09fa651SNicolas Bonnefon 
137*f09fa651SNicolas Bonnefon     void interruptWait();
138*f09fa651SNicolas Bonnefon 
139*f09fa651SNicolas Bonnefon   private:
140*f09fa651SNicolas Bonnefon     // An action which will be picked up by the worker thread.
141*f09fa651SNicolas Bonnefon     class Action {
142*f09fa651SNicolas Bonnefon       public:
143*f09fa651SNicolas Bonnefon         Action( std::function<void()> function ) : function_ { function } {}
144*f09fa651SNicolas Bonnefon         ~Action() {}
145*f09fa651SNicolas Bonnefon 
146*f09fa651SNicolas Bonnefon         void operator()() { function_(); }
147*f09fa651SNicolas Bonnefon 
148*f09fa651SNicolas Bonnefon       private:
149*f09fa651SNicolas Bonnefon         std::function<void()> function_;
150*f09fa651SNicolas Bonnefon     };
151*f09fa651SNicolas Bonnefon 
152*f09fa651SNicolas Bonnefon     // Action
153*f09fa651SNicolas Bonnefon     std::mutex action_mutex_;
154*f09fa651SNicolas Bonnefon     std::condition_variable action_done_cv_;
155*f09fa651SNicolas Bonnefon     std::unique_ptr<Action> scheduled_action_ = nullptr;
156*f09fa651SNicolas Bonnefon 
157*f09fa651SNicolas Bonnefon     // Win32 notification variables
158*f09fa651SNicolas Bonnefon     HANDLE     hCompPort_;
159*f09fa651SNicolas Bonnefon     OVERLAPPED overlapped_;
160*f09fa651SNicolas Bonnefon     unsigned long buffer_length_;
161*f09fa651SNicolas Bonnefon 
162*f09fa651SNicolas Bonnefon     // List of directory records
163*f09fa651SNicolas Bonnefon     // Accessed exclusively in the worker thread context
164*f09fa651SNicolas Bonnefon     std::vector<std::weak_ptr<WinWatchedDirRecord>> dir_records_ { };
165*f09fa651SNicolas Bonnefon 
166*f09fa651SNicolas Bonnefon     // Private member functions
167*f09fa651SNicolas Bonnefon     void serialisedAddDir(
168*f09fa651SNicolas Bonnefon             const std::string& file_name,
169*f09fa651SNicolas Bonnefon             DirId& dir_id );
170*f09fa651SNicolas Bonnefon };
171*f09fa651SNicolas Bonnefon 
172*f09fa651SNicolas Bonnefon #endif
173