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