pyinotify: fixes sporadic runtime error "dictionary changed size during iteration" (if something outside changes the pending dict during _checkPending evaluation) - simply deserialize to a list for iteration, without any lock, because unneeded here due to small and mostly empty dictionary (logrotate, etc), not to mention that pending check is normally called once per minute;

don't call process file inside of server thread calling of addLogPath (always retard it as pending event);
ensure to wake-up as soon as possible to process pending events (e. g. if file gets added).
pull/3117/head
sebres 2021-09-08 19:16:49 +02:00
parent 2f99d5accb
commit 1e4a14fb25
1 changed files with 15 additions and 12 deletions

View File

@ -165,7 +165,7 @@ class FilterPyinotify(FileFilter):
return return
found = {} found = {}
minTime = 60 minTime = 60
for path, (retardTM, isDir) in self.__pending.iteritems(): for path, (retardTM, isDir) in list(self.__pending.items()):
if ntm - self.__pendingChkTime < retardTM: if ntm - self.__pendingChkTime < retardTM:
if minTime > retardTM: minTime = retardTM if minTime > retardTM: minTime = retardTM
continue continue
@ -268,15 +268,13 @@ class FilterPyinotify(FileFilter):
def _addLogPath(self, path): def _addLogPath(self, path):
self._addFileWatcher(path) self._addFileWatcher(path)
# initial scan: # notify (wake up if in waiting):
if self.active: if self.active:
# we can execute it right now: self.__pendingMinTime = 0
self._process_file(path) # retard until filter gets started, isDir=None signals special case: process file only (don't need to refresh monitor):
else: self._addPending(path, ('INITIAL', path), isDir=None)
# retard until filter gets started, isDir=None signals special case: process file only (don't need to refresh monitor):
self._addPending(path, ('INITIAL', path), isDir=None)
## ##
# Delete a log path # Delete a log path
# #
# @param path the log file to delete # @param path the log file to delete
@ -341,12 +339,17 @@ class FilterPyinotify(FileFilter):
self.__notifier.process_events() self.__notifier.process_events()
# wait for events / timeout: # wait for events / timeout:
notify_maxtout = self.__notify_maxtout
def __check_events(): def __check_events():
return not self.active or self.__notifier.check_events(timeout=notify_maxtout) return (
if Utils.wait_for(__check_events, min(self.sleeptime, self.__pendingMinTime)): not self.active
or bool(self.__notifier.check_events(timeout=self.__notify_maxtout))
or (self.__pendingMinTime and self.__pending)
)
wres = Utils.wait_for(__check_events, min(self.sleeptime, self.__pendingMinTime))
if wres:
if not self.active: break if not self.active: break
self.__notifier.read_events() if not isinstance(wres, dict):
self.__notifier.read_events()
self.ticks += 1 self.ticks += 1