-
-
Notifications
You must be signed in to change notification settings - Fork 367
Description
A context variable is a convenient way to track something about the scope in which a task executes. Sometimes that "something" effectively constitutes a promise to child tasks that some resource remains usable. For example, trio_asyncio.open_loop()
provides the asyncio loop via a contextvar, but it's not much use if the async with
block has exited already.
Unfortunately, contextvars are inherited from the environment of the task spawner, not the environment of the nursery in which the task runs. That means they can't reliably provide resources to a task, because the resources might be released before the task finishes. Imagine:
async with trio.open_nursery() as nursery:
async with trio_asyncio.open_loop() as loop:
nursery.start_soon(some_asyncio_task)
It would be nice to have a kind of contextvar that is inherited by child tasks based on the value it had when their nursery was opened, rather than the value it had when the child task was spawned. (In theory, cancellation status could use this if it existed. In practice, we probably want to continue to special-case cancellation status because it's so fundamental.)
Implementation idea: each scoped contextvar uses an underlying regular contextvar whose value is a tuple (scoped contextvar's value, task that set it)
. Also, capture the contextvars context when creating a nursery (this is cheap, since contexts are meant to be shallow-copied all the time). When accessing the value of a scoped contextvar, check whether the underlying regular contextvar was set in the task we're currently in. If so, return the value from the underlying regular contextvar in the current context; otherwise, return its value in our parent nursery's captured context.