From e9c31c32936b123edf60114d1a07e7f59b2142c3 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Fri, 5 Mar 2021 12:45:22 +0000 Subject: [PATCH] Implement releasing of monitor.JournalLock() * Implement JournalLock.release_lock(). * Renamed other methods to remove journaldir_ prefix. * Slightly tweak the code flow for obtaining the lock. * Implement JournalLock.update_lock(), called from AppWindow.postprefs(). --- EDMarketConnector.py | 6 ++-- monitor.py | 85 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 77 insertions(+), 14 deletions(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 4067cbc8..a47baaf5 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -79,8 +79,6 @@ if __name__ == '__main__': # noqa: C901 """ logger.trace('Begin...') - locked = journal_lock.journaldir_obtain_lock() - if platform == 'win32': if not locked: @@ -191,6 +189,7 @@ if __name__ == '__main__': # noqa: C901 root.mainloop() journal_lock = JournalLock() + locked = journal_lock.obtain_lock() if journal_lock.journal_dir_lockfile: if not no_other_instance_running(): @@ -551,6 +550,9 @@ class AppWindow(object): # (Re-)install hotkey monitoring hotkeymgr.register(self.w, config.getint('hotkey_code'), config.getint('hotkey_mods')) + # Update Journal lock if needs be. + journal_lock.update_lock() + # (Re-)install log monitoring if not monitor.start(self.w): self.status['text'] = f'Error: Check {_("E:D journal file location")}' diff --git a/monitor.py b/monitor.py index 89b2bb16..21b1567b 100644 --- a/monitor.py +++ b/monitor.py @@ -5,6 +5,7 @@ import threading from os import getpid as os_getpid from os import listdir, SEEK_SET, SEEK_END from os.path import basename, expanduser, isdir, join +import pathlib from sys import platform from time import gmtime, localtime, sleep, strftime, strptime, time from calendar import timegm @@ -1030,9 +1031,20 @@ class JournalLock: def __init__(self): """Initialise where the journal directory and lock file are.""" self.journal_dir: str = config.get('journaldir') or config.default_journal_dir + self.journal_dir_path = pathlib.Path(self.journal_dir) self.journal_dir_lock = None + self.journal_dir_lockfile_name = None + self.journal_dir_lockfile = None - self.journal_dir_lockfile_name: str = join(self.journal_dir, 'edmc-journal-lock.txt') + def obtain_lock(self) -> bool: + """ + Attempt to obtain a lock on the journal directory. + + :return: bool - True if we successfully obtained the lock + """ + logger.trace(f'journal_dir_lockfile = {self.journal_dir_lockfile!r}') + + self.journal_dir_lockfile_name: str = self.journal_dir_path / 'edmc-journal-lock.txt' try: self.journal_dir_lockfile = open(self.journal_dir_lockfile_name, mode='w+', encoding='utf-8') @@ -1042,14 +1054,6 @@ class JournalLock: logger.warning(f"Couldn't open \"{self.journal_dir_lockfile_name}\" for \"w+\"" f" Aborting duplicate process checks: {e!r}") - def journaldir_obtain_lock(self) -> bool: - """ - Attempt to obtain a lock on the journal directory. - - :return: bool - True if we successfully obtained the lock - """ - logger.trace(f'journal_dir_lockfile = {self.journal_dir_lockfile!r}') - if platform == 'win32': logger.trace('win32, using msvcrt') # win32 doesn't have fcntl, so we have to use msvcrt @@ -1087,6 +1091,63 @@ class JournalLock: logger.trace('Done') return True - def journaldir_release_lock(self) -> bool: - """Release lock on journal directory.""" - pass + def release_lock(self) -> bool: + """ + Release lock on journal directory. + + :return: bool - Success of unlocking operation.""" + unlocked = False + if platform == 'win32': + logger.trace('win32, using msvcrt') + # win32 doesn't have fcntl, so we have to use msvcrt + import msvcrt + + try: + msvcrt.locking(self.journal_dir_lockfile.fileno(), msvcrt.LK_UNLCK, 4096) + + except Exception as e: + logger.info(f"Exception: Couldn't unlock journal directory \"{self.journal_dir}\": {e!r}") + + else: + unlocked = True + + else: + logger.trace('NOT win32, using fcntl') + try: + import fcntl + + except ImportError: + logger.warning("Not on win32 and we have no fcntl, can't use a file lock!") + return True # Lie about being unlocked + + try: + fcntl.flock(self.journal_dir_lockfile, fcntl.LOCK_UN) + + except Exception as e: + logger.info(f"Exception: Couldn't unlock journal directory \"{self.journal_dir}\": {e!r}") + + else: + unlocked = True + + # Close the file whether or not the unlocking succeeded. + self.journal_dir_lockfile.close() + + self.journal_dir_lock = None + self.journal_dir_lockfile_name = None + self.journal_dir_lockfile = None + + return unlocked + + def update_lock(self) -> bool: + """ + Update journal directory lock to new location if possible. + + :return: bool - Success of obtaining new lock + """ + current_journaldir = config.get('journaldir') or config.default_journal_dir + + if current_journaldir == self.journal_dir: + return True # Still the same + + self.journaldir_release_lock() + return self.journaldir_obtain_lock()