rmarques
rmarques

Reputation: 91

Tornado periodiccallback invocation is not skipped if callback takes longer then callback_time

Consider the following code:

from tornado import ioloop, gen

@gen.coroutine
def test_callback():
    print 'entering test_callback'
    yield gen.sleep(10)
    print 'exiting test_callback'


if __name__ == '__main__':
    ioloop.PeriodicCallback(test_callback, 1000 * 5).start()
    ioloop.IOLoop.current().start()

This is the output:

entering test_callback
entering test_callback
entering test_callback
exiting test_callback
entering test_callback
exiting test_callback
entering test_callback
...

The documentation says that if the callback takes longer then callback_time subsequent invocations will be skipped. But in this case this is not happening. What can I do to make sure that the callback is only called after the previous callback invocation finishes?

Upvotes: 0

Views: 821

Answers (2)

Ahmad Yoosofan
Ahmad Yoosofan

Reputation: 981

The following solution based on python 3.5 can be used too.

from tornado import ioloop, httpclient
class testCls:
  def __init__(self):
    self.n=0
    self.inProcess=False
  async def f1(self):
    url2='http://yoosofan.github.io/en/'
    http_client1 = httpclient.AsyncHTTPClient()
    response = await http_client1.fetch(url2)
    print('dd: ',self.n)
    self.n +=1
  async def my_function(self):
    print('z',end=' ')
    if self.inProcess==False:
    self.inProcess=True
    await self.f1()
    self.inProcess=False
if __name__ == '__main__':
  t1=testCls()
  ioloop.PeriodicCallback(t1.my_function, 60).start()
  ioloop.IOLoop.current().start()

Upvotes: 0

rmarques
rmarques

Reputation: 91

I found the solution on this thread.

Basically if a callback takes more then callback_time to execute subsequent invocations are only skipped if the callback is synchronous. If the callback calls another asynchronous routine PeriodicCallback has no way of knowing all will start the another invocation of the callback.

In this case we need an async-aware equivalent of PeriodicCallback:

from tornado import ioloop, gen

@gen.coroutine
def test_callback():
    print 'entering test_callback'
    yield gen.sleep(10)
    print 'exiting test_callback'

@gen.coroutine
def periodic():
    loop = ioloop.IOLoop.current()
    while True:
        start = loop.time()
        yield test_callback()
        duration = loop.time() - start
        yield gen.Task(loop.add_timeout, max(5 - duration, 0))

if __name__ == '__main__':
    ioloop.IOLoop.current().add_callback(periodic)
    ioloop.IOLoop.current().start()

This is the output:

entering test_callback
exiting test_callback
entering test_callback
exiting test_callback
entering test_callback
exiting test_callback
...

Upvotes: 1

Related Questions