Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blocking loop.subprocess_shell() and loop.subprocess_exec() #118

Open
soonum opened this issue Nov 27, 2017 · 18 comments
Open

Blocking loop.subprocess_shell() and loop.subprocess_exec() #118

soonum opened this issue Nov 27, 2017 · 18 comments
Labels

Comments

@soonum
Copy link

@soonum soonum commented Nov 27, 2017

  • uvloop version: 0.9.0
  • Python version: 3.6.1
  • Platform: UNIX
  • Can you reproduce the bug with PYTHONASYNCIODEBUG in env?: n.a.

Hi there,

I'm facing the same problem encountered in CPython issue #414. Since the patch (see issue #428) is not upstreamed in asyncio yet, I was wondering if there is any progess on this in uvloop.
I've tested that behavior with the 0.9.0 version and it seems to be still blocking.

@1st1, have you planned to fix this? Do you need help at some point? (I was working with @Martiusweb on the codebase which led to those issues)

@1st1
Copy link
Member

@1st1 1st1 commented Nov 28, 2017

@1st1, have you planned to fix this?

Yes. Will take a look in a week or two. If you have time to make a PR I'll review it.

@1st1 1st1 added the enhancement label May 23, 2018
@1st1
Copy link
Member

@1st1 1st1 commented May 25, 2018

Actually, uvloop can block only if you supply a preexec_fn argument to loop.subprocess_shell() or loop.subprocess_exec(). Could you please share your test?

@jlaine
Copy link
Contributor

@jlaine jlaine commented Mar 16, 2019

@soonum could you please share your test as requested by @1st1 ?

@soonum
Copy link
Author

@soonum soonum commented Mar 18, 2019

Sorry I wasn't following up this old issue.
In fact my use case is that one, I provide a preexec_fn argument to these methods and as a result, the loop is blocking.

@pvizeli
Copy link

@pvizeli pvizeli commented Mar 22, 2019

We have a component in Home Assistant; they call ffmpeg with a subprocess (async). They also end sometimes (less and more) in blocking and not working event loop :( I can't provide any test code because it's not all time simple to reproduce it in a laboratory situation.

The point is, we don't use preexec_fn and end in a blocking loop :(

@pvizeli
Copy link

@pvizeli pvizeli commented Apr 25, 2019

The problem is forking. It looks like the current implementation doesn't work on a heavy load of the main process.

@Martiusweb
Copy link

@Martiusweb Martiusweb commented Apr 26, 2019

@pvizeli
Copy link

@pvizeli pvizeli commented Apr 30, 2019

The point is, if we use subprocess inside executors, it work without issues. Also call the normal subprocess inside loop work with attach a streamreader to output. But the subprocess from uvloop hungs

@nicx
Copy link

@nicx nicx commented Jun 9, 2019

any news to this? the HA community is waiting for a solution wistfully ;)

@nicx
Copy link

@nicx nicx commented Aug 6, 2019

Hm very sad that its not going on here :( I would love using the HA stream component...
@1st1 any plan about this?

@1st1
Copy link
Member

@1st1 1st1 commented Oct 22, 2019

@1st1 any plan about this?

I still haven't received any clear answer to #118 (comment).

I know that uvloop is blocking when preexec_fn is used, but almost nobody uses it. Other than that, as @Martiusweb explained in his comment #118 (comment) it should not block.

@pvizeli

But the subprocess from uvloop hungs

Does uvloop behave differently from asyncio in your case?

@pvizeli
Copy link

@pvizeli pvizeli commented Oct 22, 2019

@1st1 yes. The reason why we remove it from Home Assistant. However, #268 fix the problem and we can reuse it after that is merged.

@1st1
Copy link
Member

@1st1 1st1 commented Oct 22, 2019

Great, I'm working on a release right now, and will likely merge #268 or a variation of it.

@soonum
Copy link
Author

@soonum soonum commented Dec 16, 2019

  • uvloop version: 0.14.0

Hi there,

I got some details after encountering another blocking case. I found that if you provide a value for the keyword argument cwd and experiencing a slow filesystem at runtime, the call to loop.subprocess_[exec|shell]() would block.

Here is a simple test to reproduce the behavior. It's relying on slowfs.

  1. create a directory /home_slow
  2. run the following command <path_to>/slowfs/env/bin/slowfs -c <path_to>/slowfs/slowfs/slowfs.cfg --allow-other /home /home_slow/
  3. in another shell, run the python (tested with Python v3.8.0) example subprocess_test.py

You should see that Python is blocking during 5 seconds. This is related to the delay enforced by slowfs.

Here is the content of subprocess_test.py:

import asyncio
import datetime
import subprocess
import uvloop

async def subprocess_task():
    await asyncio.sleep(2)
    process = await asyncio.create_subprocess_shell("touch a_file.txt" , cwd="/home_slow/",) 
    await process.wait()
    print("Process terminated with code", process.returncode, flush=True)


async def print_task():
    """
    Print seconds elapsed since beginning of the program at 1Hz.
    """
    start_time = datetime.datetime.now()
    while True:
        now = datetime.datetime.now()
        print(round((now - start_time).total_seconds()), flush=True)
        await asyncio.sleep(1)


if __name__ == "__main__":
    asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
    loop = asyncio.get_event_loop()
    task_1 = loop.create_task(print_task())
    task_2 = loop.create_task(subprocess_task())
    
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        task_1.cancel()
        task_2.cancel()
        loop.close()

Here's the config used in slowfs.cfg:

access = 5
chmod = 0
chown = 0
getattr = 5
readdir = 5
readlink = 0
mknod = 0
rmdir = 0
mkdir = 0
statfs = 5
unlink = 0
symlink = 0
rename = 0
link = 0
utimens = 0
open = 0
create = 0
read = 0
write = 0
truncate = 0
flush = 0
release = 0
fsync = 0

By the way, #268 doesn't solve the issue regarding using a preexec function. The way I tested it is by defining a function that does a simple time.sleep(5) to see the loop blocking.

Hope that could help to slove this issue.

@madkote
Copy link

@madkote madkote commented Mar 31, 2020

hi,

would be concurrent.futures import ProcessPoolExecutor a workaround?

@1st1
Copy link
Member

@1st1 1st1 commented Apr 1, 2020

Here is a simple test to reproduce the behavior. It's relying on slowfs.

Nice, that's great! Thanks for the slowfs tip, I didn't know about it before.

would be concurrent.futures import ProcessPoolExecutor a workaround?

Unfortunately no, they would still block.

@madkote
Copy link

@madkote madkote commented Apr 1, 2020

@1st1 thanks for reply.
So the fix would be at the moment to make subprocess async -> run subprocess in thread (with some async flavour).

`would be concurrent.futures import ProcessPoolExecutor a workaround?

Unfortunately no, they would still block.`

@madkote
Copy link

@madkote madkote commented Apr 1, 2020

@1st1 here is possible fix

def run_subprocess(cmd, loop=None):
    loop = loop or asyncio.get_event_loop()
    try:
        process = await asyncio.create_subprocess_shell(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    except NotImplementedError:
        with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) as process:
            try:
                result = await loop.run_in_executor(None, process.communicate)
            except Exception:  # muh pycodestyle
                def kill():
                    process.kill()
                    process.wait()

                await loop.run_in_executor(None, kill)
                raise

    else:
        result = await process.communicate()

    return [res.decode('utf-8') for res in result] ```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
7 participants
You can’t perform that action at this time.