Skip to content
This repository

Use time.monotonic() where available. #583

Closed
wants to merge 2 commits into from

3 participants

apenwarr Don't Add Me To Your Organization a.k.a The Travis Bot Ben Darnell
apenwarr

This patch changes tornado to use time.monotonic() wherever it can, rather than time.time(). This allows it to survive better when the normal clock jumps around (because of NTP or other reasons).

time.monotonic() is part of python 3.3, but you can also get my implementation for earlier versions from here: http://pypi.python.org/pypi/Monotime/ . This patch causes tornado to try that implementation if time.monotonic() is not otherwise available.

added some commits August 13, 2012
apenwarr Disable tests when old versions of twisted are installed.
Twisted 10.0 (on an older version of Ubuntu) doesn't seem to work with class
decorators, even on python 2.6.  This makes the tests fail with a TypeError:

Traceback (most recent call last):
  File "tornado/test/import_test.py", line 59, in test_import_twisted
    import tornado.platform.twisted
  File "tornado/platform/twisted.py", line 108, in <module>
    TornadoDelayedCall = implementer(IDelayedCall)(TornadoDelayedCall)
  File "/usr/lib/python2.6/dist-packages/zope/interface/declarations.py",
line 496, in __call__
    raise TypeError("Can't use implementer with classes.  Use one of "
TypeError: Can't use implementer with classes.  Use one of the
class-declaration functions instead.

If we catch a typeerror while importing twisted, act like twisted is not
installed.
a5bc9b5
apenwarr tornado: use time.monotonic() where available.
In ioloop.add_timeout(), we still support adding timeouts using time.time(),
but we actually convert them to time.monotonic() before running.  If you
don't explicitly set monotonic=False when calling add_timeout(), you'll get
a warning when that happens.

But mostly, you should really be passing in a datetime.timedelta object,
because almost always you want to use relative time instead of absolute
time.
a17cc80
apenwarr

See also #558

Don't Add Me To Your Organization a.k.a The Travis Bot

This pull request passes (merged a17cc80 into 4daeaeb).

Ben Darnell
Collaborator

Cool. I like this change, although I think it's probably too aggressive in encouraging the use of monotonic time. Logging a warning for each use of add_timeout(int) that doesn't specify monotonic is excessive (also the code doesn't seem to distinguish between None and False like the docstring says). I also wouldn't log at startup if neither time.monotonic or monotime is available (using the logging module at import time can mess up the logging configuration. I know we do it in ioloop.py but at least in that case it's limited to installations that are somehow broken).

In pull request #558, the time function is an attribute of the IOLoop instead of a global, which would let you have one thread's IOLoop using monotonic time and another thread on time.time(). I can see where that would be useful but it is kind of esoteric.

I don't really like the monotonic argument to add_timeout - it does allow for both monotonic and time.time-based code to coexist on the same IOLoop, but the new argument is effectively mandatory, which will get annoying. I'd almost say that add_timeout(int) should always use time.time (for cron-like tasks, "run this at midnight"), even when add_timeout(timedelta) uses monotonic time (and most instances of add_timeout should be converted to the timedelta form).

For the accompanying twisted change, a TypeError on importing tornado.platform.twisted could be a bug in that module, so I think this is a case where explicit version checks are better than catching the exception. I believe 11.0.0 is the first twisted version we support (it's at least the oldest version still in the tox config).

apenwarr
Ben Darnell bdarnell referenced this pull request from a commit September 30, 2012
Ben Darnell Add time_func parameter to IOLoop, and make it possible to use time.m…
…onotonic.

This means that calls to IOLoop.add_timeout that pass a number must be
updated to use IOLoop.time instead of time.time.

There are still some places where we use time.time in the code, but they
are either places where wall time is desired, or non-critical deltas (e.g.
printing elapsed time at the end of a request).

Thanks to apenwarr and mgenti for pull requests and discussion relating to
this change. (#558 and #583)
20deb5c
Ben Darnell
Collaborator

I've committed a time.monotonic change drawing on both pull requests. Like mgenti's version, I added a time_func argument to IOLoop's constructor. Instead of a monotonic argument to add_timeout, I simply said that all calls to add_timeout must be relative to the IOLoop's clock. This is going to be harder to audit for, but since use of a monotonic clock is opt-in I don't think it's too unreasonable (but I'm open to feedback on this point; it's not too late to change). Thanks to both of you for the pull requests and feedback.

Ben Darnell bdarnell closed this October 01, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 2 unique commits by 1 author.

Aug 13, 2012
apenwarr Disable tests when old versions of twisted are installed.
Twisted 10.0 (on an older version of Ubuntu) doesn't seem to work with class
decorators, even on python 2.6.  This makes the tests fail with a TypeError:

Traceback (most recent call last):
  File "tornado/test/import_test.py", line 59, in test_import_twisted
    import tornado.platform.twisted
  File "tornado/platform/twisted.py", line 108, in <module>
    TornadoDelayedCall = implementer(IDelayedCall)(TornadoDelayedCall)
  File "/usr/lib/python2.6/dist-packages/zope/interface/declarations.py",
line 496, in __call__
    raise TypeError("Can't use implementer with classes.  Use one of "
TypeError: Can't use implementer with classes.  Use one of the
class-declaration functions instead.

If we catch a typeerror while importing twisted, act like twisted is not
installed.
a5bc9b5
apenwarr tornado: use time.monotonic() where available.
In ioloop.add_timeout(), we still support adding timeouts using time.time(),
but we actually convert them to time.monotonic() before running.  If you
don't explicitly set monotonic=False when calling add_timeout(), you'll get
a warning when that happens.

But mostly, you should really be passing in a datetime.timedelta object,
because almost always you want to use relative time instead of absolute
time.
a17cc80
This page is out of date. Refresh to see the latest.
9  tornado/curl_httpclient.py
@@ -20,10 +20,10 @@
20 20
 
21 21
 import cStringIO
22 22
 import collections
  23
+import datetime
23 24
 import logging
24 25
 import pycurl
25 26
 import threading
26  
-import time
27 27
 
28 28
 from tornado import httputil
29 29
 from tornado import ioloop
@@ -31,6 +31,7 @@
31 31
 
32 32
 from tornado.escape import utf8
33 33
 from tornado.httpclient import HTTPRequest, HTTPResponse, HTTPError, AsyncHTTPClient, main
  34
+from tornado.util import monotime
34 35
 
35 36
 
36 37
 class CurlAsyncHTTPClient(AsyncHTTPClient):
@@ -108,7 +109,7 @@ def _set_timeout(self, msecs):
108 109
         if self._timeout is not None:
109 110
             self.io_loop.remove_timeout(self._timeout)
110 111
         self._timeout = self.io_loop.add_timeout(
111  
-            time.time() + msecs / 1000.0, self._handle_timeout)
  112
+            datetime.timedelta(milliseconds=msecs), self._handle_timeout)
112 113
 
113 114
     def _handle_events(self, fd, events):
114 115
         """Called by IOLoop when there is activity on one of our
@@ -200,7 +201,7 @@ def _process_queue(self):
200 201
                         "buffer": cStringIO.StringIO(),
201 202
                         "request": request,
202 203
                         "callback": callback,
203  
-                        "curl_start_time": time.time(),
  204
+                        "curl_start_time": monotime(),
204 205
                     }
205 206
                     # Disable IPv6 to mitigate the effects of this bug
206 207
                     # on curl versions <= 7.21.0
@@ -246,7 +247,7 @@ def _finish(self, curl, curl_error=None, curl_message=None):
246 247
             info["callback"](HTTPResponse(
247 248
                 request=info["request"], code=code, headers=info["headers"],
248 249
                 buffer=buffer, effective_url=effective_url, error=error,
249  
-                request_time=time.time() - info["curl_start_time"],
  250
+                request_time=monotime() - info["curl_start_time"],
250 251
                 time_info=time_info))
251 252
         except Exception:
252 253
             self.handle_callback_exception(info["callback"])
8  tornado/database.py
@@ -23,6 +23,8 @@
23 23
 import logging
24 24
 import time
25 25
 
  26
+from tornado.util import monotime
  27
+
26 28
 try:
27 29
     import MySQLdb.constants
28 30
     import MySQLdb.converters
@@ -79,7 +81,7 @@ def __init__(self, host, database, user=None, password=None,
79 81
 
80 82
         self._db = None
81 83
         self._db_args = args
82  
-        self._last_use_time = time.time()
  84
+        self._last_use_time = monotime()
83 85
         try:
84 86
             self.reconnect()
85 87
         except Exception:
@@ -195,9 +197,9 @@ def _ensure_connected(self):
195 197
         # case by preemptively closing and reopening the connection
196 198
         # if it has been idle for too long (7 hours by default).
197 199
         if (self._db is None or
198  
-            (time.time() - self._last_use_time > self.max_idle_time)):
  200
+            (monotime() - self._last_use_time > self.max_idle_time)):
199 201
             self.reconnect()
200  
-        self._last_use_time = time.time()
  202
+        self._last_use_time = monotime()
201 203
 
202 204
     def _cursor(self):
203 205
         self._ensure_connected()
5  tornado/httpclient.py
@@ -34,13 +34,12 @@
34 34
 import calendar
35 35
 import email.utils
36 36
 import httplib
37  
-import time
38 37
 import weakref
39 38
 
40 39
 from tornado.escape import utf8
41 40
 from tornado import httputil
42 41
 from tornado.ioloop import IOLoop
43  
-from tornado.util import import_object, bytes_type
  42
+from tornado.util import import_object, bytes_type, monotime
44 43
 
45 44
 
46 45
 class HTTPClient(object):
@@ -319,7 +318,7 @@ def __init__(self, url, method="GET", headers=None, body=None,
319 318
         self.allow_ipv6 = allow_ipv6
320 319
         self.client_key = client_key
321 320
         self.client_cert = client_cert
322  
-        self.start_time = time.time()
  321
+        self.start_time = monotime()
323 322
 
324 323
 
325 324
 class HTTPResponse(object):
9  tornado/httpserver.py
@@ -29,14 +29,13 @@ class except to start a server at the beginning of the process
29 29
 import Cookie
30 30
 import logging
31 31
 import socket
32  
-import time
33 32
 
34 33
 from tornado.escape import native_str, parse_qs_bytes
35 34
 from tornado import httputil
36 35
 from tornado import iostream
37 36
 from tornado.netutil import TCPServer
38 37
 from tornado import stack_context
39  
-from tornado.util import b, bytes_type
  38
+from tornado.util import b, bytes_type, monotime
40 39
 
41 40
 try:
42 41
     import ssl  # Python 2.6+
@@ -378,7 +377,7 @@ def __init__(self, method, uri, version="HTTP/1.0", headers=None,
378 377
         self.host = host or self.headers.get("Host") or "127.0.0.1"
379 378
         self.files = files or {}
380 379
         self.connection = connection
381  
-        self._start_time = time.time()
  380
+        self._start_time = monotime()
382 381
         self._finish_time = None
383 382
 
384 383
         self.path, sep, self.query = uri.partition('?')
@@ -414,7 +413,7 @@ def write(self, chunk, callback=None):
414 413
     def finish(self):
415 414
         """Finishes this HTTP request on the open connection."""
416 415
         self.connection.finish()
417  
-        self._finish_time = time.time()
  416
+        self._finish_time = monotime()
418 417
 
419 418
     def full_url(self):
420 419
         """Reconstructs the full URL for this request."""
@@ -423,7 +422,7 @@ def full_url(self):
423 422
     def request_time(self):
424 423
         """Returns the amount of time it took for this request to execute."""
425 424
         if self._finish_time is None:
426  
-            return time.time() - self._start_time
  425
+            return monotime() - self._start_time
427 426
         else:
428 427
             return self._finish_time - self._start_time
429 428
 
34  tornado/ioloop.py
@@ -47,6 +47,7 @@
47 47
     signal = None
48 48
 
49 49
 from tornado.platform.auto import set_close_exec, Waker
  50
+from tornado.util import monotime
50 51
 
51 52
 
52 53
 class IOLoop(object):
@@ -271,7 +272,7 @@ def start(self):
271 272
                 self._run_callback(callback)
272 273
 
273 274
             if self._timeouts:
274  
-                now = time.time()
  275
+                now = monotime()
275 276
                 while self._timeouts:
276 277
                     if self._timeouts[0].callback is None:
277 278
                         # the timeout was cancelled
@@ -366,7 +367,7 @@ def running(self):
366 367
         """Returns true if this IOLoop is currently running."""
367 368
         return self._running
368 369
 
369  
-    def add_timeout(self, deadline, callback):
  370
+    def add_timeout(self, deadline, callback, monotonic=None):
370 371
         """Calls the given callback at the time deadline from the I/O loop.
371 372
 
372 373
         Returns a handle that may be passed to remove_timeout to cancel.
@@ -378,8 +379,15 @@ def add_timeout(self, deadline, callback):
378 379
         Note that it is not safe to call `add_timeout` from other threads.
379 380
         Instead, you must use `add_callback` to transfer control to the
380 381
         IOLoop's thread, and then call `add_timeout` from there.
  382
+
  383
+        Set monotonic=False if deadline is from time.time(), or monotonic=True
  384
+        if it comes from tornado.util.monotime().  If deadline is a
  385
+        datetime.timedelta, you can omit the monotonic flag.  For backward
  386
+        compatibility, an unspecified monotonic flag acts like monotonic=False
  387
+        but prints a warning.
381 388
         """
382  
-        timeout = _Timeout(deadline, stack_context.wrap(callback))
  389
+        timeout = _Timeout(deadline, stack_context.wrap(callback),
  390
+                           monotonic=monotonic)
383 391
         heapq.heappush(self._timeouts, timeout)
384 392
         return timeout
385 393
 
@@ -441,11 +449,18 @@ class _Timeout(object):
441 449
     # Reduce memory overhead when there are lots of pending callbacks
442 450
     __slots__ = ['deadline', 'callback']
443 451
 
444  
-    def __init__(self, deadline, callback):
  452
+    def __init__(self, deadline, callback, monotonic):
445 453
         if isinstance(deadline, (int, long, float)):
446  
-            self.deadline = deadline
  454
+            if monotonic:
  455
+                self.deadline = deadline
  456
+            else:
  457
+                if hasattr(time, 'monotonic'):
  458
+                    import inspect
  459
+                    logging.warning('non-monotonic time _Timeout() created at %s:%d',
  460
+                                    inspect.stack()[2][1], inspect.stack()[2][2])
  461
+                self.deadline = deadline - time.time() + monotime()
447 462
         elif isinstance(deadline, datetime.timedelta):
448  
-            self.deadline = time.time() + _Timeout.timedelta_to_seconds(deadline)
  463
+            self.deadline = monotime() + _Timeout.timedelta_to_seconds(deadline)
449 464
         else:
450 465
             raise TypeError("Unsupported deadline %r" % deadline)
451 466
         self.callback = callback
@@ -485,7 +500,7 @@ def __init__(self, callback, callback_time, io_loop=None):
485 500
     def start(self):
486 501
         """Starts the timer."""
487 502
         self._running = True
488  
-        self._next_timeout = time.time()
  503
+        self._next_timeout = monotime()
489 504
         self._schedule_next()
490 505
 
491 506
     def stop(self):
@@ -506,10 +521,11 @@ def _run(self):
506 521
 
507 522
     def _schedule_next(self):
508 523
         if self._running:
509  
-            current_time = time.time()
  524
+            current_time = monotime()
510 525
             while self._next_timeout <= current_time:
511 526
                 self._next_timeout += self.callback_time / 1000.0
512  
-            self._timeout = self.io_loop.add_timeout(self._next_timeout, self._run)
  527
+            self._timeout = self.io_loop.add_timeout(self._next_timeout,
  528
+                                    self._run, monotonic=True)
513 529
 
514 530
 
515 531
 class _EPoll(object):
7  tornado/platform/twisted.py
@@ -48,7 +48,6 @@
48 48
 
49 49
 import functools
50 50
 import logging
51  
-import time
52 51
 
53 52
 from twisted.internet.posixbase import PosixReactorBase
54 53
 from twisted.internet.interfaces import \
@@ -62,6 +61,7 @@
62 61
 import tornado.ioloop
63 62
 from tornado.stack_context import NullContext
64 63
 from tornado.ioloop import IOLoop
  64
+from tornado.util import monotime
65 65
 
66 66
 
67 67
 class TornadoDelayedCall(object):
@@ -71,7 +71,8 @@ def __init__(self, reactor, seconds, f, *args, **kw):
71 71
         self._func = functools.partial(f, *args, **kw)
72 72
         self._time = self._reactor.seconds() + seconds
73 73
         self._timeout = self._reactor._io_loop.add_timeout(self._time,
74  
-                                                           self._called)
  74
+                                                           self._called,
  75
+                                                           monotonic=True)
75 76
         self._active = True
76 77
 
77 78
     def _called(self):
@@ -139,7 +140,7 @@ def start_if_necessary():
139 140
 
140 141
     # IReactorTime
141 142
     def seconds(self):
142  
-        return time.time()
  143
+        return monotime()
143 144
 
144 145
     def callLater(self, seconds, f, *args, **kw):
145 146
         dc = TornadoDelayedCall(self, seconds, f, *args, **kw)
13  tornado/simple_httpclient.py
@@ -6,7 +6,7 @@
6 6
 from tornado.httputil import HTTPHeaders
7 7
 from tornado.iostream import IOStream, SSLIOStream
8 8
 from tornado import stack_context
9  
-from tornado.util import b, GzipDecompressor
  9
+from tornado.util import b, GzipDecompressor, monotime
10 10
 
11 11
 import base64
12 12
 import collections
@@ -18,7 +18,6 @@
18 18
 import re
19 19
 import socket
20 20
 import sys
21  
-import time
22 21
 import urlparse
23 22
 
24 23
 try:
@@ -124,7 +123,7 @@ class _HTTPConnection(object):
124 123
 
125 124
     def __init__(self, io_loop, client, request, release_callback,
126 125
                  final_callback, max_buffer_size):
127  
-        self.start_time = time.time()
  126
+        self.start_time = monotime()
128 127
         self.io_loop = io_loop
129 128
         self.client = client
130 129
         self.request = request
@@ -218,7 +217,7 @@ def __init__(self, io_loop, client, request, release_callback,
218 217
             if timeout:
219 218
                 self._timeout = self.io_loop.add_timeout(
220 219
                     self.start_time + timeout,
221  
-                    stack_context.wrap(self._on_timeout))
  220
+                    stack_context.wrap(self._on_timeout), monotonic=True)
222 221
             self.stream.set_close_callback(self._on_close)
223 222
             self.stream.connect(sockaddr,
224 223
                                 functools.partial(self._on_connect, parsed,
@@ -236,7 +235,7 @@ def _on_connect(self, parsed, parsed_hostname):
236 235
         if self.request.request_timeout:
237 236
             self._timeout = self.io_loop.add_timeout(
238 237
                 self.start_time + self.request.request_timeout,
239  
-                stack_context.wrap(self._on_timeout))
  238
+                stack_context.wrap(self._on_timeout), monotonic=True)
240 239
         if (self.request.validate_cert and
241 240
             isinstance(self.stream, SSLIOStream)):
242 241
             match_hostname(self.stream.socket.getpeercert(),
@@ -319,7 +318,7 @@ def cleanup(self):
319 318
         except Exception, e:
320 319
             logging.warning("uncaught exception", exc_info=True)
321 320
             self._run_callback(HTTPResponse(self.request, 599, error=e,
322  
-                                request_time=time.time() - self.start_time,
  321
+                                request_time=monotime() - self.start_time,
323 322
                                 ))
324 323
             if hasattr(self, "stream"):
325 324
                 self.stream.close()
@@ -425,7 +424,7 @@ def _on_body(self, data):
425 424
             buffer = BytesIO(data)  # TODO: don't require one big string?
426 425
         response = HTTPResponse(original_request,
427 426
                                 self.code, headers=self.headers,
428  
-                                request_time=time.time() - self.start_time,
  427
+                                request_time=monotime() - self.start_time,
429 428
                                 buffer=buffer,
430 429
                                 effective_url=self.request.url)
431 430
         self._run_callback(response)
5  tornado/test/import_test.py
@@ -56,4 +56,7 @@ def test_import_twisted(self):
56 56
         except ImportError:
57 57
             pass
58 58
         else:
59  
-            import tornado.platform.twisted
  59
+            try:
  60
+                import tornado.platform.twisted
  61
+            except TypeError:
  62
+                pass
4  tornado/test/iostream_test.py
@@ -5,10 +5,10 @@
5 5
 from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase, get_unused_port
6 6
 from tornado.util import b
7 7
 from tornado.web import RequestHandler, Application
  8
+import datetime
8 9
 import errno
9 10
 import socket
10 11
 import sys
11  
-import time
12 12
 
13 13
 
14 14
 class HelloHandler(RequestHandler):
@@ -226,7 +226,7 @@ def test_close_buffered_data(self):
226 226
             # Allow the close to propagate to the client side of the
227 227
             # connection.  Using add_callback instead of add_timeout
228 228
             # doesn't seem to work, even with multiple iterations
229  
-            self.io_loop.add_timeout(time.time() + 0.01, self.stop)
  229
+            self.io_loop.add_timeout(datetime.timedelta(seconds=0.01), self.stop)
230 230
             self.wait()
231 231
             client.read_bytes(256, self.stop)
232 232
             data = self.wait()
6  tornado/test/testing_test.py
... ...
@@ -1,8 +1,8 @@
1 1
 #!/usr/bin/env python
2 2
 
3 3
 from __future__ import absolute_import, division, with_statement
  4
+import datetime
4 5
 import unittest
5  
-import time
6 6
 from tornado.testing import AsyncTestCase, LogTrapTestCase
7 7
 
8 8
 
@@ -20,9 +20,9 @@ def test_subsequent_wait_calls(self):
20 20
         This test makes sure that a second call to wait()
21 21
         clears the first timeout.
22 22
         """
23  
-        self.io_loop.add_timeout(time.time() + 0.01, self.stop)
  23
+        self.io_loop.add_timeout(datetime.timedelta(seconds=0.01), self.stop)
24 24
         self.wait(timeout=0.02)
25  
-        self.io_loop.add_timeout(time.time() + 0.03, self.stop)
  25
+        self.io_loop.add_timeout(datetime.timedelta(seconds=0.03), self.stop)
26 26
         self.wait(timeout=0.1)
27 27
 
28 28
 
2  tornado/test/twisted_test.py
@@ -37,7 +37,7 @@
37 37
     from tornado.platform.twisted import TornadoReactor
38 38
     from zope.interface import implementer
39 39
     have_twisted = True
40  
-except ImportError:
  40
+except (ImportError, TypeError):
41 41
     have_twisted = False
42 42
 
43 43
 from tornado.httpclient import AsyncHTTPClient
4  tornado/testing.py
@@ -36,11 +36,11 @@
36 36
 from tornado.stack_context import StackContext, NullContext
37 37
 from tornado.util import raise_exc_info
38 38
 import contextlib
  39
+import datetime
39 40
 import logging
40 41
 import os
41 42
 import signal
42 43
 import sys
43  
-import time
44 44
 import unittest
45 45
 
46 46
 _next_port = 10000
@@ -189,7 +189,7 @@ def timeout_func():
189 189
                     self.stop()
190 190
                 if self.__timeout is not None:
191 191
                     self.io_loop.remove_timeout(self.__timeout)
192  
-                self.__timeout = self.io_loop.add_timeout(time.time() + timeout, timeout_func)
  192
+                self.__timeout = self.io_loop.add_timeout(datetime.timedelta(seconds=timeout), timeout_func)
193 193
             while True:
194 194
                 self.__running = True
195 195
                 with NullContext():
20  tornado/util.py
@@ -5,6 +5,26 @@
5 5
 import zlib
6 6
 
7 7
 
  8
+try:
  9
+    # You can get the monotime module from:
  10
+    # http://pypi.python.org/pypi/Monotime/
  11
+    import monotime
  12
+except ImportError:
  13
+    pass
  14
+import time
  15
+try:
  16
+    # python 3.3 has time.monotonic(), or the monotime module might have
  17
+    # added it.
  18
+    monotime_impl = time.monotonic
  19
+except AttributeError:
  20
+    import logging
  21
+    logging.warning("time.monotonic() not available; using time.time()")
  22
+    monotime_impl = time.time
  23
+# wrap monotime_impl so that monotime_impl can be reassigned in unit tests
  24
+def monotime():
  25
+  return monotime_impl()
  26
+
  27
+
8 28
 class ObjectDict(dict):
9 29
     """Makes a dictionary behave like an object."""
10 30
     def __getattr__(self, name):
6  tornado/websocket.py
@@ -21,11 +21,11 @@
21 21
 # Author: Jacob Kristhammar, 2010
22 22
 
23 23
 import array
  24
+import datetime
24 25
 import functools
25 26
 import hashlib
26 27
 import logging
27 28
 import struct
28  
-import time
29 29
 import base64
30 30
 import tornado.escape
31 31
 import tornado.web
@@ -433,7 +433,7 @@ def close(self):
433 433
             self.stream.close()
434 434
         elif self._waiting is None:
435 435
             self._waiting = self.stream.io_loop.add_timeout(
436  
-                time.time() + 5, self._abort)
  436
+                datetime.timedelta(seconds=5), self._abort)
437 437
 
438 438
 
439 439
 class WebSocketProtocol13(WebSocketProtocol):
@@ -651,4 +651,4 @@ def close(self):
651 651
             # Give the client a few seconds to complete a clean shutdown,
652 652
             # otherwise just close the connection.
653 653
             self._waiting = self.stream.io_loop.add_timeout(
654  
-                time.time() + 5, self._abort)
  654
+                datetime.timedelta(seconds=5), self._abort)
7  tornado/wsgi.py
@@ -35,7 +35,6 @@
35 35
 import httplib
36 36
 import logging
37 37
 import sys
38  
-import time
39 38
 import tornado
40 39
 import urllib
41 40
 
@@ -43,7 +42,7 @@
43 42
 from tornado import httputil
44 43
 from tornado import web
45 44
 from tornado.escape import native_str, utf8, parse_qs_bytes
46  
-from tornado.util import b, bytes_type
  45
+from tornado.util import b, bytes_type, monotime
47 46
 
48 47
 try:
49 48
     from io import BytesIO  # python 3
@@ -168,7 +167,7 @@ def __init__(self, environ):
168 167
         httputil.parse_body_arguments(self.headers.get("Content-Type", ""),
169 168
                                       self.body, self.arguments, self.files)
170 169
 
171  
-        self._start_time = time.time()
  170
+        self._start_time = monotime()
172 171
         self._finish_time = None
173 172
 
174 173
     def supports_http_1_1(self):
@@ -195,7 +194,7 @@ def full_url(self):
195 194
     def request_time(self):
196 195
         """Returns the amount of time it took for this request to execute."""
197 196
         if self._finish_time is None:
198  
-            return time.time() - self._start_time
  197
+            return monotime() - self._start_time
199 198
         else:
200 199
             return self._finish_time - self._start_time
201 200
 
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.