aio-libs / aioftp

ftp client/server for asyncio (http://aioftp.readthedocs.org)
Apache License 2.0
185 stars 54 forks source link

Deadlock upon calling `await client.abort()` #163

Open michaelwillis opened 1 year ago

michaelwillis commented 1 year ago

I'm trying to implement abort download as described in Advanced download and upload, abort, restart. Following the example, I find that await client.abort() deadlocks, but if my code first calls stream.close(), then it can call await client.abort() without deadlock. Are these two lines mixed up in the example?

pohmelie commented 1 year ago

How your code looks like? Did you read the warning part? https://aioftp.readthedocs.io/client_tutorial.html#id2

michaelwillis commented 1 year ago

Yes, I read the warning and tried to follow it. Here is my code, which is working now that it calls stream.close() and await client.abort() in reverse order from that recommended by the warning:

async with aioftp.Client.context(HOST, user=USER, password=PASSWORD) as client:
    # See note about how to abort streaming download without causing deadlock:
    # https://aioftp.readthedocs.io/client_tutorial.html#advanced-download-and-upload-abort-restart
    stream = await client.download_stream(filename)
    completed = False
    try:
        # stream_process_lines() returns false if download should be aborted
        completed = await stream_process_lines(stream)
        if completed:
            await stream.finish()
    finally:
        # Need to abort download if either stream_process_lines() returned False or an exception was raised
        if not completed:
            stream.close()
            await client.abort()
pohmelie commented 1 year ago

Can you provide some log messages? So I can release where stuck is.

michaelwillis commented 1 year ago

Hmm, I rewrote the code as follows and it gives me almost no problems:

try:
    async with aioftp.Client.context(HOST, user=USER, password=PASSWORD) as client:
        async with client.download_stream(filename) as stream:
            await stream_process_lines(stream)
except Exception as e:
    log.error("Error processing file: %s, %s" % filename, e)

This code always exits, never deadlocks. If stream_process_lines(stream) either raises an exception or returns early without consuming the entire stream, then the end of the log statement says Waiting for ('2xx',) but got 450 [' Transfer aborted forcefully by client.'], which seems fine to me.

pohmelie commented 1 year ago

But issue exists. Maybe you can replicate the issue and provide some logs. Since I still have no clue about reasons for such behavior.