]> git.saurik.com Git - wxWidgets.git/blob - include/wx/msw/private/fswatcher.h
synchronize GTK2 minimum version in docs
[wxWidgets.git] / include / wx / msw / private / fswatcher.h
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: wx/msw/private/fswatcher.h
3 // Purpose: File system watcher impl classes
4 // Author: Bartosz Bekier
5 // Created: 2009-05-26
6 // RCS-ID: $Id$
7 // Copyright: (c) 2009 Bartosz Bekier <bartosz.bekier@gmail.com>
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 #ifndef WX_MSW_PRIVATE_FSWATCHER_H_
12 #define WX_MSW_PRIVATE_FSWATCHER_H_
13
14 #include "wx/filename.h"
15 #include "wx/vector.h"
16 #include "wx/msw/private.h"
17
18 // ============================================================================
19 // wxFSWatcherEntry implementation & helper declarations
20 // ============================================================================
21
22 class wxFSWatcherImplMSW;
23
24 class wxFSWatchEntryMSW : public wxFSWatchInfo
25 {
26 public:
27 enum
28 {
29 BUFFER_SIZE = 4096 // TODO parametrize
30 };
31
32 wxFSWatchEntryMSW(const wxFSWatchInfo& winfo) :
33 wxFSWatchInfo(winfo)
34 {
35 // get handle for this path
36 m_handle = OpenDir(m_path);
37 m_overlapped = (OVERLAPPED*)calloc(1, sizeof(OVERLAPPED));
38 wxZeroMemory(m_buffer);
39 }
40
41 virtual ~wxFSWatchEntryMSW()
42 {
43 wxLogTrace(wxTRACE_FSWATCHER, "Deleting entry '%s'", m_path);
44
45 if (m_handle != INVALID_HANDLE_VALUE)
46 {
47 if (!CloseHandle(m_handle))
48 {
49 wxLogSysError(_("Unable to close the handle for '%s'"),
50 m_path);
51 }
52 }
53 free(m_overlapped);
54 }
55
56 bool IsOk() const
57 {
58 return m_handle != INVALID_HANDLE_VALUE;
59 }
60
61 HANDLE GetHandle() const
62 {
63 return m_handle;
64 }
65
66 void* GetBuffer()
67 {
68 return m_buffer;
69 }
70
71 OVERLAPPED* GetOverlapped() const
72 {
73 return m_overlapped;
74 }
75
76 private:
77 // opens dir with all flags, attributes etc. necessary to be later
78 // asynchronous watched with ReadDirectoryChangesW
79 static HANDLE OpenDir(const wxString& path)
80 {
81 HANDLE handle = CreateFile(path.t_str(),
82 FILE_LIST_DIRECTORY,
83 FILE_SHARE_READ |
84 FILE_SHARE_WRITE |
85 FILE_SHARE_DELETE,
86 NULL,
87 OPEN_EXISTING,
88 FILE_FLAG_BACKUP_SEMANTICS |
89 FILE_FLAG_OVERLAPPED,
90 NULL);
91 if (handle == INVALID_HANDLE_VALUE)
92 {
93 wxLogSysError(_("Failed to open directory \"%s\" for monitoring."),
94 path);
95 }
96
97 return handle;
98 }
99
100 HANDLE m_handle; // handle to opened directory
101 char m_buffer[BUFFER_SIZE]; // buffer for fs events
102 OVERLAPPED* m_overlapped;
103
104 wxDECLARE_NO_COPY_CLASS(wxFSWatchEntryMSW);
105 };
106
107 // ============================================================================
108 // wxFSWatcherImplMSW helper classes implementations
109 // ============================================================================
110
111 class wxIOCPService
112 {
113 public:
114 wxIOCPService() :
115 m_iocp(INVALID_HANDLE_VALUE)
116 {
117 Init();
118 }
119
120 ~wxIOCPService()
121 {
122 if (m_iocp != INVALID_HANDLE_VALUE)
123 {
124 if (!CloseHandle(m_iocp))
125 {
126 wxLogSysError(_("Unable to close I/O completion port handle"));
127 }
128 }
129 m_watches.clear();
130 }
131
132 // associates a wxFSWatchEntryMSW with completion port
133 bool Add(wxSharedPtr<wxFSWatchEntryMSW> watch)
134 {
135 wxCHECK_MSG( m_iocp != INVALID_HANDLE_VALUE, false, "IOCP not init" );
136 wxCHECK_MSG( watch->IsOk(), false, "Invalid watch" );
137
138 // associate with IOCP
139 HANDLE ret = CreateIoCompletionPort(watch->GetHandle(), m_iocp,
140 (ULONG_PTR)watch.get(), 0);
141 if (ret == NULL)
142 {
143 wxLogSysError(_("Unable to associate handle with "
144 "I/O completion port"));
145 return false;
146 }
147 else if (ret != m_iocp)
148 {
149 wxFAIL_MSG(_("Unexpectedly new I/O completion port was created"));
150 return false;
151 }
152
153 // add to watch map
154 wxFSWatchEntries::value_type val(watch->GetPath(), watch);
155 return m_watches.insert(val).second;
156 }
157
158 // Removes a watch we're currently using. Notice that this doesn't happen
159 // immediately, CompleteRemoval() must be called later when it's really
160 // safe to delete the watch, i.e. after completion of the IO operation
161 // using it.
162 bool ScheduleForRemoval(const wxSharedPtr<wxFSWatchEntryMSW>& watch)
163 {
164 wxCHECK_MSG( m_iocp != INVALID_HANDLE_VALUE, false, "IOCP not init" );
165 wxCHECK_MSG( watch->IsOk(), false, "Invalid watch" );
166
167 const wxString path = watch->GetPath();
168 wxFSWatchEntries::iterator it = m_watches.find(path);
169 wxCHECK_MSG( it != m_watches.end(), false,
170 "Can't remove a watch we don't use" );
171
172 // We can't just delete the watch here as we can have pending events
173 // for it and if we destroyed it now, we could get a dangling (or,
174 // worse, reused to point to another object) pointer in ReadEvents() so
175 // just remember that this one should be removed when CompleteRemoval()
176 // is called later.
177 m_removedWatches.insert(wxFSWatchEntries::value_type(path, watch));
178 m_watches.erase(it);
179
180 return true;
181 }
182
183 // Really remove the watch previously passed to ScheduleForRemoval().
184 //
185 // It's ok to call this for a watch that hadn't been removed before, in
186 // this case we'll just return false and do nothing.
187 bool CompleteRemoval(wxFSWatchEntryMSW* watch)
188 {
189 wxFSWatchEntries::iterator it = m_removedWatches.find(watch->GetPath());
190 if ( it == m_removedWatches.end() )
191 return false;
192
193 // Removing the object from the map will result in deleting the watch
194 // itself as it's not referenced from anywhere else now.
195 m_removedWatches.erase(it);
196
197 return true;
198 }
199
200 // post completion packet
201 bool PostEmptyStatus()
202 {
203 wxCHECK_MSG( m_iocp != INVALID_HANDLE_VALUE, false, "IOCP not init" );
204
205 int ret = PostQueuedCompletionStatus(m_iocp, 0, 0, NULL);
206 if (!ret)
207 {
208 wxLogSysError(_("Unable to post completion status"));
209 }
210
211 return ret != 0;
212 }
213
214 // Wait for completion status to arrive.
215 // This function can block forever in it's wait for completion status.
216 // Use PostEmptyStatus() to wake it up (and end the worker thread)
217 bool GetStatus(unsigned long* count, wxFSWatchEntryMSW** watch,
218 OVERLAPPED** overlapped)
219 {
220 wxCHECK_MSG( m_iocp != INVALID_HANDLE_VALUE, false, "IOCP not init" );
221 wxCHECK_MSG( count != NULL, false, "Null out parameter 'count'");
222 wxCHECK_MSG( watch != NULL, false, "Null out parameter 'watch'");
223 wxCHECK_MSG( overlapped != NULL, false,
224 "Null out parameter 'overlapped'");
225
226 int ret = GetQueuedCompletionStatus(m_iocp, count, (ULONG_PTR *)watch,
227 overlapped, INFINITE);
228 if (!ret)
229 {
230 wxLogSysError(_("Unable to dequeue completion packet"));
231 }
232 return ret != 0;
233 }
234
235 protected:
236 bool Init()
237 {
238 m_iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
239 if (m_iocp == NULL)
240 {
241 wxLogSysError(_("Unable to create I/O completion port"));
242 }
243 return m_iocp != NULL;
244 }
245
246 HANDLE m_iocp;
247
248 // The hash containing all the wxFSWatchEntryMSW objects currently being
249 // watched keyed by their paths.
250 wxFSWatchEntries m_watches;
251
252 // Contains the watches which had been removed but are still pending.
253 wxFSWatchEntries m_removedWatches;
254 };
255
256
257 class wxIOCPThread : public wxThread
258 {
259 public:
260 wxIOCPThread(wxFSWatcherImplMSW* service, wxIOCPService* iocp);
261
262 // finishes this thread
263 bool Finish();
264
265 protected:
266 // structure to hold information needed to process one native event
267 // this is just a dummy holder, so it doesn't take ownership of it's data
268 struct wxEventProcessingData
269 {
270 wxEventProcessingData(const FILE_NOTIFY_INFORMATION* ne,
271 const wxFSWatchEntryMSW* watch) :
272 nativeEvent(ne), watch(watch)
273 {}
274
275 const FILE_NOTIFY_INFORMATION* nativeEvent;
276 const wxFSWatchEntryMSW* watch;
277 };
278
279 virtual ExitCode Entry();
280
281 // wait for events to occur, read them and send to interested parties
282 // returns false it empty status was read, which means we whould exit
283 // true otherwise
284 bool ReadEvents();
285
286 void ProcessNativeEvents(wxVector<wxEventProcessingData>& events);
287
288 void SendEvent(wxFileSystemWatcherEvent& evt);
289
290 static int Native2WatcherFlags(int flags);
291
292 static wxString FileNotifyInformationToString(
293 const FILE_NOTIFY_INFORMATION& e);
294
295 static wxFileName GetEventPath(const wxFSWatchEntryMSW& watch,
296 const FILE_NOTIFY_INFORMATION& e);
297
298 wxFSWatcherImplMSW* m_service;
299 wxIOCPService* m_iocp;
300 };
301
302 #endif /* WX_MSW_PRIVATE_FSWATCHER_H_ */