This will be a long question, therefore:
TL DR: I have a Python 2.7 threaded network server with a request handler, the call stack looks like this:
WorkerThread -> requestHandler -> func1 -> func2 -> .. -> func10 -> doStuff -> BlockingIO
I want to use Tornado 3.0 IOLoop and change only parts of the server and IO:
(IOLoop) -> requestHandler -> func1 -> func2 -> .. -> func10 -> (doStuff) -> (AsyncIO)
Thus, the entire code stack between requestHandler () and func10 () will not change at all. In fact, even the doStuff () interface will not change, and it will be blocked. However, internally, it will use the AsyncIO object (which is a Tornado coroutine), and during the asynchronous I / O operation, IOLoop to execute other coroutines until the I / O operation completes.
Is it possible?
Now for a practical example:
I have a network server that receives requests and processes them using a thread pool (or a process pool, it doesn't matter how much this example goes):
def main():
Pretty simple, really.
Now, let's say, I decided that I wanted to reorganize my server to use Tornado, using tornado.gen to support asynchronous operations, so it didn't interfere so much with network IO. So this is my new code:
def main():
I know this is a problem in many ways, mainly due to the call stack. When we do something like:
IOLoop.instance().wait_for_future(result)
we have a call stack that looks like this:
IOLoop.main_loop.start() -> handleRequest -> IOLoop.main_loop.wait_for_future() -> other_callbacks..
so that we can (or even possibly) encounter situations such as:
IOLoop.main_loop.start() -> handleRequest -> IOLoop.main_loop.wait_for_future() -> handleRequest -> IOLoop.main_loop.wait_for_future() -> handleRequest -> IOLoop.main_loop.wait_for_future() -> ...
obviously, if handleRequest itself becomes a coroutine, then when it issues it, we donβt have such deep stack problems.
In the embedded system that I once used using an unmanaged scheduler, there was no problem to return control to the scheduler without stack problems. The scheduler will take the execution context and the call stack and save them, then move to another context / stack and continue execution from there. When waiting for events / I / O, the scheduler starts and starts everything that was in the I / O cycle. I want something like this on my system, instead of changing the entire call stack above - converting TOTAL to coroutines.
Does anyone have any tips, any ideas?