1 /////////////////////////////////////////////////////////////////////////////
2 // Name: wx/msw/private/fswatcher.h
3 // Purpose: File system watcher impl classes
4 // Author: Bartosz Bekier
6 // Copyright: (c) 2009 Bartosz Bekier <bartosz.bekier@gmail.com>
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 #ifndef WX_MSW_PRIVATE_FSWATCHER_H_
11 #define WX_MSW_PRIVATE_FSWATCHER_H_
13 #include "wx/filename.h"
14 #include "wx/vector.h"
15 #include "wx/msw/private.h"
17 // ============================================================================
18 // wxFSWatcherEntry implementation & helper declarations
19 // ============================================================================
21 class wxFSWatcherImplMSW
;
23 class wxFSWatchEntryMSW
: public wxFSWatchInfo
28 BUFFER_SIZE
= 4096 // TODO parametrize
31 wxFSWatchEntryMSW(const wxFSWatchInfo
& winfo
) :
34 // get handle for this path
35 m_handle
= OpenDir(m_path
);
36 m_overlapped
= (OVERLAPPED
*)calloc(1, sizeof(OVERLAPPED
));
37 wxZeroMemory(m_buffer
);
40 virtual ~wxFSWatchEntryMSW()
42 wxLogTrace(wxTRACE_FSWATCHER
, "Deleting entry '%s'", m_path
);
44 if (m_handle
!= INVALID_HANDLE_VALUE
)
46 if (!CloseHandle(m_handle
))
48 wxLogSysError(_("Unable to close the handle for '%s'"),
57 return m_handle
!= INVALID_HANDLE_VALUE
;
60 HANDLE
GetHandle() const
70 OVERLAPPED
* GetOverlapped() const
76 // opens dir with all flags, attributes etc. necessary to be later
77 // asynchronous watched with ReadDirectoryChangesW
78 static HANDLE
OpenDir(const wxString
& path
)
80 HANDLE handle
= CreateFile(path
.t_str(),
87 FILE_FLAG_BACKUP_SEMANTICS
|
90 if (handle
== INVALID_HANDLE_VALUE
)
92 wxLogSysError(_("Failed to open directory \"%s\" for monitoring."),
99 HANDLE m_handle
; // handle to opened directory
100 char m_buffer
[BUFFER_SIZE
]; // buffer for fs events
101 OVERLAPPED
* m_overlapped
;
103 wxDECLARE_NO_COPY_CLASS(wxFSWatchEntryMSW
);
106 // ============================================================================
107 // wxFSWatcherImplMSW helper classes implementations
108 // ============================================================================
114 m_iocp(INVALID_HANDLE_VALUE
)
121 if (m_iocp
!= INVALID_HANDLE_VALUE
)
123 if (!CloseHandle(m_iocp
))
125 wxLogSysError(_("Unable to close I/O completion port handle"));
131 // associates a wxFSWatchEntryMSW with completion port
132 bool Add(wxSharedPtr
<wxFSWatchEntryMSW
> watch
)
134 wxCHECK_MSG( m_iocp
!= INVALID_HANDLE_VALUE
, false, "IOCP not init" );
135 wxCHECK_MSG( watch
->IsOk(), false, "Invalid watch" );
137 // associate with IOCP
138 HANDLE ret
= CreateIoCompletionPort(watch
->GetHandle(), m_iocp
,
139 (ULONG_PTR
)watch
.get(), 0);
142 wxLogSysError(_("Unable to associate handle with "
143 "I/O completion port"));
146 else if (ret
!= m_iocp
)
148 wxFAIL_MSG(_("Unexpectedly new I/O completion port was created"));
153 wxFSWatchEntries::value_type
val(watch
->GetPath(), watch
);
154 return m_watches
.insert(val
).second
;
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
161 bool ScheduleForRemoval(const wxSharedPtr
<wxFSWatchEntryMSW
>& watch
)
163 wxCHECK_MSG( m_iocp
!= INVALID_HANDLE_VALUE
, false, "IOCP not init" );
164 wxCHECK_MSG( watch
->IsOk(), false, "Invalid watch" );
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" );
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()
176 m_removedWatches
.insert(wxFSWatchEntries::value_type(path
, watch
));
182 // Really remove the watch previously passed to ScheduleForRemoval().
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
)
188 wxFSWatchEntries::iterator it
= m_removedWatches
.find(watch
->GetPath());
189 if ( it
== m_removedWatches
.end() )
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
);
199 // post completion packet
200 bool PostEmptyStatus()
202 wxCHECK_MSG( m_iocp
!= INVALID_HANDLE_VALUE
, false, "IOCP not init" );
204 int ret
= PostQueuedCompletionStatus(m_iocp
, 0, 0, NULL
);
207 wxLogSysError(_("Unable to post completion status"));
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
)
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'");
225 int ret
= GetQueuedCompletionStatus(m_iocp
, count
, (ULONG_PTR
*)watch
,
226 overlapped
, INFINITE
);
229 wxLogSysError(_("Unable to dequeue completion packet"));
237 m_iocp
= CreateIoCompletionPort(INVALID_HANDLE_VALUE
, NULL
, 0, 0);
240 wxLogSysError(_("Unable to create I/O completion port"));
242 return m_iocp
!= NULL
;
247 // The hash containing all the wxFSWatchEntryMSW objects currently being
248 // watched keyed by their paths.
249 wxFSWatchEntries m_watches
;
251 // Contains the watches which had been removed but are still pending.
252 wxFSWatchEntries m_removedWatches
;
256 class wxIOCPThread
: public wxThread
259 wxIOCPThread(wxFSWatcherImplMSW
* service
, wxIOCPService
* iocp
);
261 // finishes this thread
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
269 wxEventProcessingData(const FILE_NOTIFY_INFORMATION
* ne
,
270 const wxFSWatchEntryMSW
* watch
) :
271 nativeEvent(ne
), watch(watch
)
274 const FILE_NOTIFY_INFORMATION
* nativeEvent
;
275 const wxFSWatchEntryMSW
* watch
;
278 virtual ExitCode
Entry();
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
285 void ProcessNativeEvents(wxVector
<wxEventProcessingData
>& events
);
287 void SendEvent(wxFileSystemWatcherEvent
& evt
);
289 static int Native2WatcherFlags(int flags
);
291 static wxString
FileNotifyInformationToString(
292 const FILE_NOTIFY_INFORMATION
& e
);
294 static wxFileName
GetEventPath(const wxFSWatchEntryMSW
& watch
,
295 const FILE_NOTIFY_INFORMATION
& e
);
297 wxFSWatcherImplMSW
* m_service
;
298 wxIOCPService
* m_iocp
;
301 #endif /* WX_MSW_PRIVATE_FSWATCHER_H_ */