Skip to content

Replace tornado.locks with asyncio for Events/Locks/Conditions/Semaphore - #3397

Merged
jrbourbeau merged 18 commits into
dask:masterfrom
mrocklin:asyncio-locks
Feb 3, 2020
Merged

Replace tornado.locks with asyncio for Events/Locks/Conditions/Semaphore#3397
jrbourbeau merged 18 commits into
dask:masterfrom
mrocklin:asyncio-locks

Conversation

@mrocklin

Copy link
Copy Markdown
Member

Fixes #3395

This is a work in progress. Things are broken. I thought I'd put it up here for now though.

@mrocklin

Copy link
Copy Markdown
Member Author

OK, this is running now. cc @jrbourbeau for review (also maybe @jcrist)

A couple of points:

  1. asyncio.Condition objects had issues in Python 3.6, so there are a couple of places where I had to avoid async context managers
  2. Windows timing is about 10% off on timeouts it seems. This isn't super surprising, Python/Windows sub-second timing is poor in other ways as well. I'm inclined to ignore this and leave it for upstream rather than fix it here for now.

@jrbourbeau jrbourbeau left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Comment thread distributed/client.py
self._loop_runner = LoopRunner(loop=loop, asynchronous=asynchronous)
self.loop = self._loop_runner.loop

self._gather_semaphore = asyncio.Semaphore(5, loop=self.loop.asyncio_loop)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is specifying s loop= parameter required for using asyncio.Semaphore here? I ask because we weren't passing a loop in before and doing is has been depreciated starting in Python 3.8

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This object is created by the user outside of the event loop. The semaphore will grab the event loop of the current thread, which when we're operating in normal synchronous mode, will not be the same event loop as the one that the Client is running on. So here we have to be explicit and specify the event loop that we want to use.

This usually doesn't come up because we create these objects within async functions or functions that are only being called from within the event loop. The client code can get strange in this way due to the two threads that are active (user/jupyter/ipython thread and the event loop thread)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see, thanks for the explaination @mrocklin.

Since Semaphore, Condition, etc. will attach to the current event loop, could temporarily switch the current loop to be the Client's self.loop.asyncio_loop, create the Semaphore, and then switch the current loop back to what it was before? (Not 100% sure this wouldn't have other consequences, just proposing as a possible option).

Alternatively, I'm happy to keep this as is and open a separate issue for working around the loop= keyword depreciation

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah, I missed the statement in your original message that this was being deprecated. The clean way to do this is to to move this to some __await__ definition within this class and make sure that we only create the Semaphore in an async function. I can do this, but I wouldn't mind it being in another PR if that's easy.

@jrbourbeau jrbourbeau Feb 3, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Gotcha, that does sound cleaner. A separate PR for that would be appreciated

Comment thread distributed/client.py
Comment thread distributed/client.py Outdated
Comment thread distributed/client.py
self.lock = threading.Lock()
self.loop = loop or default_client().loop
self.condition = Condition()
self.condition = asyncio.Condition(loop=self.loop.asyncio_loop)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same question about the loop= parameter

Comment thread distributed/pubsub.py
self.name = name
self.buffer = deque()
self.condition = tornado.locks.Condition()
self.condition = asyncio.Condition(loop=self.loop.asyncio_loop)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same question about the loop= parameter

@jrbourbeau jrbourbeau left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The only remaining tornado lock objects being used are a couple of tornado.locks.Events in the BatchedSend class in distributed/batched.py. Everything else looks good to merge

@mrocklin

mrocklin commented Feb 3, 2020

Copy link
Copy Markdown
Member Author

Batched.py is a bit strange. We're using gen.coroutine there in order to avoid leaving lingering asyncio coroutines around that cause warnings on shutdown. I'm sort of ignoring that file for now.

@jrbourbeau jrbourbeau left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Okay, good to know. Then in that case, this is good to go. Merging, thanks @mrocklin!

@jrbourbeau
jrbourbeau merged commit 7c9da10 into dask:master Feb 3, 2020
@mrocklin
mrocklin deleted the asyncio-locks branch February 3, 2020 17:25
bnaul pushed a commit to replicahq/distributed that referenced this pull request Feb 10, 2020
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.

Replace tornado Event/Condition/Semaphore with asyncio equivalents

2 participants