mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Use shared memory for IPC with toc editor
This commit is contained in:
parent
54fd6080fc
commit
3155a36b4b
@ -6,6 +6,7 @@ __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os
|
||||
from itertools import count
|
||||
from collections import OrderedDict
|
||||
from qt.core import (
|
||||
QCheckBox, QDialog, QDialogButtonBox, QGridLayout, QIcon, QLabel, QTimer
|
||||
@ -100,6 +101,7 @@ class ToCEditAction(InterfaceAction):
|
||||
self.do_edit(book_id_map)
|
||||
|
||||
def genesis(self):
|
||||
self.shm_count = count()
|
||||
self.qaction.triggered.connect(self.edit_books)
|
||||
self.jobs = []
|
||||
|
||||
@ -146,34 +148,51 @@ class ToCEditAction(InterfaceAction):
|
||||
self.do_one(book_id, fmt)
|
||||
|
||||
def do_one(self, book_id, fmt):
|
||||
import struct, json, atexit
|
||||
from calibre.utils.shm import SharedMemory
|
||||
db = self.gui.current_db
|
||||
path = db.format(book_id, fmt, index_is_id=True, as_path=True)
|
||||
title = db.title(book_id, index_is_id=True) + ' [%s]'%fmt
|
||||
data = {'path': path, 'title': title}
|
||||
self.gui.job_manager.launch_gui_app('toc-dialog', kwargs=data)
|
||||
job = data.copy()
|
||||
job.update({'book_id': book_id, 'fmt': fmt, 'library_id': db.new_api.library_id, 'started': False, 'start_time': monotonic()})
|
||||
job = {'path': path, 'title': title}
|
||||
data = json.dumps(job).encode('utf-8')
|
||||
header = struct.pack('>II', 0, 0)
|
||||
shm = SharedMemory(prefix=f'c{os.getpid()}-{next(self.shm_count)}-', size=len(data) + len(header) + SharedMemory.num_bytes_for_size)
|
||||
shm.write(header)
|
||||
shm.write_data_with_size(data)
|
||||
shm.flush()
|
||||
atexit.register(shm.close)
|
||||
self.gui.job_manager.launch_gui_app('toc-dialog', kwargs={'shm_name': shm.name})
|
||||
job.update({
|
||||
'book_id': book_id, 'fmt': fmt, 'library_id': db.new_api.library_id, 'shm': shm, 'started': False, 'start_time': monotonic()})
|
||||
self.jobs.append(job)
|
||||
self.check_for_completions()
|
||||
|
||||
def check_for_completions(self):
|
||||
from calibre.utils.filenames import retry_on_fail
|
||||
import struct
|
||||
|
||||
def remove_job(job):
|
||||
job['shm'].close()
|
||||
self.jobs.remove(job)
|
||||
|
||||
for job in tuple(self.jobs):
|
||||
path = job['path']
|
||||
started_path = path + '.started'
|
||||
result_path = path + '.result'
|
||||
if job['started'] and os.path.exists(result_path):
|
||||
self.jobs.remove(job)
|
||||
ret = -1
|
||||
|
||||
def read(result_path):
|
||||
nonlocal ret
|
||||
with open(result_path) as f:
|
||||
ret = int(f.read().strip())
|
||||
|
||||
retry_on_fail(read, result_path)
|
||||
retry_on_fail(os.remove, result_path)
|
||||
if ret == 0:
|
||||
shm = job['shm']
|
||||
shm.seek(0)
|
||||
state, ok = struct.unpack('>II', shm.read(struct.calcsize('>II')))
|
||||
if state == 0:
|
||||
# not started
|
||||
if monotonic() - job['start_time'] > 120:
|
||||
remove_job(job)
|
||||
error_dialog(self.gui, _('Failed to start editor'), _(
|
||||
'Could not edit: {}. The Table of Contents editor did not start in two minutes').format(job['title']), show=True)
|
||||
elif state == 1:
|
||||
# running
|
||||
pass
|
||||
elif state == 2:
|
||||
# finished
|
||||
job['shm'].already_unlinked = True
|
||||
remove_job(job)
|
||||
if ok == 1:
|
||||
db = self.gui.current_db
|
||||
if db.new_api.library_id != job['library_id']:
|
||||
error_dialog(self.gui, _('Library changed'), _(
|
||||
@ -181,16 +200,6 @@ class ToCEditAction(InterfaceAction):
|
||||
' the calibre library has changed.').format(job['title']), show=True)
|
||||
else:
|
||||
db.new_api.add_format(job['book_id'], job['fmt'], path, run_hooks=False)
|
||||
retry_on_fail(os.remove, path)
|
||||
elif not job['started']:
|
||||
if monotonic() - job['start_time'] > 120:
|
||||
self.jobs.remove(job)
|
||||
error_dialog(self.gui, _('Failed to start editor'), _(
|
||||
'Could not edit: {}. The Table of Contents editor did not start in two minutes').format(job['title']), show=True)
|
||||
continue
|
||||
if os.path.exists(started_path):
|
||||
job['started'] = True
|
||||
retry_on_fail(os.remove, started_path)
|
||||
if self.jobs:
|
||||
QTimer.singleShot(100, self.check_for_completions)
|
||||
|
||||
|
@ -1145,7 +1145,11 @@ class TOCEditor(QDialog): # {{{
|
||||
# }}}
|
||||
|
||||
|
||||
def main(path=None, title=None):
|
||||
def main(shm_name=None):
|
||||
import json
|
||||
import struct
|
||||
from calibre.utils.shm import SharedMemory
|
||||
|
||||
# Ensure we can continue to function if GUI is closed
|
||||
os.environ.pop('CALIBRE_WORKER_TEMP_DIR', None)
|
||||
reset_base_dir()
|
||||
@ -1154,19 +1158,28 @@ def main(path=None, title=None):
|
||||
# prevents them from being grouped with viewer/editor process when
|
||||
# launched from within calibre, as both use calibre-parallel.exe
|
||||
set_app_uid(TOC_DIALOG_APP_UID)
|
||||
with SharedMemory(name=shm_name) as shm:
|
||||
pos = struct.calcsize('>II')
|
||||
state, ok = struct.unpack('>II', shm.read(pos))
|
||||
data = json.loads(shm.read_data_with_size())
|
||||
title = data['title']
|
||||
path = data['path']
|
||||
s = struct.pack('>I', 1)
|
||||
shm.seek(0), shm.write(s), shm.flush()
|
||||
|
||||
override = 'calibre-gui' if islinux else None
|
||||
app = Application([], override_program_name=override)
|
||||
d = TOCEditor(path, title=title, write_result_to=path + '.result')
|
||||
d.start()
|
||||
ok = 0
|
||||
if d.exec() == QDialog.DialogCode.Accepted:
|
||||
ok = 1
|
||||
s = struct.pack('>II', 2, ok)
|
||||
shm.seek(0), shm.write(s), shm.flush()
|
||||
|
||||
with open(path + '.started', 'w'):
|
||||
pass
|
||||
override = 'calibre-gui' if islinux else None
|
||||
app = Application([], override_program_name=override)
|
||||
d = TOCEditor(path, title=title, write_result_to=path + '.result')
|
||||
d.start()
|
||||
ret = 1
|
||||
if d.exec() == QDialog.DialogCode.Accepted:
|
||||
ret = 0
|
||||
del d
|
||||
del app
|
||||
raise SystemExit(ret)
|
||||
raise SystemExit(0 if ok else 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Loading…
x
Reference in New Issue
Block a user