Better fix for error on restart

Now both single instance and listener mutexes are explicitly released
before launching child process, so the child process has no need to wait
for the parent process to die.
This commit is contained in:
Kovid Goyal 2020-10-17 13:19:53 +05:30
parent fc77917e9c
commit f15e144e2a
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -30,6 +30,7 @@ from calibre.utils.lock import SingleInstance
from calibre.utils.monotonic import monotonic from calibre.utils.monotonic import monotonic
from polyglot.builtins import as_bytes, environ_item, range, unicode_type from polyglot.builtins import as_bytes, environ_item, range, unicode_type
after_quit_actions = {'debug_on_restart': False, 'restart_after_quit': False}
if iswindows: if iswindows:
winutil = plugins['winutil'][0] winutil = plugins['winutil'][0]
@ -353,27 +354,18 @@ class GuiRunner(QObject):
self.initialize_db() self.initialize_db()
def set_restarting_env_var():
if iswindows:
ctime = winutil.get_process_times(None)[0]
os.environ['CALIBRE_RESTARTING_FROM_GUI'] = str(ctime)
else:
os.environ['CALIBRE_RESTARTING_FROM_GUI'] = str(os.getpid())
def run_in_debug_mode(): def run_in_debug_mode():
from calibre.debug import run_calibre_debug from calibre.debug import run_calibre_debug
import tempfile, subprocess import tempfile, subprocess
fd, logpath = tempfile.mkstemp('.txt') fd, logpath = tempfile.mkstemp('.txt')
os.close(fd) os.close(fd)
set_restarting_env_var()
run_calibre_debug( run_calibre_debug(
'--gui-debug', logpath, stdout=lopen(logpath, 'wb'), '--gui-debug', logpath, stdout=lopen(logpath, 'wb'),
stderr=subprocess.STDOUT, stdin=lopen(os.devnull, 'rb')) stderr=subprocess.STDOUT, stdin=lopen(os.devnull, 'rb'))
def run_gui(opts, args, listener, app, gui_debug=None): def run_gui(opts, args, listener, app, gui_debug=None):
with SingleInstance('db') as si: with listener, SingleInstance('db') as si:
if not si: if not si:
ext = '.exe' if iswindows else '' ext = '.exe' if iswindows else ''
error_dialog(None, _('Cannot start calibre'), _( error_dialog(None, _('Cannot start calibre'), _(
@ -404,31 +396,8 @@ def run_gui_(opts, args, listener, app, gui_debug=None):
from calibre.gui2.wizard import wizard from calibre.gui2.wizard import wizard
wizard().exec_() wizard().exec_()
if getattr(runner.main, 'restart_after_quit', False): if getattr(runner.main, 'restart_after_quit', False):
e = sys.executable if getattr(sys, 'frozen', False) else sys.argv[0] after_quit_actions['restart_after_quit'] = True
if getattr(runner.main, 'debug_on_restart', False) or gui_debug is not None: after_quit_actions['debug_on_restart'] = getattr(runner.main, 'debug_on_restart', False) or gui_debug is not None
run_in_debug_mode()
else:
if hasattr(sys, 'frameworks_dir'):
app = os.path.dirname(os.path.dirname(os.path.realpath(sys.frameworks_dir)))
from calibre.debug import run_calibre_debug
prints('Restarting with:', app)
run_calibre_debug('-c', 'import sys, os, time; time.sleep(3); os.execlp("open", "open", sys.argv[-1])', app)
else:
import subprocess
set_restarting_env_var()
if iswindows:
winutil.prepare_for_restart()
if hasattr(sys, 'run_local'):
cmd = [sys.run_local]
if DEBUG:
cmd += ['calibre-debug', '-g']
else:
cmd.append('calibre')
else:
args = ['-g'] if os.path.splitext(e)[0].endswith('-debug') else []
cmd = [e] + args
prints('Restarting with:', ' '.join(cmd))
subprocess.Popen(cmd)
else: else:
if iswindows: if iswindows:
try: try:
@ -436,7 +405,6 @@ def run_gui_(opts, args, listener, app, gui_debug=None):
except: except:
pass pass
if getattr(runner.main, 'gui_debug', None) is not None: if getattr(runner.main, 'gui_debug', None) is not None:
e = sys.executable if getattr(sys, 'frozen', False) else sys.argv[0]
debugfile = runner.main.gui_debug debugfile = runner.main.gui_debug
from calibre.gui2 import open_local_file from calibre.gui2 import open_local_file
if iswindows: if iswindows:
@ -527,30 +495,36 @@ def create_listener():
return Listener(address=gui_socket_address()) return Listener(address=gui_socket_address())
def wait_for_parent_to_die(ppid, max_wait=10): def restart_after_quit():
ppid = int(ppid)
if iswindows: if iswindows:
# detach the stdout/stderr/stdin handles
def parent_done(): winutil.prepare_for_restart()
try: if after_quit_actions['debug_on_restart']:
ctime = winutil.get_process_times(os.getppid())[0] run_in_debug_mode()
except Exception: return
return True if hasattr(sys, 'frameworks_dir'):
return ctime > ppid app = os.path.dirname(os.path.dirname(os.path.realpath(sys.frameworks_dir)))
from calibre.debug import run_calibre_debug
prints('Restarting with:', app)
run_calibre_debug('-c', 'import sys, os, time; time.sleep(3); os.execlp("open", "open", sys.argv[-1])', app)
else: else:
def parent_done(): import subprocess
return os.getppid() != ppid if hasattr(sys, 'run_local'):
cmd = [sys.run_local]
st = time.monotonic() if DEBUG:
while not parent_done() and time.monotonic() - st < max_wait: cmd += ['calibre-debug', '-g']
time.sleep(0.1) else:
cmd.append('calibre')
else:
e = sys.executable if getattr(sys, 'frozen', False) else sys.argv[0]
cmd = [e]
if os.path.splitext(e)[0].endswith('-debug'):
cmd.append('-g')
prints('Restarting with:', ' '.join(cmd))
subprocess.Popen(cmd)
def main(args=sys.argv): def main(args=sys.argv):
ppid = os.environ.pop('CALIBRE_RESTARTING_FROM_GUI', None)
if ppid is not None:
wait_for_parent_to_die(ppid)
if iswindows and 'CALIBRE_REPAIR_CORRUPTED_DB' in os.environ: if iswindows and 'CALIBRE_REPAIR_CORRUPTED_DB' in os.environ:
windows_repair() windows_repair()
return 0 return 0
@ -567,6 +541,8 @@ def main(args=sys.argv):
if si and opts.shutdown_running_calibre: if si and opts.shutdown_running_calibre:
return 0 return 0
run_main(app, opts, args, gui_debug, si) run_main(app, opts, args, gui_debug, si)
if after_quit_actions['restart_after_quit']:
restart_after_quit()
def run_main(app, opts, args, gui_debug, si): def run_main(app, opts, args, gui_debug, si):