Lab 06: Async Internals

Time: 60 minutes | Level: Architect | Docker: docker run -it --rm python:3.11-slim bash

Overview

Understanding asyncio's internals — the event loop, selectors, Future scheduling, and contextvars — lets you write high-performance async code and debug concurrency issues systematically.

Step 1: The Event Loop Internals

import asyncio
import selectors

# Get the current event loop
loop = asyncio.new_event_loop()
print(f"Event loop type: {type(loop).__name__}")
print(f"Selector: {type(loop._selector).__name__}")

# _ready queue: callbacks to execute in next iteration
print(f"Ready queue: {loop._ready}")

# _scheduled: future callbacks (heapq by time)
print(f"Scheduled queue: {loop._scheduled}")

loop.close()

💡 asyncio on Linux uses EpollSelector, macOS uses KqueueSelector, and Windows uses SelectSelector. All implement the same selectors.BaseSelector interface.

Step 2: selectors — I/O Multiplexing

Step 3: asyncio.Future Internals

Step 4: Task Scheduling and Execution Order

📸 Verified Output:

💡 ContextVar is essential for request-scoped state in async servers. Unlike threading.local, it works correctly across coroutines: each task gets its own context copy.

Step 5: loop.call_soon vs loop.call_later vs loop.call_at

Step 6: Async Generators

Step 7: contextvars.ContextVar — Request-Scoped State

Step 8: Capstone — Mini Event Loop

Implement a simplified event loop to understand the mechanism:

📸 Verified Output (contextvars):

Summary

Concept
API
Use Case

Event loop

asyncio.get_event_loop()

Core I/O scheduler

Selectors

selectors.DefaultSelector

OS-level I/O polling

Future

loop.create_future()

Promise-like value holder

Task scheduling

call_soon/call_later/call_at

Deferred execution

Async generators

async def + yield

Streaming data pipelines

ContextVar

set/get/reset

Request-scoped state

copy_context()

Context isolation

Run in different context

Mini event loop

Custom _run_once

Understand asyncio internals

Last updated