asyncio internals
Saúl Ibarra Corretgé
@saghul
PyGrunn 2014
Friday, May 9, 14
Intro
New asynchronous I/O framework for Python
PEP-3156
Python >= 3.3 (backport available: Trollius)
Uses new language features: yield from
Designed to interoperate with other frameworks
You went to Rodrigo’s talk earlier today, right?
Friday, May 9, 14
Friday, May 9, 14
Architecture
Event loop
Coroutines, Futures and Tasks
Transports, Protocols and Streams
I’ll cover these
Homework!
Friday, May 9, 14
Event Loop
Friday, May 9, 14
Calculate
poll time
Poll
Run
callbacks
Friday, May 9, 14
There is no abstraction for an “event”
It runs callbacks which are put in a queue
Callbacks can be scheduled due to i/o, time or user
desire
The event loop acts as an implicit scheduler
Friday, May 9, 14
Sim
plified
def call_soon(self, callback, *args):
handle = events.Handle(callback, args, self)
self._ready.append(handle)
return handle
Friday, May 9, 14
events.Handle is like a “callbak wrapper”
The ready queue is a deque
Once per loop iteration al handles in the ready queue
are executed
Friday, May 9, 14
def call_later(self, delay, callback, *args):
return self.call_at(self.time() + delay, callback, *args)
def call_at(self, when, callback, *args):
timer = events.TimerHandle(when, callback, args, self)
heapq.heappush(self._scheduled, timer)
return timer
Sim
plified
Friday, May 9, 14
Timers are stored in a heap (loop._scheduled)
TimerHandle subclasses Handle, but stores the time
when it’s due and has comparison methods for keeping
the heap sorted by due time
Friday, May 9, 14
ntodo = len(self._ready)
for i in range(ntodo):
handle = self._ready.popleft()
if not handle._cancelled:
handle._run()
handle = None # break cycles
Friday, May 9, 14
This is the single place where the ready queue is
iterated over
A thread-safe iteration method is used, since other
threads could modify the ready queue (see
call_soon_threadsafe)
If any handles are scheduled while the ready queue is
being processed, they will be run on the next loop
iteration
Friday, May 9, 14
Different polling mechanisms on Unix: select, poll, epoll,
kqueue, devpoll
Windows is a completely different beast
Different paradigms: readyness vs completion
APIs are provided for both
I/O handling
Friday, May 9, 14
I/O handling APIs
Readyness style
add_reader/add_writer
remove_reader/remove_writer
Completion style
sock_recv/sock_sendall
sock_connect/sock_accept
Friday, May 9, 14
import selectors
New module in Python 3.4
Consistent interface to Unix polling mechanisms
On Windows it uses select()
64 file descriptors default* limit - WEBSCALE!
IOCP is the way to go, but has a different API
Caveat emptor: doesn’t work for file i/o
Friday, May 9, 14
Sim
plified
def add_reader(self, fd, callback, *args):
handle = events.Handle(callback, args, self)
try:
key = self._selector.get_key(fd)
except KeyError:
self._selector.register(fd, selectors.EVENT_READ, (handle, None))
else:
mask, (reader, writer) = key.events, key.data
self._selector.modify(fd, mask | selectors.EVENT_READ, (handle, writer))
if reader is not None:
reader.cancel()
Friday, May 9, 14
The selector key stores the fd, events and user
provided arbitrary data
In this case the arbitrary data is the reader, writer
handle tuple
Only one reader and writer per fd are allowed
Friday, May 9, 14
1.Calculate timeout
2.Block for I/O
3.Process I/O events: schedule callbacks
4.Process timers: schedule callbacks
5.Run pending callbacks
Polling for I/O
Friday, May 9, 14
timeout = None
if self._ready:
timeout = 0
elif self._scheduled:
# Compute the desired timeout.
when = self._scheduled[0]._when
deadline = max(0, when - self.time())
if timeout is None:
timeout = deadline
else:
timeout = min(timeout, deadline)
event_list = self._selector.select(timeout)
self._process_events(event_list)
end_time = self.time()
while self._scheduled:
handle = self._scheduled[0]
if handle._when >= end_time:
break
handle = heapq.heappop(self._scheduled)
self._ready.append(handle)
# run all handles in the ready queue...
Sim
plified
Friday, May 9, 14
If timeout is None an infinite poll is performed
_process_events puts the read / write handles in the
ready queue, if applicable
Friday, May 9, 14
def call_soon_threadsafe(self, callback, *args):
handle = self._call_soon(callback, args)
self._write_to_self()
return handle
Sim
plified
Friday, May 9, 14
The event loop has the read end of a socketpair added
to the selector
When _write_to_self is called the loop will be “waken
up” from the select/poll/epoll_wait/kevent syscall
Friday, May 9, 14
Coroutines, Futures & Tasks
Friday, May 9, 14
Generator functions, can also receive values
Use the @asyncio.coroutine decorator
Does extra checks in debug mode
Serves as documentation
Chain them with yield from
Coroutines
Friday, May 9, 14
Not actually PEP-3148 (concurrent.futures)
API almost identical
Represent a value which is not there yet
yield from can be used to wait for it!
asyncio.wrap_future can be used to wrap a PEP-3148
Future into one of these
Futures
Friday, May 9, 14
f = Future()
Usually a future will be the result of a function
f.set_result / f.set_exception
Someone will set the result eventually
yield from f
Wait until the result arrives
add_done_callback / remove_done_callback
Callback based interface
Friday, May 9, 14
def set_result(self, result):
if self._state != _PENDING:
raise InvalidStateError('{}: {!r}'.format(self._state, self))
self._result = result
self._state = _FINISHED
self._schedule_callbacks()
def _schedule_callbacks(self):
callbacks = self._callbacks[:]
if not callbacks:
return
self._callbacks[:] = []
for callback in callbacks:
self._loop.call_soon(callback, self)
Friday, May 9, 14
After the result or exception is set all callbacks added
with Future.add_done_callback are called
Note how callbacks are scheduled in the event loop
using call_soon
Friday, May 9, 14
Sim
plified
def sock_connect(self, sock, address):
fut = futures.Future(loop=self)
self._sock_connect(fut, False, sock, address)
return fut
def _sock_connect(self, fut, registered, sock, address):
fd = sock.fileno()
if registered:
self.remove_writer(fd)
if fut.cancelled():
return
try:
if not registered:
sock.connect(address)
else:
err = sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
if err != 0:
raise OSError(err, 'Connect call failed %s' % (address,))
except (BlockingIOError, InterruptedError):
self.add_writer(fd, self._sock_connect, fut, True, sock, address)
except Exception as exc:
fut.set_exception(exc)
else:
fut.set_result(None)
Friday, May 9, 14
Not a coroutine, but we can wait on it using yield from
because it returns a Future
The Uncallback Pattern (TM)
Hey, look at those nice exceptions: BlockingIOError,
InterruptedError
Much nicer than checking if errno is
EWOULDBLOCK or EINTR
Friday, May 9, 14
def run_until_complete(self, future):
future = tasks.async(future, loop=self)
future.add_done_callback(_raise_stop_error)
self.run_forever()
future.remove_done_callback(_raise_stop_error)
if not future.done():
raise RuntimeError('Event loop stopped before Future completed.')
return future.result()
Friday, May 9, 14
Loop.run_forever will run the loop until Loop.stop is
called
_raise_stop_error is an implementation detail, it causes
an exception to bubble up and makes run_forever
return
Friday, May 9, 14
def __iter__(self):
if not self.done():
self._blocking = True
yield self # This tells Task to wait for completion.
assert self.done(), "yield from wasn't used with future"
return self.result() # May raise too.
Friday, May 9, 14
Returning a value from __iter__ is the same as raising
StopIteration(value)
The _blocking flag is used to check if yield future was
used intead of yield from future
Task has a way to wait on a Future if yielded to it, also
checks that yield from was used (_blocking flag)
Friday, May 9, 14
Friday, May 9, 14
Unit of concurrent asynchronous work
It’s actually a coroutine wrapped in a Future
Magic!
Schedules callbacks using loop.call_soon
Use asyncio.async to run a coroutine in a Task
Tasks
Friday, May 9, 14
import asyncio
@asyncio.coroutine
def f(n, x):
while True:
print(n)
yield from asyncio.sleep(x)
loop = asyncio.get_event_loop()
asyncio.async(f('f1', 0.5))
asyncio.async(f('f2', 1.5))
loop.run_forever()
Friday, May 9, 14
Both coroutines will run concurrently
asyncio.async returns a Task if a coroutine was passed,
or the unchanged value if a Future was passed
Go and check how asyncio.sleep is implemented, it’s
really simple!
Friday, May 9, 14
def __init__(self, coro, *, loop=None):
assert iscoroutine(coro), repr(coro) # Not a coroutine function!
super().__init__(loop=loop)
self._coro = iter(coro) # Use the iterator just in case.
self._fut_waiter = None
self._loop.call_soon(self._step)
Sim
plified
Friday, May 9, 14
Tasks are not run immediately, the actual work is done
by Task._step, which is scheduled with loop.call_soon
_fut_waiter is used to store a Future which this Task is
waiting for
Friday, May 9, 14
Sim
plified
def _step(self, value=None, exc=None):
assert not self.done(), '_step(): already done'
coro = self._coro
self._fut_waiter = None
try:
if exc is not None:
result = coro.throw(exc)
elif value is not None:
result = coro.send(value)
else:
result = next(coro)
except StopIteration as exc:
self.set_result(exc.value)
except Exception as exc:
self.set_exception(exc)
except BaseException as exc:
self.set_exception(exc)
raise
else:
if isinstance(result, futures.Future):
# Yielded Future must come from Future.__iter__().
if result._blocking:
result._blocking = False
result.add_done_callback(self._wakeup)
self._fut_waiter = result
else:
# error...
elif result is None:
# Bare yield relinquishes control for one event loop iteration.
self._loop.call_soon(self._step)
else:
# error...
Friday, May 9, 14
The Magic (TM)
The coroutine is stepped over until it finishes
Note the check of _blocking to verify yield vs yield from
usage
The _wakeup function will schedule _step with either a
result or an exception
At any point in time, either _step is scheduled or
_fut_waiter is not None
Friday, May 9, 14
There is a lot more in asyncio
Go read PEP-3156
Don’t be afraid of looking under the hood
Don’t rely on internals, they are implementation details
Join the mailing list, check the third party libraries!
raise SystemExit
“I hear and I forget. I see and I remember.
I do and I understand.” - Confucius
Friday, May 9, 14
Questions?
bettercallsaghul.com
@saghul
Friday, May 9, 14

More Related Content

PPTX
Process synchronization in Operating Systems
PPTX
Introduction to Apache ZooKeeper
KEY
Introduction to memcached
PDF
Apache ZooKeeper
PDF
Introduction and Overview of Apache Kafka, TriHUG July 23, 2013
PPT
Chapter 8 distributed file systems
PDF
Concurrency vs parallelism
PPTX
HBase Low Latency
Process synchronization in Operating Systems
Introduction to Apache ZooKeeper
Introduction to memcached
Apache ZooKeeper
Introduction and Overview of Apache Kafka, TriHUG July 23, 2013
Chapter 8 distributed file systems
Concurrency vs parallelism
HBase Low Latency

What's hot (20)

PDF
Git training v10
PPT
Multithreading models
PPT
Process synchronization(deepa)
PPTX
Semaphore
PPTX
Memory Management
PDF
Monitors
PPTX
Concurrency
PDF
Building robust CDC pipeline with Apache Hudi and Debezium
PDF
Java 8 Workshop
PDF
Unit II - 3 - Operating System - Process Synchronization
PPTX
Introduction to Apache Kafka
PDF
CS9222 ADVANCED OPERATING SYSTEMS
PPTX
Git in 10 minutes
DOCX
Locking base concurrency control
PPTX
Tuning Apache Kafka Connectors for Flink.pptx
ODP
Distributed File System
 
PPTX
Process synchronization in operating system
PPTX
Threads in Operating System | Multithreading | Interprocess Communication
PPTX
Evening out the uneven: dealing with skew in Flink
PDF
Introducing the Apache Flink Kubernetes Operator
Git training v10
Multithreading models
Process synchronization(deepa)
Semaphore
Memory Management
Monitors
Concurrency
Building robust CDC pipeline with Apache Hudi and Debezium
Java 8 Workshop
Unit II - 3 - Operating System - Process Synchronization
Introduction to Apache Kafka
CS9222 ADVANCED OPERATING SYSTEMS
Git in 10 minutes
Locking base concurrency control
Tuning Apache Kafka Connectors for Flink.pptx
Distributed File System
 
Process synchronization in operating system
Threads in Operating System | Multithreading | Interprocess Communication
Evening out the uneven: dealing with skew in Flink
Introducing the Apache Flink Kubernetes Operator
Ad

Viewers also liked (20)

PDF
Introduction to asyncio
PDF
Logging in Python for large applications
PDF
A Curious Course on Coroutines and Concurrency
PDF
HSGR
PDF
Il lavoro delle donne nel settore turismo
PDF
Bai tap cnc_0516
TXT
Playrlic
PPT
Evaluation question 2
PDF
WiscNet 2016 Strategic Plan
PDF
Start Your Website for Free!
PPTX
Nbhtc pure
PPTX
Welcome to central web power point oct 2014
PDF
AppZero & GoGrid: Moving Windows Server Apps to Cloud in 3 Easy Steps
PDF
Certificación Red Hat
PDF
Culturetoute N1
PPTX
Fraud-on-the-Market Theory: Significant Issues and Updates for 2014 and Beyon...
PPTX
Supranational Collaboration in Agricultural Research in Sub-Saharan Africa
PPTX
PDF
2014 Anti-Corruption Public Procurement guide
PDF
Garcinia cambogia: nuovo estratto miracolose per perdere peso velocemente. Tu...
Introduction to asyncio
Logging in Python for large applications
A Curious Course on Coroutines and Concurrency
HSGR
Il lavoro delle donne nel settore turismo
Bai tap cnc_0516
Playrlic
Evaluation question 2
WiscNet 2016 Strategic Plan
Start Your Website for Free!
Nbhtc pure
Welcome to central web power point oct 2014
AppZero & GoGrid: Moving Windows Server Apps to Cloud in 3 Easy Steps
Certificación Red Hat
Culturetoute N1
Fraud-on-the-Market Theory: Significant Issues and Updates for 2014 and Beyon...
Supranational Collaboration in Agricultural Research in Sub-Saharan Africa
2014 Anti-Corruption Public Procurement guide
Garcinia cambogia: nuovo estratto miracolose per perdere peso velocemente. Tu...
Ad

Similar to asyncio internals (20)

PDF
Python, do you even async?
PDF
A deep dive into PEP-3156 and the new asyncio module
PDF
The journey of asyncio adoption in instagram
PDF
PyCon Canada 2019 - Introduction to Asynchronous Programming
PPTX
Introducing to Asynchronous Programming
PPTX
Async programming and python
PDF
PDF
Tornado in Depth
PDF
Python Async IO Horizon
PDF
Introduction to Python Asyncio
PDF
Codemania101: The Present, Past and Future of Asynchronous Programming in Python
PDF
Syncing up with Python’s asyncio for (micro) service development, Joir-dan Gumbs
PDF
Asynchronous Python A Gentle Introduction
PDF
BUILDING APPS WITH ASYNCIO
PDF
Concurrency in Python
PDF
The future of async i/o in Python
PDF
Python Coroutines, Present and Future
KEY
Do more than one thing at the same time, the Python way
PDF
Python3.6 and asynchronous programming
PDF
How do event loops work in Python?
Python, do you even async?
A deep dive into PEP-3156 and the new asyncio module
The journey of asyncio adoption in instagram
PyCon Canada 2019 - Introduction to Asynchronous Programming
Introducing to Asynchronous Programming
Async programming and python
Tornado in Depth
Python Async IO Horizon
Introduction to Python Asyncio
Codemania101: The Present, Past and Future of Asynchronous Programming in Python
Syncing up with Python’s asyncio for (micro) service development, Joir-dan Gumbs
Asynchronous Python A Gentle Introduction
BUILDING APPS WITH ASYNCIO
Concurrency in Python
The future of async i/o in Python
Python Coroutines, Present and Future
Do more than one thing at the same time, the Python way
Python3.6 and asynchronous programming
How do event loops work in Python?

More from Saúl Ibarra Corretgé (20)

PDF
JanusCon 2024: Mom there are robots in my meeting
PDF
Challenges running Jitsi Meet at scale during the pandemic
PDF
The Road to End-to-End Encryption in Jitsi Meet
PDF
Jitsi: State of the Union 2020
PDF
Jitsi Meet: our tale of blood, sweat, tears and love
PDF
Jitsi Meet: Video conferencing for the privacy minded
PDF
Jitsi - Estado de la unión 2019
PDF
Get a room! Spot: the ultimate physical meeting room experience
PDF
Going Mobile with React Native and WebRTC
PDF
Going Mobile with React Native and WebRTC
PDF
Jitsi: Estado de la Unión (2018)
PDF
Jitsi: state-of-the-art video conferencing you can self-host
PDF
WebRTC: El epicentro de la videoconferencia y IoT
PDF
Jitsi: Open Source Video Conferencing
PDF
Jitsi: State of the Union
PDF
libuv: cross platform asynchronous i/o
PDF
Videoconferencias: el santo grial de WebRTC
PDF
SylkServer: State of the art RTC application server
PDF
Escalabilidad horizontal desde las trincheras
PDF
A deep dive into libuv
JanusCon 2024: Mom there are robots in my meeting
Challenges running Jitsi Meet at scale during the pandemic
The Road to End-to-End Encryption in Jitsi Meet
Jitsi: State of the Union 2020
Jitsi Meet: our tale of blood, sweat, tears and love
Jitsi Meet: Video conferencing for the privacy minded
Jitsi - Estado de la unión 2019
Get a room! Spot: the ultimate physical meeting room experience
Going Mobile with React Native and WebRTC
Going Mobile with React Native and WebRTC
Jitsi: Estado de la Unión (2018)
Jitsi: state-of-the-art video conferencing you can self-host
WebRTC: El epicentro de la videoconferencia y IoT
Jitsi: Open Source Video Conferencing
Jitsi: State of the Union
libuv: cross platform asynchronous i/o
Videoconferencias: el santo grial de WebRTC
SylkServer: State of the art RTC application server
Escalabilidad horizontal desde las trincheras
A deep dive into libuv

Recently uploaded (20)

PDF
AI.gov: A Trojan Horse in the Age of Artificial Intelligence
PDF
Co-training pseudo-labeling for text classification with support vector machi...
PDF
NewMind AI Journal Monthly Chronicles - August 2025
PDF
Altius execution marketplace concept.pdf
PDF
EIS-Webinar-Regulated-Industries-2025-08.pdf
PDF
The-Future-of-Automotive-Quality-is-Here-AI-Driven-Engineering.pdf
PDF
Data Virtualization in Action: Scaling APIs and Apps with FME
PDF
IT-ITes Industry bjjbnkmkhkhknbmhkhmjhjkhj
PDF
Streamline Vulnerability Management From Minimal Images to SBOMs
PPTX
Report in SIP_Distance_Learning_Technology_Impact.pptx
PDF
CXOs-Are-you-still-doing-manual-DevOps-in-the-age-of-AI.pdf
PPTX
Rise of the Digital Control Grid Zeee Media and Hope and Tivon FTWProject.com
PDF
NewMind AI Weekly Chronicles – August ’25 Week IV
PDF
The-2025-Engineering-Revolution-AI-Quality-and-DevOps-Convergence.pdf
PDF
Decision Optimization - From Theory to Practice
PDF
Transform-Your-Factory-with-AI-Driven-Quality-Engineering.pdf
PDF
Examining Bias in AI Generated News Content.pdf
PPTX
How to Convert Tickets Into Sales Opportunity in Odoo 18
PDF
giants, standing on the shoulders of - by Daniel Stenberg
PDF
CCUS-as-the-Missing-Link-to-Net-Zero_AksCurious.pdf
AI.gov: A Trojan Horse in the Age of Artificial Intelligence
Co-training pseudo-labeling for text classification with support vector machi...
NewMind AI Journal Monthly Chronicles - August 2025
Altius execution marketplace concept.pdf
EIS-Webinar-Regulated-Industries-2025-08.pdf
The-Future-of-Automotive-Quality-is-Here-AI-Driven-Engineering.pdf
Data Virtualization in Action: Scaling APIs and Apps with FME
IT-ITes Industry bjjbnkmkhkhknbmhkhmjhjkhj
Streamline Vulnerability Management From Minimal Images to SBOMs
Report in SIP_Distance_Learning_Technology_Impact.pptx
CXOs-Are-you-still-doing-manual-DevOps-in-the-age-of-AI.pdf
Rise of the Digital Control Grid Zeee Media and Hope and Tivon FTWProject.com
NewMind AI Weekly Chronicles – August ’25 Week IV
The-2025-Engineering-Revolution-AI-Quality-and-DevOps-Convergence.pdf
Decision Optimization - From Theory to Practice
Transform-Your-Factory-with-AI-Driven-Quality-Engineering.pdf
Examining Bias in AI Generated News Content.pdf
How to Convert Tickets Into Sales Opportunity in Odoo 18
giants, standing on the shoulders of - by Daniel Stenberg
CCUS-as-the-Missing-Link-to-Net-Zero_AksCurious.pdf

asyncio internals

  • 1. asyncio internals Saúl Ibarra Corretgé @saghul PyGrunn 2014 Friday, May 9, 14
  • 2. Intro New asynchronous I/O framework for Python PEP-3156 Python >= 3.3 (backport available: Trollius) Uses new language features: yield from Designed to interoperate with other frameworks You went to Rodrigo’s talk earlier today, right? Friday, May 9, 14
  • 4. Architecture Event loop Coroutines, Futures and Tasks Transports, Protocols and Streams I’ll cover these Homework! Friday, May 9, 14
  • 7. There is no abstraction for an “event” It runs callbacks which are put in a queue Callbacks can be scheduled due to i/o, time or user desire The event loop acts as an implicit scheduler Friday, May 9, 14
  • 8. Sim plified def call_soon(self, callback, *args): handle = events.Handle(callback, args, self) self._ready.append(handle) return handle Friday, May 9, 14
  • 9. events.Handle is like a “callbak wrapper” The ready queue is a deque Once per loop iteration al handles in the ready queue are executed Friday, May 9, 14
  • 10. def call_later(self, delay, callback, *args): return self.call_at(self.time() + delay, callback, *args) def call_at(self, when, callback, *args): timer = events.TimerHandle(when, callback, args, self) heapq.heappush(self._scheduled, timer) return timer Sim plified Friday, May 9, 14
  • 11. Timers are stored in a heap (loop._scheduled) TimerHandle subclasses Handle, but stores the time when it’s due and has comparison methods for keeping the heap sorted by due time Friday, May 9, 14
  • 12. ntodo = len(self._ready) for i in range(ntodo): handle = self._ready.popleft() if not handle._cancelled: handle._run() handle = None # break cycles Friday, May 9, 14
  • 13. This is the single place where the ready queue is iterated over A thread-safe iteration method is used, since other threads could modify the ready queue (see call_soon_threadsafe) If any handles are scheduled while the ready queue is being processed, they will be run on the next loop iteration Friday, May 9, 14
  • 14. Different polling mechanisms on Unix: select, poll, epoll, kqueue, devpoll Windows is a completely different beast Different paradigms: readyness vs completion APIs are provided for both I/O handling Friday, May 9, 14
  • 15. I/O handling APIs Readyness style add_reader/add_writer remove_reader/remove_writer Completion style sock_recv/sock_sendall sock_connect/sock_accept Friday, May 9, 14
  • 16. import selectors New module in Python 3.4 Consistent interface to Unix polling mechanisms On Windows it uses select() 64 file descriptors default* limit - WEBSCALE! IOCP is the way to go, but has a different API Caveat emptor: doesn’t work for file i/o Friday, May 9, 14
  • 17. Sim plified def add_reader(self, fd, callback, *args): handle = events.Handle(callback, args, self) try: key = self._selector.get_key(fd) except KeyError: self._selector.register(fd, selectors.EVENT_READ, (handle, None)) else: mask, (reader, writer) = key.events, key.data self._selector.modify(fd, mask | selectors.EVENT_READ, (handle, writer)) if reader is not None: reader.cancel() Friday, May 9, 14
  • 18. The selector key stores the fd, events and user provided arbitrary data In this case the arbitrary data is the reader, writer handle tuple Only one reader and writer per fd are allowed Friday, May 9, 14
  • 19. 1.Calculate timeout 2.Block for I/O 3.Process I/O events: schedule callbacks 4.Process timers: schedule callbacks 5.Run pending callbacks Polling for I/O Friday, May 9, 14
  • 20. timeout = None if self._ready: timeout = 0 elif self._scheduled: # Compute the desired timeout. when = self._scheduled[0]._when deadline = max(0, when - self.time()) if timeout is None: timeout = deadline else: timeout = min(timeout, deadline) event_list = self._selector.select(timeout) self._process_events(event_list) end_time = self.time() while self._scheduled: handle = self._scheduled[0] if handle._when >= end_time: break handle = heapq.heappop(self._scheduled) self._ready.append(handle) # run all handles in the ready queue... Sim plified Friday, May 9, 14
  • 21. If timeout is None an infinite poll is performed _process_events puts the read / write handles in the ready queue, if applicable Friday, May 9, 14
  • 22. def call_soon_threadsafe(self, callback, *args): handle = self._call_soon(callback, args) self._write_to_self() return handle Sim plified Friday, May 9, 14
  • 23. The event loop has the read end of a socketpair added to the selector When _write_to_self is called the loop will be “waken up” from the select/poll/epoll_wait/kevent syscall Friday, May 9, 14
  • 24. Coroutines, Futures & Tasks Friday, May 9, 14
  • 25. Generator functions, can also receive values Use the @asyncio.coroutine decorator Does extra checks in debug mode Serves as documentation Chain them with yield from Coroutines Friday, May 9, 14
  • 26. Not actually PEP-3148 (concurrent.futures) API almost identical Represent a value which is not there yet yield from can be used to wait for it! asyncio.wrap_future can be used to wrap a PEP-3148 Future into one of these Futures Friday, May 9, 14
  • 27. f = Future() Usually a future will be the result of a function f.set_result / f.set_exception Someone will set the result eventually yield from f Wait until the result arrives add_done_callback / remove_done_callback Callback based interface Friday, May 9, 14
  • 28. def set_result(self, result): if self._state != _PENDING: raise InvalidStateError('{}: {!r}'.format(self._state, self)) self._result = result self._state = _FINISHED self._schedule_callbacks() def _schedule_callbacks(self): callbacks = self._callbacks[:] if not callbacks: return self._callbacks[:] = [] for callback in callbacks: self._loop.call_soon(callback, self) Friday, May 9, 14
  • 29. After the result or exception is set all callbacks added with Future.add_done_callback are called Note how callbacks are scheduled in the event loop using call_soon Friday, May 9, 14
  • 30. Sim plified def sock_connect(self, sock, address): fut = futures.Future(loop=self) self._sock_connect(fut, False, sock, address) return fut def _sock_connect(self, fut, registered, sock, address): fd = sock.fileno() if registered: self.remove_writer(fd) if fut.cancelled(): return try: if not registered: sock.connect(address) else: err = sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) if err != 0: raise OSError(err, 'Connect call failed %s' % (address,)) except (BlockingIOError, InterruptedError): self.add_writer(fd, self._sock_connect, fut, True, sock, address) except Exception as exc: fut.set_exception(exc) else: fut.set_result(None) Friday, May 9, 14
  • 31. Not a coroutine, but we can wait on it using yield from because it returns a Future The Uncallback Pattern (TM) Hey, look at those nice exceptions: BlockingIOError, InterruptedError Much nicer than checking if errno is EWOULDBLOCK or EINTR Friday, May 9, 14
  • 32. def run_until_complete(self, future): future = tasks.async(future, loop=self) future.add_done_callback(_raise_stop_error) self.run_forever() future.remove_done_callback(_raise_stop_error) if not future.done(): raise RuntimeError('Event loop stopped before Future completed.') return future.result() Friday, May 9, 14
  • 33. Loop.run_forever will run the loop until Loop.stop is called _raise_stop_error is an implementation detail, it causes an exception to bubble up and makes run_forever return Friday, May 9, 14
  • 34. def __iter__(self): if not self.done(): self._blocking = True yield self # This tells Task to wait for completion. assert self.done(), "yield from wasn't used with future" return self.result() # May raise too. Friday, May 9, 14
  • 35. Returning a value from __iter__ is the same as raising StopIteration(value) The _blocking flag is used to check if yield future was used intead of yield from future Task has a way to wait on a Future if yielded to it, also checks that yield from was used (_blocking flag) Friday, May 9, 14
  • 37. Unit of concurrent asynchronous work It’s actually a coroutine wrapped in a Future Magic! Schedules callbacks using loop.call_soon Use asyncio.async to run a coroutine in a Task Tasks Friday, May 9, 14
  • 38. import asyncio @asyncio.coroutine def f(n, x): while True: print(n) yield from asyncio.sleep(x) loop = asyncio.get_event_loop() asyncio.async(f('f1', 0.5)) asyncio.async(f('f2', 1.5)) loop.run_forever() Friday, May 9, 14
  • 39. Both coroutines will run concurrently asyncio.async returns a Task if a coroutine was passed, or the unchanged value if a Future was passed Go and check how asyncio.sleep is implemented, it’s really simple! Friday, May 9, 14
  • 40. def __init__(self, coro, *, loop=None): assert iscoroutine(coro), repr(coro) # Not a coroutine function! super().__init__(loop=loop) self._coro = iter(coro) # Use the iterator just in case. self._fut_waiter = None self._loop.call_soon(self._step) Sim plified Friday, May 9, 14
  • 41. Tasks are not run immediately, the actual work is done by Task._step, which is scheduled with loop.call_soon _fut_waiter is used to store a Future which this Task is waiting for Friday, May 9, 14
  • 42. Sim plified def _step(self, value=None, exc=None): assert not self.done(), '_step(): already done' coro = self._coro self._fut_waiter = None try: if exc is not None: result = coro.throw(exc) elif value is not None: result = coro.send(value) else: result = next(coro) except StopIteration as exc: self.set_result(exc.value) except Exception as exc: self.set_exception(exc) except BaseException as exc: self.set_exception(exc) raise else: if isinstance(result, futures.Future): # Yielded Future must come from Future.__iter__(). if result._blocking: result._blocking = False result.add_done_callback(self._wakeup) self._fut_waiter = result else: # error... elif result is None: # Bare yield relinquishes control for one event loop iteration. self._loop.call_soon(self._step) else: # error... Friday, May 9, 14
  • 43. The Magic (TM) The coroutine is stepped over until it finishes Note the check of _blocking to verify yield vs yield from usage The _wakeup function will schedule _step with either a result or an exception At any point in time, either _step is scheduled or _fut_waiter is not None Friday, May 9, 14
  • 44. There is a lot more in asyncio Go read PEP-3156 Don’t be afraid of looking under the hood Don’t rely on internals, they are implementation details Join the mailing list, check the third party libraries! raise SystemExit “I hear and I forget. I see and I remember. I do and I understand.” - Confucius Friday, May 9, 14