X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5cd99866132366b74289e5a08e963723732bae01..64ea838d8f4d1853b7d850db93ee565e901d099a:/src/unix/fswatcher_inotify.cpp diff --git a/src/unix/fswatcher_inotify.cpp b/src/unix/fswatcher_inotify.cpp index edb3c52765..5bcc475698 100644 --- a/src/unix/fswatcher_inotify.cpp +++ b/src/unix/fswatcher_inotify.cpp @@ -91,8 +91,7 @@ public: wxCHECK_RET( IsOk(), "Inotify not initialized or invalid inotify descriptor" ); - delete m_source; - m_source = NULL; + wxDELETE(m_source); if ( close(m_ifd) != 0 ) { @@ -140,6 +139,9 @@ public: wxFAIL_MSG( wxString::Format("Path %s is not watched", watch->GetPath()) ); } + // Cache the wd in case any events arrive late + m_staleDescriptors.Add(watch->GetWatchDescriptor()); + watch->SetWatchDescriptor(-1); return true; } @@ -218,13 +220,39 @@ protected: // will be already removed from our list at that time if (inevt.mask & IN_IGNORED) { + // It is now safe to remove it from the stale descriptors too, we + // won't get any more events for it. + // However if we're here because a dir that we're still watching + // has just been deleted, its wd won't be on this list + const int pos = m_staleDescriptors.Index(inevt.wd); + if ( pos != wxNOT_FOUND ) + { + m_staleDescriptors.RemoveAt(static_cast(pos)); + wxLogTrace(wxTRACE_FSWATCHER, + "Removed wd %i from the stale-wd cache", inevt.wd); + } return; } // get watch entry for this event wxFSWatchEntryDescriptors::iterator it = m_watchMap.find(inevt.wd); - wxCHECK_RET(it != m_watchMap.end(), - "Watch descriptor not present in the watch map!"); + if (it == m_watchMap.end()) + { + // It's not in the map; check if was recently removed from it. + if (m_staleDescriptors.Index(inevt.wd) != wxNOT_FOUND) + { + wxLogTrace(wxTRACE_FSWATCHER, + "Got an event for stale wd %i", inevt.wd); + } + else + { + wxFAIL_MSG("Event for unknown watch descriptor."); + } + + // In any case, don't process this event: it's either for an + // already removed entry, or for a completely unknown one. + return; + } wxFSWatchEntry& watch = *(it->second); int nativeFlags = inevt.mask; @@ -243,11 +271,81 @@ protected: { return; } + + // Creation + // We need do something here only if the original watch was recursive; + // we don't watch a child dir itself inside a non-tree watch. + // We watch only dirs explicitly, so we don't want file IN_CREATEs. + // Distinguish by whether nativeFlags contain IN_ISDIR + else if ((nativeFlags & IN_CREATE) && + (watch.GetType() == wxFSWPath_Tree) && (inevt.mask & IN_ISDIR)) + { + wxFileName fn = GetEventPath(watch, inevt); + // Though it's a dir, fn treats it as a file. So: + fn.AssignDir(fn.GetFullPath()); + + if (m_watcher->AddAny(fn, wxFSW_EVENT_ALL, + wxFSWPath_Tree, watch.GetFilespec())) + { + // Tell the owner, in case it's interested + // If there's a filespec, assume he's not + if (watch.GetFilespec().empty()) + { + wxFileSystemWatcherEvent event(flags, fn, fn); + SendEvent(event); + } + } + } + + // Deletion + // We watch only dirs explicitly, so we don't want file IN_DELETEs. + // We obviously can't check using DirExists() as the object has been + // deleted; and nativeFlags here doesn't contain IN_ISDIR, even for + // a dir. Fortunately IN_DELETE_SELF doesn't happen for files. We need + // to do something here only inside a tree watch, or if it's the parent + // dir that's deleted. Otherwise let the parent dir cope + else if ((nativeFlags & IN_DELETE_SELF) && + ((watch.GetType() == wxFSWPath_Dir) || + (watch.GetType() == wxFSWPath_Tree))) + { + // We must remove the deleted directory from the map, so that + // DoRemoveInotify() isn't called on it in the future. Don't assert + // if the wd isn't found: repeated IN_DELETE_SELFs can occur + wxFileName fn = GetEventPath(watch, inevt); + wxString path(fn.GetPathWithSep()); + + if (m_watchMap.erase(inevt.wd) == 1) + { + // Delete from wxFileSystemWatcher + wxDynamicCast(m_watcher, wxInotifyFileSystemWatcher)-> + OnDirDeleted(path); + + // Now remove from our local list of watched items + wxFSWatchEntries::iterator wit = + m_watches.find(path); + if (wit != m_watches.end()) + { + m_watches.erase(wit); + } + + // Cache the wd in case any events arrive late + m_staleDescriptors.Add(inevt.wd); + } + + // Tell the owner, in case it's interested + // If there's a filespec, assume he's not + if (watch.GetFilespec().empty()) + { + wxFileSystemWatcherEvent event(flags, fn, fn); + SendEvent(event); + } + } + // renames else if (nativeFlags & IN_MOVE) { - wxInotifyCookies::iterator it = m_cookies.find(inevt.cookie); - if ( it == m_cookies.end() ) + wxInotifyCookies::iterator it2 = m_cookies.find(inevt.cookie); + if ( it2 == m_cookies.end() ) { int size = sizeof(inevt) + inevt.len; inotify_event* e = (inotify_event*) operator new (size); @@ -258,22 +356,27 @@ protected: } else { - inotify_event& oldinevt = *(it->second); + inotify_event& oldinevt = *(it2->second); - wxFileSystemWatcherEvent event(flags); - if ( inevt.mask & IN_MOVED_FROM ) - { - event.SetPath(GetEventPath(watch, inevt)); - event.SetNewPath(GetEventPath(watch, oldinevt)); - } - else + // Tell the owner, in case it's interested + // If there's a filespec, assume he's not + if ( watch.GetFilespec().empty() ) { - event.SetPath(GetEventPath(watch, oldinevt)); - event.SetNewPath(GetEventPath(watch, inevt)); + wxFileSystemWatcherEvent event(flags); + if ( inevt.mask & IN_MOVED_FROM ) + { + event.SetPath(GetEventPath(watch, inevt)); + event.SetNewPath(GetEventPath(watch, oldinevt)); + } + else + { + event.SetPath(GetEventPath(watch, oldinevt)); + event.SetNewPath(GetEventPath(watch, inevt)); + } + SendEvent(event); } - SendEvent(event); - m_cookies.erase(it); + m_cookies.erase(it2); delete &oldinevt; } } @@ -281,8 +384,12 @@ protected: else { wxFileName path = GetEventPath(watch, inevt); - wxFileSystemWatcherEvent event(flags, path, path); - SendEvent(event); + // For files, check that it matches any filespec + if ( MatchesFilespec(path, watch.GetFilespec()) ) + { + wxFileSystemWatcherEvent event(flags, path, path); + SendEvent(event); + } } } @@ -301,11 +408,18 @@ protected: wxCHECK_RET(wit != m_watchMap.end(), "Watch descriptor not present in the watch map!"); + // Tell the owner, in case it's interested + // If there's a filespec, assume he's not wxFSWatchEntry& watch = *(wit->second); - int flags = Native2WatcherFlags(inevt.mask); - wxFileName path = GetEventPath(watch, inevt); - wxFileSystemWatcherEvent event(flags, path, path); - SendEvent(event); + if ( watch.GetFilespec().empty() ) + { + int flags = Native2WatcherFlags(inevt.mask); + wxFileName path = GetEventPath(watch, inevt); + { + wxFileSystemWatcherEvent event(flags, path, path); + SendEvent(event); + } + } m_cookies.erase(it); delete &inevt; @@ -345,9 +459,12 @@ protected: wxString mask = (inevt.mask & IN_ISDIR) ? wxString::Format("IS_DIR | %u", inevt.mask & ~IN_ISDIR) : wxString::Format("%u", inevt.mask); + const char* name = ""; + if (inevt.len) + name = inevt.name; return wxString::Format("Event: wd=%d, mask=%s, cookie=%u, len=%u, " "name=%s", inevt.wd, mask, inevt.cookie, - inevt.len, inevt.name); + inevt.len, name); } static wxFileName GetEventPath(const wxFSWatchEntry& watch, @@ -355,7 +472,7 @@ protected: { // only when dir is watched, we have non-empty e.name wxFileName path = watch.GetPath(); - if (path.IsDir()) + if (path.IsDir() && inevt.len) { path = wxFileName(path.GetPath(), inevt.name); } @@ -423,6 +540,7 @@ protected: wxFSWSourceHandler* m_handler; // handler for inotify event source wxFSWatchEntryDescriptors m_watchMap; // inotify wd=>wxFSWatchEntry* map + wxArrayInt m_staleDescriptors; // stores recently-removed watches wxInotifyCookies m_cookies; // map to track renames wxEventLoopSource* m_source; // our event loop source @@ -487,6 +605,19 @@ bool wxInotifyFileSystemWatcher::Init() return m_service->Init(); } +void wxInotifyFileSystemWatcher::OnDirDeleted(const wxString& path) +{ + if (!path.empty()) + { + wxFSWatchInfoMap::iterator it = m_watches.find(path); + wxCHECK_RET(it != m_watches.end(), + wxString::Format("Path '%s' is not watched", path)); + + // path has been deleted, so we must forget it whatever its refcount + m_watches.erase(it); + } +} + #endif // wxHAS_INOTIFY #endif // wxUSE_FSWATCHER