The wait_for_exit method eventually calls os.waitpid, which throws a ChildProcessError if no process with the specified pid exists. This exception is caught, the function just returns, and the Future never resolves.
if __name__ == '__main__':
import asyncio
from tornado.process import Subprocess
async def f():
p = Subprocess("ls")
p.proc.wait()
return await p.wait_for_exit()
loop = asyncio.get_event_loop()
loop.run_until_complete(f())
Instead, the process could be retrieved from the _waiting dict and the return code could be accessed from the object directly.