Retry only a fixed number of times in compiling RS fails

This commit is contained in:
Kovid Goyal 2015-10-27 21:44:35 +05:30
parent 467f7a3273
commit 93004be1b6

View File

@ -8,15 +8,19 @@ __copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
import os, sys, subprocess, signal, time, errno, socket import os, sys, subprocess, signal, time, errno, socket
from threading import Thread, Lock from threading import Thread, Lock
from Queue import Queue, Empty
from calibre.constants import islinux, iswindows, isosx from calibre.constants import islinux, iswindows, isosx
from calibre.srv.http_response import create_http_handler from calibre.srv.http_response import create_http_handler
from calibre.srv.loop import ServerLoop from calibre.srv.loop import ServerLoop
from calibre.srv.opts import Options from calibre.srv.opts import Options
from calibre.srv.standalone import create_option_parser from calibre.srv.standalone import create_option_parser
from calibre.srv.utils import create_sock_pair
from calibre.srv.web_socket import DummyHandler from calibre.srv.web_socket import DummyHandler
from calibre.utils.monotonic import monotonic from calibre.utils.monotonic import monotonic
MAX_RETRIES = 10
class NoAutoReload(EnvironmentError): class NoAutoReload(EnvironmentError):
pass pass
@ -45,6 +49,10 @@ class WatcherBase(object):
self.worker.restart() self.worker.restart()
self.last_restart_time = monotonic() self.last_restart_time = monotonic()
def force_restart(self):
self.worker.restart(forced=True)
self.last_restart_time = monotonic()
def file_is_watched(self, fname): def file_is_watched(self, fname):
return fname and fname.rpartition('.')[-1] in self.EXTENSIONS_TO_WATCH return fname and fname.rpartition('.')[-1] in self.EXTENSIONS_TO_WATCH
@ -56,6 +64,7 @@ if islinux:
def __init__(self, root_dirs, worker, log): def __init__(self, root_dirs, worker, log):
WatcherBase.__init__(self, worker, log) WatcherBase.__init__(self, worker, log)
self.client_sock, self.srv_sock = create_sock_pair()
self.fd_map = {} self.fd_map = {}
for d in frozenset(root_dirs): for d in frozenset(root_dirs):
w = INotifyTreeWatcher(d, self.ignore_event) w = INotifyTreeWatcher(d, self.ignore_event)
@ -63,9 +72,13 @@ if islinux:
def loop(self): def loop(self):
while True: while True:
r = select.select(list(self.fd_map.iterkeys()), [], [])[0] r = select.select([self.srv_sock] + list(self.fd_map.iterkeys()), [], [])[0]
modified = set() modified = set()
for fd in r: for fd in r:
if fd is self.srv_sock:
self.srv_sock.recv(1000)
self.force_restart()
continue
w = self.fd_map[fd] w = self.fd_map[fd]
modified |= w() modified |= w()
self.handle_modified(modified) self.handle_modified(modified)
@ -73,9 +86,11 @@ if islinux:
def ignore_event(self, path, name): def ignore_event(self, path, name):
return not self.file_is_watched(name) return not self.file_is_watched(name)
def wakeup(self):
self.client_sock.sendall(b'w')
elif iswindows: elif iswindows:
import win32file, win32con import win32file, win32con
from Queue import Queue
FILE_LIST_DIRECTORY = 0x0001 FILE_LIST_DIRECTORY = 0x0001
from calibre.srv.utils import HandleInterrupt from calibre.srv.utils import HandleInterrupt
@ -127,6 +142,9 @@ elif iswindows:
for d in frozenset(root_dirs): for d in frozenset(root_dirs):
self.watchers.append(TreeWatcher(d, self.modified_queue)) self.watchers.append(TreeWatcher(d, self.modified_queue))
def wakeup(self):
self.modified_queue.put(True)
def loop(self): def loop(self):
for w in self.watchers: for w in self.watchers:
w.start() w.start()
@ -135,6 +153,9 @@ elif iswindows:
path = self.modified_queue.get() path = self.modified_queue.get()
if path is None: if path is None:
break break
if path is True:
self.force_restart()
else:
self.handle_modified({path}) self.handle_modified({path})
elif isosx: elif isosx:
@ -145,6 +166,10 @@ elif isosx:
def __init__(self, root_dirs, worker, log): def __init__(self, root_dirs, worker, log):
WatcherBase.__init__(self, worker, log) WatcherBase.__init__(self, worker, log)
self.stream = Stream(self.notify, *(x.encode('utf-8') for x in root_dirs), file_events=True) self.stream = Stream(self.notify, *(x.encode('utf-8') for x in root_dirs), file_events=True)
self.wait_queue = Queue()
def wakeup(self):
self.wait_queue.put(True)
def loop(self): def loop(self):
observer = Observer() observer = Observer()
@ -153,9 +178,13 @@ elif isosx:
observer.start() observer.start()
try: try:
while True: while True:
# Cannot use observer.join() as it is not interrupted by try:
# Cannot use blocking get() as it is not interrupted by
# Ctrl-C # Ctrl-C
time.sleep(10000) if self.wait_queue.get(10000) is True:
self.force_restart()
except Empty:
pass
finally: finally:
observer.unschedule(self.stream) observer.unschedule(self.stream)
observer.stop() observer.stop()
@ -201,6 +230,7 @@ class Worker(object):
self.log = log self.log = log
self.server = server self.server = server
self.p = None self.p = None
self.wakeup = None
self.timeout = timeout self.timeout = timeout
cmd = self.cmd cmd = self.cmd
if 'calibre-debug' in cmd[0].lower(): if 'calibre-debug' in cmd[0].lower():
@ -214,6 +244,7 @@ class Worker(object):
opts = create_option_parser().parse_args(cmd)[0] opts = create_option_parser().parse_args(cmd)[0]
self.port = opts.port self.port = opts.port
self.connection_timeout = opts.timeout self.connection_timeout = opts.timeout
self.retry_count = 0
t = Thread(name='PingThread', target=self.ping_thread) t = Thread(name='PingThread', target=self.ping_thread)
t.daemon = True t.daemon = True
t.start() t.start()
@ -241,10 +272,11 @@ class Worker(object):
self.p.wait() self.p.wait()
self.p = None self.p = None
def restart(self): def restart(self, forced=False):
from calibre.utils.rapydscript import compile_srv, CompileFailure from calibre.utils.rapydscript import compile_srv, CompileFailure
self.clean_kill() self.clean_kill()
while True: if forced:
self.retry_count += 1
try: try:
compile_srv() compile_srv()
except EnvironmentError as e: except EnvironmentError as e:
@ -257,11 +289,12 @@ class Worker(object):
compile_srv() compile_srv()
except CompileFailure as e: except CompileFailure as e:
self.log.error(e.message) self.log.error(e.message)
self.log('Retrying in one second') time.sleep(0.1 * self.retry_count)
time.sleep(1) if self.retry_count < MAX_RETRIES:
continue self.wakeup() # Force a restart
break return
self.retry_count = 0
self.p = subprocess.Popen(self.cmd, creationflags=getattr(subprocess, 'CREATE_NEW_PROCESS_GROUP', 0)) self.p = subprocess.Popen(self.cmd, creationflags=getattr(subprocess, 'CREATE_NEW_PROCESS_GROUP', 0))
self.wait_for_listen() self.wait_for_listen()
self.server.notify_reload() self.server.notify_reload()
@ -354,6 +387,7 @@ def auto_reload(log, dirs=frozenset(), cmd=None, add_default_dirs=True):
log('Watching %d directory trees for changes' % len(dirs)) log('Watching %d directory trees for changes' % len(dirs))
with ReloadServer() as server, Worker(cmd, log, server) as worker: with ReloadServer() as server, Worker(cmd, log, server) as worker:
w = Watcher(dirs, worker, log) w = Watcher(dirs, worker, log)
worker.wakeup = w.wakeup
try: try:
w.loop() w.loop()
except KeyboardInterrupt: except KeyboardInterrupt: