Fix regression in 0.8.33 that caused calibre to crash when starting the Content Server, if the port the content server is trying to listen on is blocked/busy. Fixes #910512 (calibre crashs after start without message)

This commit is contained in:
Kovid Goyal 2012-01-02 10:59:33 +05:30
parent 87c2406cd8
commit b117148241
4 changed files with 114 additions and 87 deletions

View File

@ -63,9 +63,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
def start_server(self):
ConfigWidgetBase.commit(self)
self.setCursor(Qt.BusyCursor)
try:
self.gui.start_content_server(check_started=False)
while not self.gui.content_server.is_running and self.gui.content_server.exception is None:
time.sleep(1)
while (not self.gui.content_server.is_running and
self.gui.content_server.exception is None):
time.sleep(0.1)
if self.gui.content_server.exception is not None:
error_dialog(self, _('Failed to start content server'),
as_unicode(self.gui.content_server.exception)).exec_()
@ -73,6 +76,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.start_button.setEnabled(False)
self.test_button.setEnabled(True)
self.stop_button.setEnabled(True)
finally:
self.unsetCursor()
def stop_server(self):
self.gui.content_server.threaded_exit()

View File

@ -368,9 +368,14 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
self.library_view.model().db, server_config().parse())
self.content_server.state_callback = Dispatcher(
self.iactions['Connect Share'].content_server_state_changed)
self.content_server.state_callback(True)
if check_started:
QTimer.singleShot(10000, self.test_server)
self.content_server.start_failure_callback = \
Dispatcher(self.content_server_start_failed)
def content_server_start_failed(self, msg):
error_dialog(self, _('Failed to start Content Server'),
_('Could not start the content server. Error:\n\n%s')%msg,
show=True)
def resizeEvent(self, ev):
MainWindow.resizeEvent(self, ev)

View File

@ -26,7 +26,7 @@ from calibre.library.server.cache import Cache
from calibre.library.server.browse import BrowseServer
from calibre.library.server.ajax import AjaxServer
from calibre.utils.search_query_parser import saved_searches
from calibre import prints
from calibre import prints, as_unicode
class DispatchController(object): # {{{
@ -112,6 +112,7 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
self.opts = opts
self.embedded = embedded
self.state_callback = None
self.start_failure_callback = None
try:
self.max_cover_width, self.max_cover_height = \
map(int, self.opts.max_cover.split('x'))
@ -225,10 +226,7 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
h.setFormatter(cherrypy._cplogging.logfmt)
log.access_log.addHandler(h)
def start(self):
self.is_running = False
cherrypy.tree.mount(root=None, config=self.config)
try:
def start_cherrypy(self):
try:
cherrypy.engine.start()
except:
@ -244,17 +242,36 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
cherrypy.engine.start()
self.is_running = True
#if hasattr(cherrypy.engine, 'signal_handler'):
# cherrypy.engine.signal_handler.subscribe()
cherrypy.engine.block()
def start(self):
self.is_running = False
self.exception = None
cherrypy.tree.mount(root=None, config=self.config)
try:
self.start_cherrypy()
except Exception as e:
self.exception = e
import traceback
traceback.print_exc()
if callable(self.start_failure_callback):
try:
self.start_failure_callback(as_unicode(e))
except:
pass
return
try:
self.is_running = True
self.notify_listener()
cherrypy.engine.block()
except Exception as e:
import traceback
traceback.print_exc()
self.exception = e
finally:
self.is_running = False
self.notify_listener()
def notify_listener(self):
try:
if callable(self.state_callback):
self.state_callback(self.is_running)
@ -267,11 +284,7 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
finally:
cherrypy.server.httpserver = None
self.is_running = False
try:
if callable(self.state_callback):
self.state_callback(self.is_running)
except:
pass
self.notify_listener()
def threaded_exit(self):
from threading import Thread

View File

@ -268,7 +268,11 @@ class Bus(object):
# Assume it's been logged and just die.
os._exit(70) # EX_SOFTWARE
if exitstate == states.STARTING:
# Changed by Kovid, we cannot have all of calibre being quit
# Also we want to catch the port blocked/busy error and try listening only on
# the external ip
# See https://bitbucket.org/cherrypy/cherrypy/issue/1017/exit-behavior-is-not-good-when-running-in
if False and exitstate == states.STARTING:
# exit() was called before start() finished, possibly due to
# Ctrl-C because a start listener got stuck. In this case,
# we could get stuck in a loop where Ctrl-C never exits the