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

check for tasks before cancelling them #533

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

Randelung
Copy link
Contributor

Fixes #337.
Have a look at the changes and see if you want the None tasks to not trigger a log warning or not.

asyncua/client/ua_client.py Outdated Show resolved Hide resolved
asyncua/client/ua_client.py Outdated Show resolved Hide resolved
asyncua/client/ua_client.py Outdated Show resolved Hide resolved
asyncua/client/ua_client.py Outdated Show resolved Hide resolved
asyncua/client/client.py Outdated Show resolved Hide resolved
asyncua/client/ua_client.py Outdated Show resolved Hide resolved
asyncua/client/ua_client.py Outdated Show resolved Hide resolved
Comment on lines +375 to +384
try:
result = state is None and client.uaclient.protocol is None or \
client.uaclient.protocol.state is state
if result:
break
except AttributeError: # protocol doesn't exist yet (connection attempt ongoing), but target is not None
pass
await sleep(SLEEP)
assert client.uaclient.protocol.state == state
assert result

Copy link
Contributor

@ZuZuD ZuZuD Apr 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not changing just this bit?

if (
    client.uaclient.protocol == state or 
    client.uaclient.protocol and client.uaclient.protocol.state == state
):

nit: is compares object and == compares values, better being explicit here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method gives the test several tries before asserting a False, so that if after 3 seconds the test comes out positive it'll return immediately instead of waiting the full timeout. In order to not evaluate the expression twice (also DRY) I used a storage variable.
The reason for is instead of == is that the arguments in the function are enums, so either UaProtocol.INITIALIZED/OPEN/CLOSED or None. IMO any other value would be wrong and shouldn't be accepted.

except asyncio.CancelledError:
pass
finally:
self._connect_task = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we end up here, we have a connect coroutine running. What's the use-case where we need to set self._connect_task = None again?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we end up here, a connect task is running and being awaited inside the try block just above. Another task doing something else might call disconnect and cancel the connect task, resulting in a CancelledError. In any case, the _connect_task is either cancelled or done after the await inside the try block, which is why resetting it to None signals that it's not running.
It's not explicitly necessary, but returning to initial state means you only have to worry about "there's a task and it's running or there's no task" and not check if the task is done or cancelled or whatever when disconnecting.

except asyncio.CancelledError:
pass
finally:
self._connect_task = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why putting the connection in the background then waiting for it?? It does not make any sense

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that disconnect can cancel the task. The connect behavior is supposed to be blocking (to preserve the behavior it has so far), but disconnect should be able to cancel the process. Therefore, a task is needed to be cancellable and awaiting it keeps the process blocking.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in theory in a normal applicaiton you call await connect()then await disconnect() An application calling disconnect() while doing calling connect() is doing something quite unusual and should handle its own shit itself. They will anyway do a much better job than we do at low level

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

¯\_(ツ)_/¯ the use case was given by ZuZuD. Without it, checking for an existing protocol before trying to disonnect it would be sufficient.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never looked at his code. But for sure he should have his own tasks and can cancel them. It does need even more tasks inside his tasks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't follow. It was the first code review for this PR and the reason why a simple check for client.protocol was insufficient.
If you don't think it's necessary I'll revert it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in theory in a normal applicaiton you call await connect()then await disconnect() An application calling disconnect() while doing calling connect() is doing something quite unusual and should handle its own shit itself. They will anyway do a much better job than we do at low level

I can't agree more with this and that's why we chose to deal with this at the HA level.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Randelung sorry I did not follow and do not have the capacity to folow everything. So I just check PR when I got 5 minutes and comment what I see. I do not know the history but clearly a wrapper making task of a method and just awaiting it ooks very strnage and you must have a very clear use case for such a thing. It does not seem to fit the current API

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, no worries, I know how it is.
How do we continue? I think at this point it's more of a design choice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

cannot stop client if no connection established
3 participants