# \[Concurrency\]\[Pitch\] Task Local Values

**URL:** <https://forums.swift.org/t/concurrency-pitch-task-local-values/42829>\
**Category:** Pitches\
**Tags:** concurrency\
**Created:** [December 15, 2020, 3:58am UTC](https://forums.swift.org/t/concurrency-pitch-task-local-values/42829 "2020-12-15T03:58:06Z")\
**Posts on this page:** 10\
**Page:** 3

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [January 31, 2021, 3:29am UTC](https://forums.swift.org/t/concurrency-pitch-task-local-values/42829/42 "2021-01-31T03:29:38Z")

</div>

Small status update: I received some excellent feedback off-thread and will be preparing a Pitch #2 soon.

It will extend the semantics to also cover inheritance of values with detached tasks, although with very carefully specialized semantics to avoid infinitely growing task local bound value stacks. It'll help with end-to-end tracing even in presence of detached tasks.

It also means we pretty definitely do need the `Key` type, as we'll need to special mark some keys for storage in "always inherit" limited-space storage.

Thanks for the input and cya in Pitch #2 soon

---

<div class="post-metadata">

**Author:** ![Chris\_Lattner3](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/chris_lattner3/32/348_2.png) [@Chris\_Lattner3](https://forums.swift.org/u/Chris_Lattner3)\
**Post date:** [February 1, 2021, 1:42am UTC](https://forums.swift.org/t/concurrency-pitch-task-local-values/42829/43 "2021-02-01T01:42:18Z")

</div>

> [@ktoso](#):
>
> Back in December when this was implemented and pitched I had my hands tied somewhat by being unable to implement this given the restrictive state of the Task APIs. Access to tasks is currently implemented as "magical extra parameter to async functions," making it impossible to implement accessing a task from a sync function. It is indeed a huge, feature-breaking even, limitation...
> 
> Thankfully, during Pitch #2 of Structure Concurrency in the last weeks ([this design](https://forums.swift.org/t/pitch-2-structured-concurrency/43452/116)) we were able to hash out the missing and misaligned APIs which resulted in: [https://github.com/DougGregor/swift-evolution/pull/48](https://github.com/DougGregor/swift-evolution/pull/48) which will be Pitch #3 of those APIs.
> 
> This implies changes in ABI/calling-convention how a Task is passed around, so it's still not implementable until that is changed, but at least the team seems to be agreeing on adding `Task.unsafeCurrent` so finally we can express task locals within synchronous functions and this proposal becomes wholly useful then 🥳

Fantastic, I'm thrilled to hear this @ktoso!

> [@ktoso](#):
>
> Indeed, `Task.local(\.x) ` becomes non-async then 👍

:happydance: Thank you!!

> [@ktoso](#):
>
> > While actors are out of scope for this proposal, it should also be possible to get a pointer to the current actor (or nil if not on one) from sync code as well for the same reason.  
> > ..  
> > +1 overall, but let's keep this separate from this proposal. I do suspect though we'll end up having such thing. Probably through the Task.current APIs 🤔

Yep of course, it isn't related to this proposal.

> [@ktoso](#):
>
> That's a specific request from @John_McCall -- this isn't really about the task local values but about the general existence of an "give me a handle with full access to everything, even APIs which should never be called from a _different_ task". We can get into the details of that `Unsafe...` name and type in the Pitch #3 of Structured Concurrency which should be posted this week I hope.
> 
> The actual unsafety of it comes not really from the `Task.unsafeCurrent` but the fact someone could escape/store this value and then use it from a different task, that's the unsafeness we're concerned about. John wanted to eventually make a type that's "un-movable" which then could be safe, since we'd prevent people from doing this. `ConcurrentValue` things don't really solve this -- people could store it in some property and then touch the same property, get that `UnsafeCurrentTask` from a different task and poke at it unsafely.

Ok, I'd love to know more about this. It just seems odd and would be best to keep these things orthogonal. The word "unsafe" has a lot of precedent in Swift, and keeping that consistent would be good.

> [@ktoso](#):
>
> Will do, by now the `ConcurrentValue ` is fleshed out enough and looking good that this seems like a good time to refer to it. I agree that restricting values stored in task locals to `ConcurrentValue` is a good idea 👍

Thx! Also, no worries about self-deprecating humor. I didn't mean anything negative, just trying to help make the writing clear and productive for when it gets out to a wider audience in full review.

* * *

> [@ktoso](#):
>
> It is the "binds" that must be _structured_ . The reason it can't be "just a setter" you kind of said yourself -- it would not be structured.

I don't understand though: why is this desirable? What does "structured" mean in this context?

> [@ktoso](#):
>
> As usual... with enough compiler magic

I'm not arguing for more compiler magic :-) , I'm just trying to understand the model and use-case. People will think about these as "thread local" values or "task local" values, and such concepts have wide precedent in ptherads, GCD, etc. These are based on getter/setter styles of APIs. Why deviate? I think I'm just missing an important aspect of your design goals here.

> [@ktoso](#):
>
> And the only way to restore correctness here, is to force every single function that ever touches any task local to also store the previous value (causes copying, and retain/releases), and restore/reset it as it returns. This is exactly what current day, un-structured runtimes have to do. Most of them have to weave code into functions to actually get it right, which is error prone, goes wrong often and is just incredibly accidental complexity.

Ok, I can see why you want scoped mutation so you can restore if this is a goal, but "why is this a goal of the design"? It isn't clear to me at all that this is a better design than a simple getter/setter design. Such a design makes it much more complicated and just seems odd - what is the precedent and rationale here?

> [@ktoso](#):
>
> Thanks for the input and cya in Pitch #2 soon

Thank you for all the hard work on this!

-Chris

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [February 1, 2021, 1:58am UTC](https://forums.swift.org/t/concurrency-pitch-task-local-values/42829/44 "2021-02-01T01:58:27Z")

</div>

> [@Chris\_Lattner3](#):
>
> Ok, I'd love to know more about this. It just seems odd and would be best to keep these things orthogonal. The word "unsafe" has a lot of precedent in Swift, and keeping that consistent would be good.

Yeah ok, let’s revisit this in the Structured Concucrrency Pitch update (meh, pending, should have been out but slipped) as that’s where we propose that API. John can chime in a bit more then about his preference on the naming there... It is “concurrency unsafe” though I understand that normally in Swift “unsafe” most often meant “memory unsafe”, but then again... Swift never before talked about anything wrt. concurrency, so that’s a _first_.

> [@Chris\_Lattner3](#):
>
> > [@ktoso](#):
> >
> > And the only way to restore correctness here, is to force every single function that ever touches any task local to also store the previous value (causes copying, and retain/releases), and restore/reset it as it returns. This is exactly what current day, un-structured runtimes have to do. Most of them have to weave code into functions to actually get it right, which is error prone, goes wrong often and is just incredibly accidental complexity.
> 
> Ok, I can see why you want scoped mutation so you can restore if this is a goal, but "why is this a goal of the design"? It isn't clear to me at all that this is a better design than a simple getter/setter design. Such a design makes it much more complicated and just seems odd - what is the precedent and rationale here?

The primary use-case of these APIs is tracing, there’s two primary use cases (which are actually the same):

- “Instruments style” use cases which need to “for this call, attach some identifier, and carry it through all calls, async/sync and even detached tasks made from this function — such that profilers can “this call, caused all this (a)synchronous work to happen.”
  - subsequent calls (one line below that call) should not have the same identifier, so we need the “restore” / “reset” operation

- distributed tracing — pick any specification you like, but they all need “reset” to implement correctly and efficiently. We (SSWG) have [https://opentelemetry.io/](https://opentelemetry.io/) compatible tracer implementations already, but through painfully passing around the metadata around explicitly.
  - the concept is exactly the same here as the “local” tracing/profiling, but we can extend the traces through multiple machines. Imagine getting a trace from device through all services it is hitting in backend services — a tremendously useful performance analysis tool.

Both those are something we’re actively interested in solving awesomely. Though it will some more time to get the tracers, profilers to make use of this.

—

> [@Chris\_Lattner3](#):
>
> Ok, I can see why you want scoped mutation so you can restore if this is a goal, but "why is this a goal of the design"? It isn't clear to me at all that this is a better design than a simple getter/setter design. Such a design makes it much more complicated and just seems odd - what is the precedent and rationale here?

There’s a lot of prior art on this, I link to [three in the proposal itself](https://github.com/ktoso/swift-evolution/blob/wip-tasklocals/proposals/nnnn-task-locals.md#prior-art) — how Go’s context’s form such “tree”, Kotlin’s scopes, and Loom’s Scope Variables.

The best one to check out is Loom’s Scope Variables mostly because they have the best “in single place” complete rationale writeup about the pains they’re addressing: [State of Loom: Part 2](https://cr.openjdk.java.net/~rpressler/loom/loom/sol1_part2.html#scope-variables) The ability to solve issues with thread locals is served really well by embracing structured concurrency, and as Swift wants to do this anyway — structured task locals are solving another easy to get wrong thing while we’re at “fixing” concurrency to make it simpler to work with.

I’ll add some more rationale on this to the Motivation section too.

---

<div class="post-metadata">

**Author:** ![Lantua](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lantua/32/8868_2.png) [@Lantua](https://forums.swift.org/u/Lantua)\
**Post date:** [February 1, 2021, 2:55pm UTC](https://forums.swift.org/t/concurrency-pitch-task-local-values/42829/45 "2021-02-01T14:55:03Z")

</div>

> [@ktoso](#):
>
> It is “concurrency unsafe” though I understand that normally in Swift “unsafe” most often meant “memory unsafe”, but then again... Swift never before talked about anything wrt. concurrency, so that’s a _first_ .

There's also `UnsafeContinuation`, which which I think is worth mentioning as it's already in review (and is already past the review period).

---

<div class="post-metadata">

**Author:** ![kirilltitov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/kirilltitov/32/4182_2.png) [@kirilltitov](https://forums.swift.org/u/kirilltitov)\
**Post date:** [March 24, 2021, 8:21am UTC](https://forums.swift.org/t/concurrency-pitch-task-local-values/42829/46 "2021-03-24T08:21:40Z")

</div>

What is the status of this one? I presume, it relies on structured concurrency proposal (and we're waiting for core team conclusion on that one).

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [March 24, 2021, 8:23am UTC](https://forums.swift.org/t/concurrency-pitch-task-local-values/42829/47 "2021-03-24T08:23:01Z")

</div>

Correct, it’s waiting for Structured Concurrency to conclude. It will be up for review once that is cleared up. We’re trying to have a focused review of Structured Concurrency and Actors right now, and not adding another topic to focus on concurrently.

Not much has changed here since, the up-to-date wording remains in: [https://github.com/apple/swift-evolution/pull/1245](https://github.com/apple/swift-evolution/pull/1245) (fixed link)

If you want to give it a go it is available in recent toolchains under the experimental concurrency flag.

---

<div class="post-metadata">

**Author:** ![kirilltitov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/kirilltitov/32/4182_2.png) [@kirilltitov](https://forums.swift.org/u/kirilltitov)\
**Post date:** [March 24, 2021, 11:14am UTC](https://forums.swift.org/t/concurrency-pitch-task-local-values/42829/48 "2021-03-24T11:14:43Z")

</div>

Yep, I've been using it for some time already :) I'm just afraid the API might change (or the whole feature gets rejected, whereas it is actually critical — currently I have to pass event loop/context struct to every call, while I could just take it from the Task.local).

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [March 24, 2021, 12:29pm UTC](https://forums.swift.org/t/concurrency-pitch-task-local-values/42829/49 "2021-03-24T12:29:34Z")

</div>

Very glad to hear you’re using it already!

Yeah it is fairly critical for tracing systems and other instrumentation. I had some mixed feelings about passing the event loop with it, but perhaps it’s fine... I would not recommend designing a library around that idea I think though 🤔 (specifically for the event loop passing around I mean).

I can’t say much before the review but I do think this is important enough that it’ll make it in some shape or form. The usual caveats remain — this is under an experimental flag and may change at any moment after all 🙂

Please make sure to chime in the review to voice the feedback for it once it is going to be under review — we’ll ping here then of course as well too. Thanks!

---

<div class="post-metadata">

**Author:** ![Lantua](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lantua/32/8868_2.png) [@Lantua](https://forums.swift.org/u/Lantua)\
**Post date:** [March 24, 2021, 12:37pm UTC](https://forums.swift.org/t/concurrency-pitch-task-local-values/42829/50 "2021-03-24T12:37:05Z")

</div>

> [@ktoso](#):
>
> and not adding another topic to focus on concurrently.

I see what you did there 😏

---

<div class="post-metadata">

**Author:** ![johnburkey](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/johnburkey/32/20566_2.png) [@johnburkey](https://forums.swift.org/u/johnburkey)\
**Post date:** [April 25, 2022, 6:13pm UTC](https://forums.swift.org/t/concurrency-pitch-task-local-values/42829/51 "2022-04-25T18:13:38Z")

</div>

Given that these task local values are only available inside the task, can this lead to a place where the NonAtomic ARC implementation is used for ref counting ? It would be nice to mark a whole society of instances as task local and this seems like a conduit to that semantic.

Once it’s non atomic, you could also replace malloc with something for this case and wipe out most of the cost of ARC + malloc for task internal allocations. (Which is most allocations in any sensible design ;-))

Yeah me again ….

[Previous page](https://forums.swift.org/t/concurrency-pitch-task-local-values/42829.md?page=2)
