diff --git a/setup.py b/setup.py index aa40fa9e..c15f8850 100644 --- a/setup.py +++ b/setup.py @@ -345,6 +345,10 @@ def params(): # documentation dependencies install_requires = install_requires + extras_require['develop'] + import sys + if sys.platform in ("linux2", "darwin"): + install_requires += ["monotime"] + entry_points = { "console_scripts": [ "octoprint = octoprint:main" diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 206d17f6..c9a6daec 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -58,6 +58,7 @@ import octoprint.filemanager.analysis import octoprint.slicing from . import util +util.tornado.fix_ioloop_scheduling() UI_API_KEY = ''.join('%02X' % ord(z) for z in uuid.uuid4().bytes) @@ -655,6 +656,11 @@ class Server(): app.register_blueprint(blueprint, url_prefix=url_prefix) logger.debug("Registered API of plugin {name} under URL prefix {url_prefix}".format(name=name, url_prefix=url_prefix)) + ## Tornado initialization starts here + + ioloop = IOLoop() + ioloop.install() + self._router = SockJSRouter(self._createSocketConnection, "/sockjs") upload_suffixes = dict(name=settings().get(["server", "uploads", "nameSuffix"]), path=settings().get(["server", "uploads", "pathSuffix"])) @@ -684,8 +690,6 @@ class Server(): observer.schedule(util.watchdog.GcodeWatchdogHandler(fileManager, printer), settings().getBaseFolder("watched")) observer.start() - ioloop = IOLoop.instance() - # run our startup plugins octoprint.plugin.call_plugin(octoprint.plugin.StartupPlugin, "on_startup", diff --git a/src/octoprint/server/util/tornado.py b/src/octoprint/server/util/tornado.py index 18e7281a..399dd4d7 100644 --- a/src/octoprint/server/util/tornado.py +++ b/src/octoprint/server/util/tornado.py @@ -29,6 +29,36 @@ import tornado.util import octoprint.util +#~~ Monkey patching + + +def fix_ioloop_scheduling(): + """ + This monkey patches tornado's :meth:`tornado.ioloop.PeriodicCallback._schedule_next` method so it no longer + blocks for long times on slow machines (RPi) when the system time happens to change by a large amount (e.g. due to + the first ever contact to an NTP server). + + Patch by @nosyjoe on Github. See this PR against tornado: https://github.com/tornadoweb/tornado/pull/1290 + """ + + import math + + # patched implementation taken from PR + def _schedule_next(self): + if self._running: + current_time = self.io_loop.time() + + if self._next_timeout <= current_time: + callback_time_sec = self.callback_time / 1000.0 + self._next_timeout += (math.floor((current_time - self._next_timeout) / callback_time_sec) + 1) * callback_time_sec + + self._timeout = self.io_loop.add_timeout(self._next_timeout, self._run) + + # replace original implementation with patched version + import tornado.ioloop + tornado.ioloop.PeriodicCallback._schedule_next = _schedule_next + + #~~ WSGI middleware