<p>In some languages, <code>async</code>/<code>await</code> can be simply transformed into callbacks, as if it were syntactical sugar:</p>
<pre class="lang-js prettyprint-override"><code>async function foo() {
  bar();
  const x = await baz();
  return qux(x.y);
}
// equivalent to:
function foo() {
  bar();
  return baz().then(x =&gt; qux(x.y));
}
</code></pre>
<p>However, this isn't necessarily the case. For example, Swift has no equivalent of the <code>.then</code> method, and asynchronous calls inherit the context they're called from (priority, executing actor if any, etc) unless explicitly run <code>detached</code>. It's not clear to me how this would be implemented, since task-local variables are not thread-local -- <code>await</code> may switch threads but does not switch tasks, and one task may spawn several parallel child tasks. The documentation on async/await is reminiscent of coroutines, using phrases like &quot;yielding execution&quot;, but it doesn't get into the implementation details (as most developers don't need to know them).</p>
<p>Here's an example of task-local variables in Swift:</p>
<pre class="lang-swift prettyprint-override"><code>class Foo {
  @TaskLocal static var thing = 0
  static func printThing(_ label: String) { print(label, thing, separator: &quot;: &quot;) }
}

Task {
  Foo.$thing.withValue(1) {
    Foo.printThing(&quot;Start of first task&quot;)

    Task {
      Foo.printThing(&quot;Start of first task's child&quot;)
      try! await Task.sleep(nanoseconds: 10_000_000)
      Foo.printThing(&quot;End of first task's child&quot;)
    }

    Task.detached {
      Foo.printThing(&quot;Detached task spawned in first task&quot;)
    }
  }

  try! await Task.sleep(nanoseconds: 5_000_000)

  Foo.$thing.withValue(2) {
    Foo.printThing(&quot;End of first task&quot;)
  }
}

Task {
  Foo.$thing.withValue(3) {
    Foo.printThing(&quot;Second task&quot;)
  }
}

/* possible output:
Start of first task: 1
Second task: 3
Detached task spawned in first task: 0
Start of first task's child: 1
End of first task: 2
End of first task's child: 1
*/
</code></pre>
<p>The task-local variable is a <code>static</code> variable on <code>class Foo</code>, and that I never reference the task instances after spawning them. The values follow the assignment order in source, not the execution order.</p>
<p>How could thread-agnostic (and indeed thread-safe), task-local variables be implemented?</p>

<implementation><async>
How can task-local variables be implemented?
CC BY-SA 4.0
2023-06-28T05:04:06.740Z
I don’t entirely understand your question. You say `async`/`await` can be desugared, which is true, but surely that answers your question as-stated. The fact that Swift doesn’t expose a `then` method in its API doesn’t fundamentally mean it couldn’t implement things that way internally. If your question is really about what Swift does specifically, I think it would be helpful to say that in your question, or at least elaborate on what features Swift offers that you think are incompatible with that desugaring.
Alexis King
CC BY-SA 4.0
2023-06-28T11:52:22.570Z
@AlexisKing I’ve seen it mentioned that certain features such as `async let` — which spawn a child task without the overhead of a full-fledged `TaskGroup` — require additional runtime machinery. Regardless, even if I *can* replace `await` with some runtime method that isn’t exposed to the end user, it’s still not clear to me how to implement task-local variables, since those can live anywhere (just with the `@TaskLocal` property wrapper).
Bbrk24
CC BY-SA 4.0
2023-06-28T14:54:51.823Z
I don’t understand what you mean by “can live anywhere”. I’m not intimately familiar with the way Swift implements `async`/`await`, which is what you really seem to be asking about, but every task necessarily has state associated with it, whether that means something like closures or captured stack frames. The variables are stored there. What do you think is the *obstacle* to storing them?
Alexis King
CC BY-SA 4.0
2023-06-28T16:20:02.467Z
[Here](https://swiftfiddle.com/6tpbx4arnjhw3c6r477kgovg3u) is an example of what I mean. Notice that the `@TaskLocal` variable lives on `class Foo`, not the instance of the task.
Bbrk24
CC BY-SA 4.0
2023-06-28T16:45:51.873Z
Could you edit this example into your question?
Alexis King
CC BY-SA 4.0
2023-06-28T16:52:44.043Z
@AlexisKing edited
Bbrk24
<p>Task-local variables can be implemented in essentially the same way as thread-local variables, which have two standard implementation strategies:</p>
<ol>
<li><p>Every thread-local variable is really a weak map from threads to values. When the value is accessed, the current thread ID is used to index into the map and look up the current thread’s value.</p>
</li>
<li><p>Every thread-local variable is really just a unique key, and it doesn’t “contain” anything at all. Every thread context then contains a map from these keys to values. Accessing a thread-local variable just looks up the value in the current thread’s map using the variable’s key.</p>
</li>
</ol>
<p>This second strategy is more commonly chosen when the implementor has control over the language and runtime, as it has nicer characteristics:</p>
<ul>
<li><p>Since the values are stored in the thread context itself, they are automatically deallocated when the thread is destroyed.</p>
</li>
<li><p>There is no need for any synchronization because each thread context has its own distinct map, and no state is actually stored in the thread-local variables themselves.</p>
</li>
</ul>
<p>There are many optimizations to the basic technique that allow thread-local storage to be made more efficient. For example, <a href="https://chao-tic.github.io/blog/2018/12/25/tls" rel="noreferrer">this article</a> discusses how thread-local storage is implemented on Linux; it is quite extensive. But the basic idea remains the same: thread-local variables are keys into a per-thread mapping.</p>
<p>This strategy can be easily adapted to tasks. All that is required is that tasks have some well-defined notion of task identity (so it’s clear what it means for code to execute within “the same task”) and that there is some way of obtaining a reference to the currently-executing task. Swift provides this via the <a href="https://developer.apple.com/documentation/swift/withunsafecurrenttask(body:)" rel="noreferrer"><code>withUnsafeCurrentTask(body:)</code></a> function, which <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0311-task-locals.md" rel="noreferrer">the proposal</a> notes is used in the implementation of task-local storage.</p>

CC BY-SA 4.0
2023-06-28T17:30:40.460Z
2023-06-28T17:30:40.460Z
Alexis King
# How can task-local variables be implemented?
Posted by: username_0 on 2023-06-23
In some languages, async/await can be simply transformed into callbacks, as if it were syntactical sugar:
async function foo() {
  bar();
  const x = await baz();
  return qux(x.y);
}
// equivalent to:
function foo() {
  bar();
  return baz().then(x => qux(x.y));
}

However, this isn't necessarily the case. For example, Swift has no equivalent of the .then method, and asynchronous calls inherit the context they're called from (priority, executing actor if any, etc) unless explicitly run detached. It's not clear to me how this would be implemented, since task-local variables are not thread-local -- await may switch threads but does not switch tasks, and one task may spawn several parallel child tasks. The documentation on async/await is reminiscent of coroutines, using phrases like "yielding execution", but it doesn't get into the implementation details (as most developers don't need to know them).
Here's an example of task-local variables in Swift:
class Foo {
  @TaskLocal static var thing = 0
  static func printThing(_ label: String) { print(label, thing, separator: ": ") }
}

Task {
  Foo.$thing.withValue(1) {
    Foo.printThing("Start of first task")

    Task {
      Foo.printThing("Start of first task's child")
      try! await Task.sleep(nanoseconds: 10_000_000)
      Foo.printThing("End of first task's child")
    }

    Task.detached {
      Foo.printThing("Detached task spawned in first task")
    }
  }

  try! await Task.sleep(nanoseconds: 5_000_000)

  Foo.$thing.withValue(2) {
    Foo.printThing("End of first task")
  }
}

Task {
  Foo.$thing.withValue(3) {
    Foo.printThing("Second task")
  }
}

/* possible output:
Start of first task: 1
Second task: 3
Detached task spawned in first task: 0
Start of first task's child: 1
End of first task: 2
End of first task's child: 1
*/

The task-local variable is a static variable on class Foo, and that I never reference the task instances after spawning them. The values follow the assignment order in source, not the execution order.
How could thread-agnostic (and indeed thread-safe), task-local variables be implemented?
**Tags:** implementation async
> **Comments:**
> **username_1**: I don’t entirely understand your question. You say `async`/`await` can be desugared, which is true, but surely that answers your question as-stated. The fact that Swift doesn’t expose a `then` method in its API doesn’t fundamentally mean it couldn’t implement things that way internally. If your question is really about what Swift does specifically, I think it would be helpful to say that in your question, or at least elaborate on what features Swift offers that you think are incompatible with that desugaring.  
> **username_0**: @AlexisKing I’ve seen it mentioned that certain features such as `async let` — which spawn a child task without the overhead of a full-fledged `TaskGroup` — require additional runtime machinery. Regardless, even if I *can* replace `await` with some runtime method that isn’t exposed to the end user, it’s still not clear to me how to implement task-local variables, since those can live anywhere (just with the `@TaskLocal` property wrapper).  
> **username_1**: I don’t understand what you mean by “can live anywhere”. I’m not intimately familiar with the way Swift implements `async`/`await`, which is what you really seem to be asking about, but every task necessarily has state associated with it, whether that means something like closures or captured stack frames. The variables are stored there. What do you think is the *obstacle* to storing them?  
> **username_0**: [Here](https://swiftfiddle.com/6tpbx4arnjhw3c6r477kgovg3u) is an example of what I mean. Notice that the `@TaskLocal` variable lives on `class Foo`, not the instance of the task.  
> **username_1**: Could you edit this example into your question?  
> **username_0**: @AlexisKing edited  
---
## 1 answers
### Answer by username_1 on 2023-06-28. Score: 6
Task-local variables can be implemented in essentially the same way as thread-local variables, which have two standard implementation strategies:

1. Every thread-local variable is really a weak map from threads to values. When the value is accessed, the current thread ID is used to index into the map and look up the current thread’s value.
2. Every thread-local variable is really just a unique key, and it doesn’t “contain” anything at all. Every thread context then contains a map from these keys to values. Accessing a thread-local variable just looks up the value in the current thread’s map using the variable’s key.

This second strategy is more commonly chosen when the implementor has control over the language and runtime, as it has nicer characteristics:

* Since the values are stored in the thread context itself, they are automatically deallocated when the thread is destroyed.
* There is no need for any synchronization because each thread context has its own distinct map, and no state is actually stored in the thread-local variables themselves.

There are many optimizations to the basic technique that allow thread-local storage to be made more efficient. For example, [this article](https://chao-tic.github.io/blog/2018/12/25/tls) discusses how thread-local storage is implemented on Linux; it is quite extensive. But the basic idea remains the same: thread-local variables are keys into a per-thread mapping.

This strategy can be easily adapted to tasks. All that is required is that tasks have some well-defined notion of task identity (so it’s clear what it means for code to execute within “the same task”) and that there is some way of obtaining a reference to the currently-executing task. Swift provides this via the [`withUnsafeCurrentTask(body:)`](https://developer.apple.com/documentation/swift/withunsafecurrenttask(body:)) function, which [the proposal](https://github.com/apple/swift-evolution/blob/main/proposals/0311-task-locals.md) notes is used in the implementation of task-local storage.
---
