Matthew Massicotte - The Bleeding Edge of Swift Concurrency

Swift TO
30 Aug 202321:26

Summary

TLDRThe speaker shares their experiences with Swift concurrency, highlighting the complexities and challenges they faced when using it incorrectly. They emphasize the importance of understanding concurrency as a 'deeply hard' problem and discuss various issues such as task retention cycles, actor reentrancy, and the difficulties of maintaining order in asynchronous code. The presentation aims to raise awareness about potential pitfalls, promote best practices, and encourage developers to use the latest APIs and tools to avoid common concurrency-related mistakes.

Takeaways

  • 🧠 The presentation is about the complexities and challenges of using Swift concurrency, focusing on the speaker's personal experiences with its misuse.
  • 📈 The speaker emphasizes the importance of understanding Swift concurrency as it is neither simple nor easy, despite its potential to simplify certain tasks.
  • 🔁 The concept of 'simple versus easy' is introduced to illustrate that some things may be easy to use but have deep underlying complexities.
  • 🔒 The talk discusses issues with Swift's actor model, such as difficulties with capturing 'self' weakly and the challenges of asynchronous sequences.
  • 💡 It highlights problems with main actor isolation in Swift UI and how property wrappers can implicitly change the global actor.
  • 🚧 The speaker shares their experiences with concurrency issues, such as the actor re-entrancy problem when caching values in an actor.
  • 🛠️ Solutions to concurrency problems are explored, like using 'async semaphore' for managing critical sections in a thread-safe manner.
  • ⚠️ The script points out the importance of making dependencies explicit in asynchronous code to avoid race conditions.
  • 🔧 The use of 'async unsafe' APIs is introduced to bypass the 'sendable' requirement for closures, which helps with updating UI and protecting mutable state.
  • 🛑 The speaker discusses the implications of a strict Swift concurrency build setting that led to numerous warnings and the need to address these issues proactively.
  • 🔄 The presentation concludes with a cautionary tale about the dangers of building APIs incompatible with Swift concurrency and the need for developers to be aware and plan for upcoming changes in Swift 6.

Q & A

  • What is the main topic of the presentation?

    -The main topic of the presentation is Swift concurrency and the experiences of using it, particularly focusing on the challenges and issues encountered when using it incorrectly.

  • Why does the presenter want to leave the audience with a bit of fear?

    -The presenter wants to leave the audience with a bit of fear, or rather a healthy respect, for Swift concurrency to highlight the potential issues and complexities that can arise from its misuse.

  • What is the difference between 'simple' and 'easy' as discussed in the presentation?

    -In the presentation, 'simple' refers to things that are easy to understand once the concepts behind them are grasped, but may require significant effort to initially understand. 'Easy' refers to things that can be used with little effort to learn but may be fundamentally complicated underneath.

  • What is the presenter's view on Swift concurrency in terms of simplicity and ease of use?

    -The presenter believes that Swift concurrency is neither simple nor easy. It is a complex system that, despite its potential to make things easier, can be challenging to use correctly and can lead to many problems if not handled properly.

  • What is the 'international unit of fear' mentioned by the presenter?

    -The 'international unit of fear' is a humorous term used by the presenter to quantify the level of concern or caution one should have when dealing with Swift concurrency issues, with 'scared beakers' being the unit of measurement.

  • Can you explain the issue with capturing 'self' in asynchronous tasks in Swift?

    -The issue with capturing 'self' in asynchronous tasks in Swift is related to the potential for creating retained cycles that can lead to memory leaks. The Swift team decided not to enforce capturing 'self' weakly by default to avoid breaking existing code and to allow for flexibility.

  • What is the problem with using async sequences and how can it lead to memory leaks?

    -Async sequences can lead to memory leaks because if they are effectively infinitely long, they may never finish executing, leading to the retention of 'self' indefinitely. This can happen if the async sequence is started and then suspended without properly completing.

  • Why does the presenter mention 'main actor' functions and their relation to tasks?

    -The presenter mentions 'main actor' functions because they have a specific context that tasks inherit when started. This can lead to unexpected behavior if a task is intended to run on a different actor but instead runs on the main actor due to inheritance.

  • What is the issue with property wrappers and how do they relate to 'main actor' isolation?

    -Property wrappers can implicitly change the global actor isolation of their enclosing type, which can lead to confusion and errors. This behavior has been identified as problematic, and there are proposals to change this behavior in Swift Evolution.

  • What is the 'actor re-entrancy problem' and how does it relate to caching in Swift concurrency?

    -The 'actor re-entrancy problem' occurs when an actor starts an asynchronous task that suspends, allowing other threads to enter the actor and perform the same operation, potentially leading to duplicated work or other issues. This can happen when caching values in an actor if not handled correctly.

  • How does the presenter suggest handling the ordering issues in asynchronous code?

    -The presenter suggests making dependencies explicit to handle ordering issues in asynchronous code. This can involve capturing a reference in one task and awaiting it in another to ensure that the operations occur in the correct order.

  • What is the significance of the 'async semaphore' in the context of Swift concurrency?

    -The 'async semaphore' is a package that provides a semaphore that is safe to use with Swift's concurrency model. It allows for synchronization similar to traditional locks but in a way that is compatible with Swift's async/await syntax and concurrency features.

  • What are the implications of the 'sendable' requirement in Swift's asynchronous functions?

    -The 'sendable' requirement implies that any value passed to an asynchronous function must be safely sendable across different threads or execution contexts. This can be problematic for types that are not sendable, such as UI-related objects, and may require workarounds or changes to the API design.

  • What is the presenter's stance on the Swift concurrency build setting?

    -The presenter advises turning up the Swift concurrency build setting to the maximum to get a full picture of potential issues in the code. They argue that it is better to be aware of these issues early on and plan for how to address them rather than building APIs that are fundamentally incompatible with Swift concurrency.

  • What does the presenter suggest for dealing with Swift 6's stricter concurrency requirements?

    -The presenter suggests that developers should start turning on the concurrency warnings in their builds to find and address problems in their code before Swift 6, where these warnings will become errors. This will help avoid building APIs that won't work with the new concurrency model.

Outlines

00:00

📚 Introduction to Swift Concurrency Challenges

The speaker begins by inviting the audience to recall impactful presentations that evoked strong emotions, setting the stage for a discussion on Swift concurrency. They candidly admit to using Swift concurrency incorrectly, emphasizing the complexity of the topic. The presentation aims to share experiences and lessons learned from misusing concurrency, with the goal of instilling a 'healthy respect' or a bit of fear in the audience. The talk will cover a range of issues from least to most concerning, using 'scared beakers' as a unit of fear, starting with a simple class worker example and moving towards more complex topics.

05:01

🔁 The Perils of Actor Re-entrancy and Caching

This paragraph delves into the intricacies of working with actors in Swift concurrency, highlighting the pitfalls of re-entrancy. The speaker uses a counter example to illustrate the problem of caching within an actor, where multiple threads can lead to duplicate work or wasted efforts. They propose a solution involving the caching of tasks instead of values, which prevents the re-entrancy issue by ensuring that the initial value computation is only started once. However, this approach comes with trade-offs, such as not benefiting from automatic cancellation or priority propagation that structured concurrency provides.

10:01

🔒 Managing Concurrency with Async Semaphores

The speaker introduces 'async semaphore' as a solution to manage concurrency safely, particularly when dealing with traditional locks that are incompatible with Swift's concurrency model. They discuss the challenges of updating UI elements or mutable states that are not 'sendable' and how Apple's new APIs, 'async(unsafe)' and 'mainActor', address these issues. However, these solutions are only available in the latest OS versions, prompting the speaker to create a package for backward compatibility. The paragraph also touches on the complexities of ensuring the correct order of execution in asynchronous code.

15:02

🛠 Navigating Swift Concurrency's Evolving Landscape

This section discusses the transition from synchronous to asynchronous functions in Swift and the implications this has on code ordering and execution. The speaker explains the need for explicit dependencies when managing tasks and states, as implicit ordering can lead to race conditions. They introduce their own solution, 'asynchronous queues', which allow for controlled ordering of tasks, and emphasize the importance of being aware of and planning for changes in Swift's concurrency model, especially with the upcoming Swift 6 release.

20:04

⚠️ The Concurrency Compiler Setting and Its Impact

The speaker shares their experience with Xcode's concurrency compiler setting, which controls the strictness of checking for proper concurrency usage. Initially set to the lowest level, an Xcode bug prompted an increase in the setting, resulting in a significant number of warnings. While some were easy to fix, others revealed deeper incompatibilities with existing APIs and Swift's concurrency model. The paragraph serves as a cautionary tale about the importance of addressing these issues proactively to avoid fundamental API incompatibilities in the future.

🚧 Embracing Change and Planning for Swift Concurrency

In the concluding paragraph, the speaker underscores the importance of embracing the changes brought by Swift concurrency and planning for the transition. They encourage developers to start addressing the compiler warnings and to be aware of the evolving nature of the language and its frameworks. The speaker also stresses the need for explicitness in managing dependencies and the potential for early warnings to help developers avoid building fundamentally flawed systems in the context of Swift 6's stricter concurrency requirements.

Mindmap

Keywords

💡Swift Concurrency

Swift Concurrency refers to the ability of Swift, a powerful programming language, to handle multiple tasks simultaneously. This is a core theme of the video, as the presenter discusses their experiences with using Swift's concurrency features, particularly focusing on the challenges and pitfalls they encountered. The video aims to educate viewers on how to use Swift concurrency correctly to avoid common mistakes.

💡Actor Model

The Actor Model is a concept in concurrent computing where an 'actor' is an independent unit of computation that owns its state and communicates with other actors via message passing. In the context of the video, the presenter mentions 'actors' in Swift, which are used to manage state in a concurrent environment, and discusses issues like actor re-entrancy and the implications of using actors with shared state.

💡Sendable

In Swift, 'Sendable' is a protocol that indicates a type can be safely sent across concurrency boundaries, such as between threads. The video discusses the importance of 'Sendable' in the context of capturing 'self' within closures and the restrictions it imposes on types that need to be used across different threads or actors.

💡Main Actor

The 'Main Actor' in Swift refers to the primary actor that is responsible for handling UI updates and other tasks that need to be performed on the main thread. The video script mentions issues related to 'Main Actor' isolation and how it can lead to complications when updating UI elements from a background task.

💡Async/Await

Async/Await is a feature in Swift that allows developers to write asynchronous code in a more synchronous-like manner. The presenter in the video uses async/await to illustrate the complexities that arise when dealing with concurrency and the need to manage tasks and their dependencies carefully.

💡Global Actor Isolation

Global Actor Isolation is a feature in Swift that ensures certain operations are performed on a specific actor, such as the main actor for UI updates. The video discusses the challenges faced by the presenter when applying global actor isolation to types and protocols that were not originally designed with actor isolation in mind.

💡Task Cancellation

Task Cancellation is a mechanism in Swift concurrency that allows for the cancellation of ongoing tasks. The video touches on the importance of handling cancellations properly, especially when dealing with tasks that may outlive their usefulness or when resources need to be freed up.

💡Continuations

Continuations represent a way to resume or continue an asynchronous task after a suspension point. In the video, the presenter discusses using continuations to manage the state of an asynchronous cache, illustrating the complexity of handling concurrency in Swift.

💡Unstructured Concurrency

Unstructured Concurrency is a term used to describe the management of asynchronous tasks without the structured approach provided by structured concurrency constructs like actors. The video mentions unstructured tasks in the context of caching, where the presenter explains the disadvantages of not participating in automatic cancellation or priority propagation.

💡Async Semaphore

Async Semaphore is a synchronization primitive in Swift that allows for managing access to a resource across concurrent tasks. The presenter in the video recommends using async semaphores as a solution to manage critical sections in code, providing an example of how to use it to avoid race conditions.

💡Concurrency Build Setting

The Concurrency Build Setting in Xcode controls the strictness of the compiler's checks for the correct use of concurrency features in Swift. The video describes how the presenter increased the setting to the maximum level to identify potential concurrency issues in their code, resulting in a large number of warnings that needed to be addressed.

Highlights

The presenter emphasizes the emotional impact of a presentation about Swift concurrency, drawing attention to the feelings stirred up by their experiences.

The misuse of Swift concurrency is highlighted, with the presenter admitting to using it incorrectly and wanting to share these lessons learned.

The concept of 'simple versus easy' is introduced, differentiating between things that are easy to use and those that are conceptually simple after understanding.

An analogy of a 'for loop' is used to illustrate the complexity of seemingly simple programming concepts to beginners.

Swift concurrency is described as neither simple nor easy, emphasizing the inherent difficulty of concurrency systems.

The presenter's experiences with Swift concurrency's sharp edges and problems are shared to prevent the audience from repeating the same mistakes.

The introduction of 'scared beakers' as a unit of fear to measure the severity of problems encountered with Swift concurrency.

A detailed explanation of the complexities and potential pitfalls of capturing 'self' in asynchronous tasks in Swift.

The issue of retained cycles and their impact on memory management when using Swift concurrency is discussed.

The presenter explains the unexpected behavior of tasks inheriting the actor context they are started in, leading to potential UI refresh issues.

A strategy to avoid inheriting actor context is presented, using 'detached' tasks to control execution context.

The limitations of synchronous access to immutable properties across modules in Swift are highlighted.

An exploration of the complexities added by property wrappers in Swift UI, which can implicitly change the global actor isolation.

The presenter introduces the concept of caching in concurrency, discussing the challenges of computing initial values asynchronously.

The 'actor re-entrancy problem' is explained with an example of a counter, demonstrating the issues with caching in concurrent environments.

A solution involving the use of 'async semaphore' is proposed to handle the complexities of asynchronous caching and concurrency.

The importance of understanding the implications of turning functions into asynchronous ones, especially regarding their synchronous call sites.

The challenges of maintaining order in programming with concurrency are discussed, emphasizing the need for explicit dependencies.

The presenter introduces a custom solution, 'asynchronous queues', to control task ordering in Swift concurrency.

The impact of the Swift compiler's strictness on checking concurrency usage through an Xcode build setting is examined.

The presenter shares their experience with a bug in Xcode 14.3 beta 1 that unexpectedly increased the compiler's strictness on concurrency.

A call to action for developers to enable compiler warnings for concurrency to find and fix issues in their code before they become errors in Swift 6.

Transcripts

play00:12

I'd like you all to think about some

play00:12

I'd like you all to think about some presentation you saw that left you with

play00:14

presentation you saw that left you with

play00:14

presentation you saw that left you with an impact

play00:15

an impact

play00:15

an impact stirred up some strong emotions

play00:18

stirred up some strong emotions

play00:18

stirred up some strong emotions and this is a presentation about Swift

play00:20

and this is a presentation about Swift

play00:20

and this is a presentation about Swift concurrency but I'm bringing up feelings

play00:22

concurrency but I'm bringing up feelings

play00:22

concurrency but I'm bringing up feelings because really it's my experiences using

play00:26

because really it's my experiences using

play00:26

because really it's my experiences using Swift concurrency

play00:27

Swift concurrency

play00:27

Swift concurrency and more specifically my experiences

play00:30

and more specifically my experiences

play00:30

and more specifically my experiences using Swift concurrency incorrectly and

play00:33

using Swift concurrency incorrectly and

play00:33

using Swift concurrency incorrectly and I just I cannot stress enough I have

play00:35

I just I cannot stress enough I have

play00:35

I just I cannot stress enough I have used this thing just horribly wrong

play00:38

used this thing just horribly wrong

play00:38

used this thing just horribly wrong now we're going to look at code we're

play00:40

now we're going to look at code we're

play00:40

now we're going to look at code we're going to look at a whole bunch of code

play00:41

going to look at a whole bunch of code

play00:41

going to look at a whole bunch of code actually

play00:42

actually

play00:42

actually but when I was getting started I wanted

play00:44

but when I was getting started I wanted

play00:44

but when I was getting started I wanted to think about what feeling I wanted you

play00:46

to think about what feeling I wanted you

play00:46

to think about what feeling I wanted you to leave with and I think I want to

play00:48

to leave with and I think I want to

play00:48

to leave with and I think I want to leave you with just a little bit of fear

play00:52

leave you with just a little bit of fear

play00:52

leave you with just a little bit of fear maybe we'll call it a healthy respect

play00:55

maybe we'll call it a healthy respect

play00:55

maybe we'll call it a healthy respect have you uh have you seen this before

play00:57

have you uh have you seen this before

play00:57

have you uh have you seen this before simple versus easy

play00:59

simple versus easy

play00:59

simple versus easy comes up in programming I think it's

play01:01

comes up in programming I think it's

play01:01

comes up in programming I think it's fascinating

play01:02

fascinating

play01:02

fascinating the idea is there are these things that

play01:04

the idea is there are these things that

play01:04

the idea is there are these things that are amazingly simple once you understand

play01:07

are amazingly simple once you understand

play01:07

are amazingly simple once you understand the concepts behind them but getting

play01:09

the concepts behind them but getting

play01:09

the concepts behind them but getting that understanding can require a lot of

play01:11

that understanding can require a lot of

play01:11

that understanding can require a lot of work

play01:12

work

play01:12

work and this compares to something that

play01:14

and this compares to something that

play01:14

and this compares to something that maybe it's a little bit easy to use easy

play01:16

maybe it's a little bit easy to use easy

play01:16

maybe it's a little bit easy to use easy to learn but actually comes down to

play01:18

to learn but actually comes down to

play01:18

to learn but actually comes down to being very deeply complicated

play01:21

being very deeply complicated

play01:21

being very deeply complicated uh take a look at this for Loop here

play01:23

uh take a look at this for Loop here

play01:23

uh take a look at this for Loop here what is going on like we're going over

play01:25

what is going on like we're going over

play01:25

what is going on like we're going over some array and then we're checking the

play01:26

some array and then we're checking the

play01:27

some array and then we're checking the condition function on items in there and

play01:28

condition function on items in there and

play01:28

condition function on items in there and if they pass we're putting into another

play01:30

if they pass we're putting into another

play01:30

if they pass we're putting into another array this is just a filter

play01:33

array this is just a filter

play01:33

array this is just a filter but if you think this is easy you try

play01:35

but if you think this is easy you try

play01:35

but if you think this is easy you try explaining this to a beginner

play01:38

explaining this to a beginner

play01:38

explaining this to a beginner now I bring this up because I believe

play01:40

now I bring this up because I believe

play01:40

now I bring this up because I believe that Swift concurrency is neither simple

play01:42

that Swift concurrency is neither simple

play01:42

that Swift concurrency is neither simple nor easy and that is not a criticism

play01:46

nor easy and that is not a criticism

play01:46

nor easy and that is not a criticism the problem is that fundamentally Swift

play01:48

the problem is that fundamentally Swift

play01:48

the problem is that fundamentally Swift concurrency is a concurrency system and

play01:50

concurrency is a concurrency system and

play01:50

concurrency is a concurrency system and this is just a deeply hard thing to

play01:53

this is just a deeply hard thing to

play01:53

this is just a deeply hard thing to solve

play01:54

solve

play01:54

solve um I think it's amazing what Swift

play01:56

um I think it's amazing what Swift

play01:56

um I think it's amazing what Swift concurrency can do especially

play01:57

concurrency can do especially

play01:57

concurrency can do especially considering it's really not all the way

play01:59

considering it's really not all the way

play01:59

considering it's really not all the way done and is still under active

play02:00

done and is still under active

play02:00

done and is still under active development

play02:01

development

play02:01

development it really can make things easier

play02:03

it really can make things easier

play02:03

it really can make things easier but while I was learning about it and

play02:06

but while I was learning about it and

play02:06

but while I was learning about it and introducing it into my own code I ran

play02:08

introducing it into my own code I ran

play02:08

introducing it into my own code I ran into a lot of problems I mean I cut

play02:10

into a lot of problems I mean I cut

play02:10

into a lot of problems I mean I cut myself on so many sharp edges and so

play02:13

myself on so many sharp edges and so

play02:13

myself on so many sharp edges and so what I want to do with this presentation

play02:14

what I want to do with this presentation

play02:14

what I want to do with this presentation is introduce them to you explain them

play02:17

is introduce them to you explain them

play02:17

is introduce them to you explain them and hopefully prevent you from having

play02:18

and hopefully prevent you from having

play02:18

and hopefully prevent you from having the same problem

play02:20

the same problem

play02:20

the same problem so what I uh what I did was I wrote down

play02:22

so what I uh what I did was I wrote down

play02:22

so what I uh what I did was I wrote down all the problems that I had and I kind

play02:24

all the problems that I had and I kind

play02:24

all the problems that I had and I kind of sorted them from the least scary to

play02:26

of sorted them from the least scary to

play02:26

of sorted them from the least scary to the most scary I needed some kind of a

play02:29

the most scary I needed some kind of a

play02:29

the most scary I needed some kind of a unit to describe those things so we're

play02:30

unit to describe those things so we're

play02:30

unit to describe those things so we're going with scared beakers here this is

play02:32

going with scared beakers here this is

play02:32

going with scared beakers here this is the international unit of fear

play02:34

the international unit of fear

play02:34

the international unit of fear uh we're starting with one

play02:36

uh we're starting with one

play02:36

uh we're starting with one uh so take a look at this this is a

play02:38

uh so take a look at this this is a

play02:38

uh so take a look at this this is a class worker and it's going to run some

play02:40

class worker and it's going to run some

play02:40

class worker and it's going to run some sort of uh some sort of asynchronous

play02:42

sort of uh some sort of asynchronous

play02:42

sort of uh some sort of asynchronous long-running operation

play02:43

long-running operation

play02:43

long-running operation and then we need to kick that off

play02:45

and then we need to kick that off

play02:45

and then we need to kick that off synchronously so in the start function

play02:47

synchronously so in the start function

play02:47

synchronously so in the start function we just like start a task

play02:48

we just like start a task

play02:48

we just like start a task and this is one of the first kinds of

play02:50

and this is one of the first kinds of

play02:50

and this is one of the first kinds of things I did I was immediately confused

play02:52

things I did I was immediately confused

play02:52

things I did I was immediately confused why isn't the compiler telling me that I

play02:54

why isn't the compiler telling me that I

play02:54

why isn't the compiler telling me that I have to catch yourself

play02:55

have to catch yourself

play02:55

have to catch yourself this is intentional

play02:57

this is intentional

play02:57

this is intentional uh what's going on is the Swift team was

play02:59

uh what's going on is the Swift team was

play02:59

uh what's going on is the Swift team was thinking well the only times we really

play03:01

thinking well the only times we really

play03:01

thinking well the only times we really want to ensure that you capture self is

play03:03

want to ensure that you capture self is

play03:03

want to ensure that you capture self is when it's likely that you're going to

play03:04

when it's likely that you're going to

play03:04

when it's likely that you're going to introduce a retained cycle and it was

play03:06

introduce a retained cycle and it was

play03:06

introduce a retained cycle and it was decided that that is not likely in this

play03:08

decided that that is not likely in this

play03:08

decided that that is not likely in this case

play03:12

um that has sort of had I think mixed

play03:12

um that has sort of had I think mixed results in my experience it's true that

play03:15

results in my experience it's true that

play03:15

results in my experience it's true that usually tasks start and they run all the

play03:17

usually tasks start and they run all the

play03:17

usually tasks start and they run all the way to the beginning and then they

play03:17

way to the beginning and then they

play03:17

way to the beginning and then they finish even if maybe they take a little

play03:19

finish even if maybe they take a little

play03:19

finish even if maybe they take a little while along the way

play03:21

while along the way

play03:21

while along the way but if you do run into a problem you're

play03:22

but if you do run into a problem you're

play03:22

but if you do run into a problem you're thinking well no big deal I'll just do

play03:24

thinking well no big deal I'll just do

play03:24

thinking well no big deal I'll just do what I do for all closures and I'll add

play03:25

what I do for all closures and I'll add

play03:25

what I do for all closures and I'll add a weak self in there and this does work

play03:27

a weak self in there and this does work

play03:27

a weak self in there and this does work with the slight Quirk that everything on

play03:31

with the slight Quirk that everything on

play03:31

with the slight Quirk that everything on the right side of an await is strongly

play03:34

the right side of an await is strongly

play03:34

the right side of an await is strongly retained while it's running including

play03:36

retained while it's running including

play03:36

retained while it's running including while it's just suspended

play03:38

while it's just suspended

play03:38

while it's just suspended um so let's might not introduce a leak

play03:40

um so let's might not introduce a leak

play03:40

um so let's might not introduce a leak it certainly could extend life cycles

play03:42

it certainly could extend life cycles

play03:42

it certainly could extend life cycles Beyond where you might expect them to be

play03:44

Beyond where you might expect them to be

play03:44

Beyond where you might expect them to be there's one other case where I think

play03:46

there's one other case where I think

play03:46

there's one other case where I think leaks can be real and happen is with

play03:50

leaks can be real and happen is with

play03:50

leaks can be real and happen is with async sequence and in that case what

play03:51

async sequence and in that case what

play03:51

async sequence and in that case what you'd have is a task it's maybe starting

play03:53

you'd have is a task it's maybe starting

play03:53

you'd have is a task it's maybe starting up some four or a weight

play03:55

up some four or a weight

play03:55

up some four or a weight the problem is that many async sequences

play03:57

the problem is that many async sequences

play03:57

the problem is that many async sequences are effectively infinitely long and so

play03:59

are effectively infinitely long and so

play04:00

are effectively infinitely long and so while it's true in principle this thing

play04:01

while it's true in principle this thing

play04:01

while it's true in principle this thing will finish in practice it never

play04:03

will finish in practice it never

play04:03

will finish in practice it never actually happens and you end up holding

play04:04

actually happens and you end up holding

play04:04

actually happens and you end up holding on to self forever

play04:09

here's another example we've got some

play04:09

here's another example we've got some main actor function that's refreshing

play04:10

main actor function that's refreshing

play04:10

main actor function that's refreshing our UI in some way and it's got to do

play04:13

our UI in some way and it's got to do

play04:13

our UI in some way and it's got to do that it has to run some expensive and

play04:16

that it has to run some expensive and

play04:16

that it has to run some expensive and synchronous function and so you think

play04:18

synchronous function and so you think

play04:18

synchronous function and so you think well before I used to like do some async

play04:21

well before I used to like do some async

play04:21

well before I used to like do some async on a global queue I'll just throw a task

play04:22

on a global queue I'll just throw a task

play04:22

on a global queue I'll just throw a task in there but this does not work this

play04:24

in there but this does not work this

play04:24

in there but this does not work this actually still runs on the main actor I

play04:26

actually still runs on the main actor I

play04:26

actually still runs on the main actor I think this surprises a lot of people and

play04:28

think this surprises a lot of people and

play04:28

think this surprises a lot of people and the reason why is because that task

play04:30

the reason why is because that task

play04:30

the reason why is because that task unstructured tasks inherit the actor

play04:33

unstructured tasks inherit the actor

play04:33

unstructured tasks inherit the actor context that they're starting in so

play04:34

context that they're starting in so

play04:34

context that they're starting in so because we started on a main actor this

play04:36

because we started on a main actor this

play04:36

because we started on a main actor this task will run on a main actor as well

play04:38

task will run on a main actor as well

play04:38

task will run on a main actor as well and this can be convenient in a number

play04:40

and this can be convenient in a number

play04:40

and this can be convenient in a number of cases but in this case it's the exact

play04:42

of cases but in this case it's the exact

play04:42

of cases but in this case it's the exact opposite of what we want

play04:44

opposite of what we want

play04:44

opposite of what we want the solution is just to throw a detached

play04:47

the solution is just to throw a detached

play04:47

the solution is just to throw a detached in here and this is another API that

play04:48

in here and this is another API that

play04:48

in here and this is another API that works exactly the same except it does

play04:50

works exactly the same except it does

play04:50

works exactly the same except it does not do this inheritance thing

play04:57

all right two scared beakers now uh

play04:57

all right two scared beakers now uh let's take a look at an actor this is a

play04:59

let's take a look at an actor this is a

play04:59

let's take a look at an actor this is a really simple actor in fact it does

play05:00

really simple actor in fact it does

play05:00

really simple actor in fact it does nothing at all it just has one property

play05:02

nothing at all it just has one property

play05:02

nothing at all it just has one property and that property is immutable

play05:04

and that property is immutable

play05:04

and that property is immutable and in some other actor over here we're

play05:06

and in some other actor over here we're

play05:06

and in some other actor over here we're referencing that value and we're doing

play05:08

referencing that value and we're doing

play05:08

referencing that value and we're doing it synchronously typically you would

play05:10

it synchronously typically you would

play05:10

it synchronously typically you would have to do this always

play05:11

have to do this always

play05:11

have to do this always asynchronously when you're Crossing

play05:13

asynchronously when you're Crossing

play05:13

asynchronously when you're Crossing actors like this but in this case it's a

play05:15

actors like this but in this case it's a

play05:15

actors like this but in this case it's a it's synchronous and the compiler's

play05:16

it's synchronous and the compiler's

play05:16

it's synchronous and the compiler's happy

play05:17

happy

play05:18

happy as long as you do it within the same

play05:19

as long as you do it within the same

play05:19

as long as you do it within the same module but this is not allowed across

play05:22

module but this is not allowed across

play05:22

module but this is not allowed across modules

play05:23

modules

play05:23

modules what's going on here

play05:24

what's going on here

play05:24

what's going on here well it's interesting so Swift is

play05:27

well it's interesting so Swift is

play05:27

well it's interesting so Swift is preserving your ability to change that

play05:29

preserving your ability to change that

play05:29

preserving your ability to change that let in the future into a VAR

play05:32

let in the future into a VAR

play05:32

let in the future into a VAR and I think that's really cool because

play05:34

and I think that's really cool because

play05:34

and I think that's really cool because you can do it without making any Source

play05:36

you can do it without making any Source

play05:36

you can do it without making any Source uh it doesn't introduce source

play05:37

uh it doesn't introduce source

play05:37

uh it doesn't introduce source incompatibility

play05:39

incompatibility

play05:39

incompatibility however maybe you spent a lot of time

play05:40

however maybe you spent a lot of time

play05:40

however maybe you spent a lot of time working on module a and you even realize

play05:42

working on module a and you even realize

play05:42

working on module a and you even realize this until you got into working on

play05:44

this until you got into working on

play05:44

this until you got into working on module B finally so this is definitely

play05:45

module B finally so this is definitely

play05:45

module B finally so this is definitely something worth keeping keeping in mind

play05:51

how about Swift UI just talking about

play05:51

how about Swift UI just talking about Swift UI uh here we got some view it's

play05:54

Swift UI uh here we got some view it's

play05:54

Swift UI uh here we got some view it's got a state object and in the body it is

play05:57

got a state object and in the body it is

play05:57

got a state object and in the body it is running some function on a peer that's

play05:58

running some function on a peer that's

play05:58

running some function on a peer that's calling some other function main actor

play06:00

calling some other function main actor

play06:00

calling some other function main actor isolated no problems until for some

play06:03

isolated no problems until for some

play06:03

isolated no problems until for some reason you decide to change that state

play06:04

reason you decide to change that state

play06:04

reason you decide to change that state object into a state and now the compiler

play06:07

object into a state and now the compiler

play06:07

object into a state and now the compiler is complaining about main actor

play06:09

is complaining about main actor

play06:09

is complaining about main actor isolation

play06:10

isolation

play06:10

isolation what could possibly be going on here

play06:13

what could possibly be going on here

play06:13

what could possibly be going on here well it turns out that property wrappers

play06:16

well it turns out that property wrappers

play06:16

well it turns out that property wrappers can implicitly change the global actor

play06:19

can implicitly change the global actor

play06:19

can implicitly change the global actor isolation of their enclosing type this

play06:21

isolation of their enclosing type this

play06:21

isolation of their enclosing type this is super weird and has surprised so many

play06:24

is super weird and has surprised so many

play06:24

is super weird and has surprised so many people in fact it has been such a

play06:25

people in fact it has been such a

play06:25

people in fact it has been such a problem that I lifted this code from the

play06:28

problem that I lifted this code from the

play06:28

problem that I lifted this code from the Swift Evolution proposal to change this

play06:30

Swift Evolution proposal to change this

play06:30

Swift Evolution proposal to change this and that's coming although right now it

play06:32

and that's coming although right now it

play06:32

and that's coming although right now it still works this way

play06:34

still works this way

play06:34

still works this way the solution of course is to get rid of

play06:36

the solution of course is to get rid of

play06:36

the solution of course is to get rid of this implicit main actor isolation and

play06:38

this implicit main actor isolation and

play06:38

this implicit main actor isolation and instead apply an annotation either to

play06:40

instead apply an annotation either to

play06:40

instead apply an annotation either to the view appeared method or to the whole

play06:42

the view appeared method or to the whole

play06:42

the view appeared method or to the whole type

play06:46

okay

play06:46

okay three scared beakers now we're getting

play06:47

three scared beakers now we're getting

play06:47

three scared beakers now we're getting into some more interesting stuff

play06:50

into some more interesting stuff

play06:50

into some more interesting stuff um

play06:51

um

play06:51

um caching I mean like how hard could this

play06:53

caching I mean like how hard could this

play06:53

caching I mean like how hard could this be right

play06:55

be right

play06:55

be right well let's look at a counter and it's a

play06:59

well let's look at a counter and it's a

play07:00

well let's look at a counter and it's a little bit of a strange counter in that

play07:01

little bit of a strange counter in that

play07:01

little bit of a strange counter in that for some reason this counter's initial

play07:03

for some reason this counter's initial

play07:03

for some reason this counter's initial starting value is expensive to compute

play07:06

starting value is expensive to compute

play07:06

starting value is expensive to compute for some reason

play07:07

for some reason

play07:07

for some reason so what we decide to do is when we

play07:09

so what we decide to do is when we

play07:09

so what we decide to do is when we increment we're going to increment a

play07:10

increment we're going to increment a

play07:10

increment we're going to increment a Delta and then when we need that

play07:12

Delta and then when we need that

play07:12

Delta and then when we need that absolute count we will just go and

play07:15

absolute count we will just go and

play07:15

absolute count we will just go and compute it asynchronously and then we're

play07:17

compute it asynchronously and then we're

play07:17

compute it asynchronously and then we're going to add these values together

play07:18

going to add these values together

play07:18

going to add these values together and we think well why should we do this

play07:20

and we think well why should we do this

play07:20

and we think well why should we do this every time why don't we cache that

play07:22

every time why don't we cache that

play07:22

every time why don't we cache that initial value in some private variable

play07:24

initial value in some private variable

play07:24

initial value in some private variable so that we can only have to do it once

play07:27

so that we can only have to do it once

play07:27

so that we can only have to do it once and you know we know we're going to use

play07:28

and you know we know we're going to use

play07:28

and you know we know we're going to use this thing concurrently and this is an

play07:30

this thing concurrently and this is an

play07:30

this thing concurrently and this is an actor and actors are magic solutions to

play07:32

actor and actors are magic solutions to

play07:32

actor and actors are magic solutions to all concurrency problems we have nothing

play07:33

all concurrency problems we have nothing

play07:33

all concurrency problems we have nothing to worry about

play07:35

to worry about

play07:35

to worry about uh but this actually a classic example

play07:37

uh but this actually a classic example

play07:37

uh but this actually a classic example of the actor re-entrancy problem

play07:40

of the actor re-entrancy problem

play07:40

of the actor re-entrancy problem and what's going on here is get into get

play07:42

and what's going on here is get into get

play07:42

and what's going on here is get into get count some thread goes into get count

play07:44

count some thread goes into get count

play07:44

count some thread goes into get count initial is nil so it goes and kicks off

play07:47

initial is nil so it goes and kicks off

play07:47

initial is nil so it goes and kicks off this await suspends and that makes it

play07:49

this await suspends and that makes it

play07:49

this await suspends and that makes it free for other threads to come into this

play07:51

free for other threads to come into this

play07:51

free for other threads to come into this actor and do the exact same thing

play07:54

actor and do the exact same thing

play07:54

actor and do the exact same thing uh now in this case it's not really a

play07:56

uh now in this case it's not really a

play07:56

uh now in this case it's not really a huge deal I guess you're just kind of

play07:57

huge deal I guess you're just kind of

play07:57

huge deal I guess you're just kind of like doing some wasted work but you can

play07:59

like doing some wasted work but you can

play07:59

like doing some wasted work but you can imagine situations where this would be a

play08:01

imagine situations where this would be a

play08:01

imagine situations where this would be a big problem

play08:03

big problem

play08:03

big problem if you search online you will find

play08:06

if you search online you will find

play08:06

if you search online you will find solutions to this and here's kind of

play08:07

solutions to this and here's kind of

play08:07

solutions to this and here's kind of what they look like it's a change where

play08:09

what they look like it's a change where

play08:09

what they look like it's a change where instead of caching caching the initial

play08:11

instead of caching caching the initial

play08:11

instead of caching caching the initial value you cache a task that returns that

play08:14

value you cache a task that returns that

play08:14

value you cache a task that returns that initial value

play08:15

initial value

play08:15

initial value and if you look at get count what's

play08:17

and if you look at get count what's

play08:17

and if you look at get count what's happening now is that we are reading

play08:18

happening now is that we are reading

play08:18

happening now is that we are reading initial tasks or creating one and

play08:20

initial tasks or creating one and

play08:20

initial tasks or creating one and writing to it with no await in between

play08:22

writing to it with no await in between

play08:22

writing to it with no await in between there's no opportunity for suspension

play08:24

there's no opportunity for suspension

play08:24

there's no opportunity for suspension and so we are able to do this safely

play08:25

and so we are able to do this safely

play08:25

and so we are able to do this safely within an actor

play08:28

within an actor

play08:28

within an actor um this works this is correct but it

play08:30

um this works this is correct but it

play08:30

um this works this is correct but it does come with some disadvantages you

play08:31

does come with some disadvantages you

play08:31

does come with some disadvantages you see we're using tasks unstructured

play08:33

see we're using tasks unstructured

play08:33

see we're using tasks unstructured concurrency and this means that we are

play08:37

concurrency and this means that we are

play08:37

concurrency and this means that we are not going to participate in automatic

play08:39

not going to participate in automatic

play08:39

not going to participate in automatic cancellation or priority propagation

play08:42

cancellation or priority propagation

play08:42

cancellation or priority propagation that might not be a huge deal but if you

play08:44

that might not be a huge deal but if you

play08:44

that might not be a huge deal but if you wanted to do this we can we have to take

play08:47

wanted to do this we can we have to take

play08:47

wanted to do this we can we have to take a look though at what it even means to

play08:49

a look though at what it even means to

play08:49

a look though at what it even means to be an asynchronous cache so let's model

play08:52

be an asynchronous cache so let's model

play08:52

be an asynchronous cache so let's model that

play08:57

let's model that with a um with some

play08:57

let's model that with a um with some State and it can be in three possible

play08:59

State and it can be in three possible

play08:59

State and it can be in three possible three possible States so we have our

play09:01

three possible States so we have our

play09:01

three possible States so we have our caches ready that means it has a value

play09:02

caches ready that means it has a value

play09:03

caches ready that means it has a value and it can just return it

play09:05

and it can just return it

play09:05

and it can just return it the other possibility is we're in

play09:07

the other possibility is we're in

play09:07

the other possibility is we're in progress and that means some other

play09:08

progress and that means some other

play09:08

progress and that means some other thread has started filling the cache but

play09:10

thread has started filling the cache but

play09:10

thread has started filling the cache but hasn't finished yet and we now have to

play09:12

hasn't finished yet and we now have to

play09:12

hasn't finished yet and we now have to wait for that process to finish this is

play09:13

wait for that process to finish this is

play09:13

wait for that process to finish this is in progress

play09:15

in progress

play09:15

in progress and then the other state is nobody's

play09:17

and then the other state is nobody's

play09:17

and then the other state is nobody's filled the cash and there's nobody

play09:18

filled the cash and there's nobody

play09:18

filled the cash and there's nobody waiting so we're the first and we're

play09:19

waiting so we're the first and we're

play09:19

waiting so we're the first and we're going to talk about that one right now

play09:22

going to talk about that one right now

play09:22

going to talk about that one right now so in this case the first thing we got

play09:24

so in this case the first thing we got

play09:24

so in this case the first thing we got to do is transition this state so that

play09:25

to do is transition this state so that

play09:25

to do is transition this state so that all future requesters will get funneled

play09:28

all future requesters will get funneled

play09:28

all future requesters will get funneled to this in progress condition

play09:30

to this in progress condition

play09:30

to this in progress condition then we're safe now to do this await we

play09:33

then we're safe now to do this await we

play09:33

then we're safe now to do this await we don't have to worry about being

play09:33

don't have to worry about being

play09:33

don't have to worry about being interrupted and we can return that value

play09:35

interrupted and we can return that value

play09:35

interrupted and we can return that value but we still need to take care of these

play09:38

but we still need to take care of these

play09:38

but we still need to take care of these other potential tasks that are waiting

play09:41

other potential tasks that are waiting

play09:41

other potential tasks that are waiting and we're going to do that using

play09:43

and we're going to do that using

play09:43

and we're going to do that using continuations somehow

play09:45

continuations somehow

play09:45

continuations somehow let's look at what we do in progress

play09:48

let's look at what we do in progress

play09:48

let's look at what we do in progress so the first thing we need is an ID that

play09:50

so the first thing we need is an ID that

play09:51

so the first thing we need is an ID that uniquely represents this particular

play09:53

uniquely represents this particular

play09:53

uniquely represents this particular request

play09:54

request

play09:54

request um this is kind of contextual but it can

play09:56

um this is kind of contextual but it can

play09:56

um this is kind of contextual but it can be easier complicated depending

play09:59

be easier complicated depending

play09:59

be easier complicated depending we now need a continuation and we're

play10:01

we now need a continuation and we're

play10:01

we now need a continuation and we're going to get this from the built-in with

play10:02

going to get this from the built-in with

play10:03

going to get this from the built-in with checked continuation function that gives

play10:05

checked continuation function that gives

play10:05

checked continuation function that gives us a continuation which is safe to

play10:07

us a continuation which is safe to

play10:07

us a continuation which is safe to escape from this closure and we're going

play10:08

escape from this closure and we're going

play10:08

escape from this closure and we're going to associate it with this ID and you're

play10:10

to associate it with this ID and you're

play10:10

to associate it with this ID and you're thinking but wait a minute I see this

play10:11

thinking but wait a minute I see this

play10:11

thinking but wait a minute I see this await keyword so doesn't that mean we

play10:14

await keyword so doesn't that mean we

play10:14

await keyword so doesn't that mean we have to worry about actor reentrancy and

play10:17

have to worry about actor reentrancy and

play10:17

have to worry about actor reentrancy and uh this function is weird because it

play10:19

uh this function is weird because it

play10:19

uh this function is weird because it guarantees that its body will be

play10:21

guarantees that its body will be

play10:21

guarantees that its body will be executed synchronously before it ever

play10:23

executed synchronously before it ever

play10:23

executed synchronously before it ever gets suspended and that is very strange

play10:25

gets suspended and that is very strange

play10:25

gets suspended and that is very strange that is not the way of the await keyword

play10:27

that is not the way of the await keyword

play10:27

that is not the way of the await keyword is supposed to work and yet this is how

play10:28

is supposed to work and yet this is how

play10:28

is supposed to work and yet this is how this function works

play10:31

this function works

play10:31

this function works um but with this we're able to take care

play10:33

um but with this we're able to take care

play10:33

um but with this we're able to take care of resuming waiting continuations but we

play10:36

of resuming waiting continuations but we

play10:36

of resuming waiting continuations but we still have to deal with cancellation now

play10:37

still have to deal with cancellation now

play10:37

still have to deal with cancellation now we need to rely on yet another built-in

play10:39

we need to rely on yet another built-in

play10:39

we need to rely on yet another built-in function called with task cancellation

play10:41

function called with task cancellation

play10:41

function called with task cancellation Handler also weird also runs its body

play10:44

Handler also weird also runs its body

play10:44

Handler also weird also runs its body synchronously

play10:45

synchronously

play10:45

synchronously but that allows us to associate the

play10:47

but that allows us to associate the

play10:47

but that allows us to associate the continuation with that particular ID and

play10:49

continuation with that particular ID and

play10:49

continuation with that particular ID and execute that continuation

play10:51

execute that continuation

play10:51

execute that continuation and I have omitted all kinds of code

play10:53

and I have omitted all kinds of code

play10:53

and I have omitted all kinds of code here but I think we can agree this is

play10:54

here but I think we can agree this is

play10:54

here but I think we can agree this is very complicated

play10:56

very complicated

play10:56

very complicated and when I realized this I first started

play10:58

and when I realized this I first started

play10:58

and when I realized this I first started thinking I mean there's got to be some

play10:59

thinking I mean there's got to be some

play10:59

thinking I mean there's got to be some kind of alternative

play11:01

kind of alternative

play11:01

kind of alternative to understand that let's look at our

play11:03

to understand that let's look at our

play11:03

to understand that let's look at our original incorrect implementation again

play11:05

original incorrect implementation again

play11:06

original incorrect implementation again remember the problem is in get count

play11:08

remember the problem is in get count

play11:08

remember the problem is in get count we're reading initial we're then waiting

play11:10

we're reading initial we're then waiting

play11:10

we're reading initial we're then waiting and then writing to initial so we have

play11:12

and then writing to initial so we have

play11:12

and then writing to initial so we have this suspension point in between a

play11:14

this suspension point in between a

play11:14

this suspension point in between a critical section

play11:15

critical section

play11:15

critical section and if we're not using Swift concurrency

play11:17

and if we're not using Swift concurrency

play11:17

and if we're not using Swift concurrency critical section we're going to start

play11:19

critical section we're going to start

play11:19

critical section we're going to start thinking lock of some kind but

play11:20

thinking lock of some kind but

play11:20

thinking lock of some kind but traditional locks are not safe to use

play11:23

traditional locks are not safe to use

play11:23

traditional locks are not safe to use with swift concurrency

play11:25

with swift concurrency

play11:25

with swift concurrency mercifully somebody made one that is

play11:28

mercifully somebody made one that is

play11:28

mercifully somebody made one that is it's called async semaphore and you can

play11:30

it's called async semaphore and you can

play11:30

it's called async semaphore and you can throw it right in there keep code that

play11:31

throw it right in there keep code that

play11:31

throw it right in there keep code that looks much more familiar to what you'd

play11:33

looks much more familiar to what you'd

play11:33

looks much more familiar to what you'd used to but it even supports uh

play11:36

used to but it even supports uh

play11:36

used to but it even supports uh cancellation

play11:37

cancellation

play11:37

cancellation this is a wonderful package and I think

play11:39

this is a wonderful package and I think

play11:39

this is a wonderful package and I think it's something you should at least be

play11:40

it's something you should at least be

play11:40

it's something you should at least be aware of while you're working with swift

play11:42

aware of while you're working with swift

play11:42

aware of while you're working with swift concurrency

play11:49

okay we're getting to the real stuff now

play11:49

okay we're getting to the real stuff now uh

play11:54

here's some code that probably can be

play11:54

here's some code that probably can be found in every application that has ever

play11:57

found in every application that has ever

play11:57

found in every application that has ever been written

play11:59

been written

play11:59

been written um unfortunately with concurrency

play12:02

um unfortunately with concurrency

play12:02

um unfortunately with concurrency checking on this produces warnings and

play12:04

checking on this produces warnings and

play12:04

checking on this produces warnings and remember that in Swift 6 These Warnings

play12:06

remember that in Swift 6 These Warnings

play12:06

remember that in Swift 6 These Warnings are going to become errors

play12:08

are going to become errors

play12:08

are going to become errors and I'm pretty sure this is an example

play12:10

and I'm pretty sure this is an example

play12:10

and I'm pretty sure this is an example where those errors are rapidly going to

play12:12

where those errors are rapidly going to

play12:12

where those errors are rapidly going to become tiers now let me explain why

play12:15

become tiers now let me explain why

play12:15

become tiers now let me explain why there's two warnings actually here so

play12:17

there's two warnings actually here so

play12:17

there's two warnings actually here so the first one is talking about capturing

play12:19

the first one is talking about capturing

play12:19

the first one is talking about capturing self which is unscendable and self is

play12:21

self which is unscendable and self is

play12:21

self which is unscendable and self is just it doesn't matter some sort of UI

play12:24

just it doesn't matter some sort of UI

play12:24

just it doesn't matter some sort of UI related object typically not a sendable

play12:26

related object typically not a sendable

play12:26

related object typically not a sendable thing and definitely not sendable here

play12:27

thing and definitely not sendable here

play12:27

thing and definitely not sendable here but it's necessary because Apple has

play12:29

but it's necessary because Apple has

play12:29

but it's necessary because Apple has updated dispatch cues async method to

play12:32

updated dispatch cues async method to

play12:32

updated dispatch cues async method to only accept sendable ascendable closure

play12:36

only accept sendable ascendable closure

play12:36

only accept sendable ascendable closure um this is a real problem for us and

play12:38

um this is a real problem for us and

play12:38

um this is a real problem for us and it's actually a problem in many other

play12:39

it's actually a problem in many other

play12:39

it's actually a problem in many other cases besides just updating the UI

play12:41

cases besides just updating the UI

play12:41

cases besides just updating the UI because dispatch queues can often be

play12:42

because dispatch queues can often be

play12:42

because dispatch queues can often be used to protect mutable state that is

play12:44

used to protect mutable state that is

play12:44

used to protect mutable state that is inherently unscendable

play12:46

inherently unscendable

play12:46

inherently unscendable Apple has recognized this as a problem

play12:48

Apple has recognized this as a problem

play12:48

Apple has recognized this as a problem and so they've introduced new API called

play12:51

and so they've introduced new API called

play12:51

and so they've introduced new API called async unsafe which is exactly the same

play12:53

async unsafe which is exactly the same

play12:53

async unsafe which is exactly the same but removes the sendable requirement

play12:55

but removes the sendable requirement

play12:55

but removes the sendable requirement that takes care of one warning but

play12:57

that takes care of one warning but

play12:57

that takes care of one warning but there's another

play12:59

there's another

play12:59

there's another uh

play13:01

uh

play13:01

uh it's telling us that up this update UI

play13:03

it's telling us that up this update UI

play13:03

it's telling us that up this update UI main actor function is not being run on

play13:05

main actor function is not being run on

play13:05

main actor function is not being run on the main actor but how can that be main

play13:06

the main actor but how can that be main

play13:06

the main actor but how can that be main is like right there

play13:08

is like right there

play13:08

is like right there the thing to understand is that async

play13:11

the thing to understand is that async

play13:11

the thing to understand is that async unsafe and async these are defined on

play13:13

unsafe and async these are defined on

play13:13

unsafe and async these are defined on all operation queues not just the main

play13:16

all operation queues not just the main

play13:16

all operation queues not just the main instance and apple was not able to apply

play13:18

instance and apple was not able to apply

play13:18

instance and apple was not able to apply main actor isolation because of this

play13:20

main actor isolation because of this

play13:20

main actor isolation because of this fortunately they've introduced yet more

play13:23

fortunately they've introduced yet more

play13:23

fortunately they've introduced yet more new API that we can use

play13:30

and what this will do is say okay the

play13:30

and what this will do is say okay the compiler doesn't see This Global actor

play13:32

compiler doesn't see This Global actor

play13:32

compiler doesn't see This Global actor isolation but we promise that it is

play13:34

isolation but we promise that it is

play13:34

isolation but we promise that it is there and should that not happen at

play13:35

there and should that not happen at

play13:35

there and should that not happen at runtime this actually will crash

play13:38

runtime this actually will crash

play13:38

runtime this actually will crash so with these two changes we've now

play13:40

so with these two changes we've now

play13:40

so with these two changes we've now eliminated all the warnings but we've

play13:41

eliminated all the warnings but we've

play13:41

eliminated all the warnings but we've got another problem here both of these

play13:43

got another problem here both of these

play13:43

got another problem here both of these apis are brand new only available in iOS

play13:46

apis are brand new only available in iOS

play13:46

apis are brand new only available in iOS 17 and Mac OS 14.

play13:49

17 and Mac OS 14.

play13:49

17 and Mac OS 14. uh I just wasn't able to deal with that

play13:51

uh I just wasn't able to deal with that

play13:51

uh I just wasn't able to deal with that so I made the package that includes

play13:53

so I made the package that includes

play13:53

so I made the package that includes backwards compatible versions of these

play13:55

backwards compatible versions of these

play13:55

backwards compatible versions of these two apis you can check out here on

play13:56

two apis you can check out here on

play13:56

two apis you can check out here on GitHub and I also added a whole bunch of

play13:59

GitHub and I also added a whole bunch of

play13:59

GitHub and I also added a whole bunch of other a uh unsafe versions in places

play14:02

other a uh unsafe versions in places

play14:02

other a uh unsafe versions in places where I needed them but Apple has not at

play14:04

where I needed them but Apple has not at

play14:04

where I needed them but Apple has not at least yet added those apis

play14:10

okay there's one more level four problem

play14:10

okay there's one more level four problem that I've run into and it's ordering uh

play14:13

that I've run into and it's ordering uh

play14:13

that I've run into and it's ordering uh and this is pretty fundamental to

play14:15

and this is pretty fundamental to

play14:15

and this is pretty fundamental to programming so how hard could this one

play14:16

programming so how hard could this one

play14:16

programming so how hard could this one be

play14:17

be

play14:17

be well to understand the ordering issues

play14:20

well to understand the ordering issues

play14:20

well to understand the ordering issues you really have to look at what it means

play14:21

you really have to look at what it means

play14:21

you really have to look at what it means to be an async function and so here's

play14:24

to be an async function and so here's

play14:24

to be an async function and so here's one that is not some work function that

play14:26

one that is not some work function that

play14:26

one that is not some work function that has a completion handle of some kind

play14:28

has a completion handle of some kind

play14:28

has a completion handle of some kind and when you make it async you get rid

play14:30

and when you make it async you get rid

play14:30

and when you make it async you get rid of all that and you have just this nice

play14:32

of all that and you have just this nice

play14:32

of all that and you have just this nice simple looking thing but you've really

play14:34

simple looking thing but you've really

play14:34

simple looking thing but you've really you fundamentally changed how this

play14:36

you fundamentally changed how this

play14:36

you fundamentally changed how this function works

play14:38

function works

play14:38

function works and to understand that let's look at the

play14:40

and to understand that let's look at the

play14:40

and to understand that let's look at the call sites so previously you'd call work

play14:42

call sites so previously you'd call work

play14:42

call sites so previously you'd call work and it would take that completion

play14:45

and it would take that completion

play14:45

and it would take that completion Handler but you could call it

play14:46

Handler but you could call it

play14:46

Handler but you could call it synchronously it gave work an

play14:48

synchronously it gave work an

play14:48

synchronously it gave work an opportunity to run code

play14:50

opportunity to run code

play14:50

opportunity to run code now and then also run code when it's

play14:52

now and then also run code when it's

play14:52

now and then also run code when it's finished but once it's async that await

play14:54

finished but once it's async that await

play14:54

finished but once it's async that await keyword means you must have an async

play14:56

keyword means you must have an async

play14:56

keyword means you must have an async context and that removes your ability to

play14:58

context and that removes your ability to

play14:58

context and that removes your ability to run code synchronously right now

play15:01

run code synchronously right now

play15:01

run code synchronously right now this transition from

play15:04

this transition from

play15:04

this transition from sync to async is always necessary and

play15:07

sync to async is always necessary and

play15:07

sync to async is always necessary and depending on what your functions are

play15:08

depending on what your functions are

play15:08

depending on what your functions are doing that can end up being very

play15:11

doing that can end up being very

play15:11

doing that can end up being very perilous

play15:12

perilous

play15:12

perilous so let's look at why some sort of

play15:14

so let's look at why some sort of

play15:14

so let's look at why some sort of timeline here and it's a synchronous

play15:16

timeline here and it's a synchronous

play15:16

timeline here and it's a synchronous timeline of events so before we recall

play15:18

timeline of events so before we recall

play15:18

timeline of events so before we recall in work synchronously and then doing

play15:20

in work synchronously and then doing

play15:20

in work synchronously and then doing some other stuff in the middle here and

play15:22

some other stuff in the middle here and

play15:22

some other stuff in the middle here and then we were going to call work again

play15:25

then we were going to call work again

play15:25

then we were going to call work again now we've made it async which means we

play15:27

now we've made it async which means we

play15:27

now we've made it async which means we can no longer call it this but we have

play15:28

can no longer call it this but we have

play15:28

can no longer call it this but we have to use a weight and to preserve the same

play15:30

to use a weight and to preserve the same

play15:30

to use a weight and to preserve the same ordering in exactly the same way we

play15:32

ordering in exactly the same way we

play15:33

ordering in exactly the same way we really have to do this inside of one

play15:34

really have to do this inside of one

play15:34

really have to do this inside of one single asynchronous context

play15:37

single asynchronous context

play15:37

single asynchronous context but there's all kinds of stuff in the

play15:39

but there's all kinds of stuff in the

play15:39

but there's all kinds of stuff in the middle I mean we could be calling all

play15:40

middle I mean we could be calling all

play15:40

middle I mean we could be calling all kinds of nested functions in different

play15:42

kinds of nested functions in different

play15:42

kinds of nested functions in different subsystems across modules we could even

play15:44

subsystems across modules we could even

play15:44

subsystems across modules we could even be bouncing back and forth between apple

play15:46

be bouncing back and forth between apple

play15:46

be bouncing back and forth between apple Frameworks and our own this can all

play15:48

Frameworks and our own this can all

play15:48

Frameworks and our own this can all happen synchronously but it's just

play15:49

happen synchronously but it's just

play15:49

happen synchronously but it's just totally impractical to have a single

play15:51

totally impractical to have a single

play15:51

totally impractical to have a single context so what do you do you make two

play15:54

context so what do you do you make two

play15:54

context so what do you do you make two and now you have a race because the

play15:57

and now you have a race because the

play15:57

and now you have a race because the system is completely free to start and

play15:59

system is completely free to start and

play15:59

system is completely free to start and even finish task number two before task

play16:01

even finish task number two before task

play16:01

even finish task number two before task one has run

play16:03

one has run

play16:03

one has run the uh the solution here is to make this

play16:08

the uh the solution here is to make this

play16:08

the uh the solution here is to make this dependency explicit so you capture a

play16:10

dependency explicit so you capture a

play16:10

dependency explicit so you capture a reference in the first task and then you

play16:11

reference in the first task and then you

play16:11

reference in the first task and then you await it in the second and while this

play16:13

await it in the second and while this

play16:13

await it in the second and while this does solve the problem it kind of

play16:15

does solve the problem it kind of

play16:15

does solve the problem it kind of suffers from the same drawbacks where

play16:17

suffers from the same drawbacks where

play16:17

suffers from the same drawbacks where passing this task reference around can

play16:20

passing this task reference around can

play16:20

passing this task reference around can be just as impractical and it might be

play16:22

be just as impractical and it might be

play16:22

be just as impractical and it might be possible but it might be at best kind of

play16:25

possible but it might be at best kind of

play16:25

possible but it might be at best kind of inconvenient and at worst just not

play16:27

inconvenient and at worst just not

play16:27

inconvenient and at worst just not something that you can really do

play16:32

now this is the thing you really have to

play16:32

now this is the thing you really have to keep in mind is that when you're

play16:33

keep in mind is that when you're

play16:33

keep in mind is that when you're starting tasks and working with any kind

play16:35

starting tasks and working with any kind

play16:35

starting tasks and working with any kind of state or stateful system you always

play16:37

of state or stateful system you always

play16:37

of state or stateful system you always have to be thinking about races and when

play16:38

have to be thinking about races and when

play16:38

have to be thinking about races and when you're changing things are you changing

play16:40

you're changing things are you changing

play16:40

you're changing things are you changing it from a situation where it needed to

play16:42

it from a situation where it needed to

play16:42

it from a situation where it needed to be run synchronously and that was

play16:43

be run synchronously and that was

play16:43

be run synchronously and that was dependent and now no longer necessary

play16:46

dependent and now no longer necessary

play16:46

dependent and now no longer necessary to fix this your dependencies always

play16:49

to fix this your dependencies always

play16:49

to fix this your dependencies always have to be explicit it is always up to

play16:51

have to be explicit it is always up to

play16:51

have to be explicit it is always up to you and it's much harder in this async

play16:53

you and it's much harder in this async

play16:53

you and it's much harder in this async world to hide that kind of dependency

play16:54

world to hide that kind of dependency

play16:54

world to hide that kind of dependency from callers

play16:56

from callers

play16:57

from callers I have found for myself

play16:58

I have found for myself

play16:58

I have found for myself what I really wanted was like this this

play17:00

what I really wanted was like this this

play17:00

what I really wanted was like this this await keyword I really wanted to just

play17:02

await keyword I really wanted to just

play17:02

await keyword I really wanted to just shove this thing into a queue so that I

play17:04

shove this thing into a queue so that I

play17:04

shove this thing into a queue so that I could control the ordering

play17:06

could control the ordering

play17:06

could control the ordering unfortunately neither dispatch queue nor

play17:09

unfortunately neither dispatch queue nor

play17:09

unfortunately neither dispatch queue nor operation queue support this

play17:11

operation queue support this

play17:11

operation queue support this and so I I made one that does it's on

play17:15

and so I I made one that does it's on

play17:15

and so I I made one that does it's on GitHub

play17:16

GitHub

play17:16

GitHub and I have found these asynchronous cues

play17:18

and I have found these asynchronous cues

play17:18

and I have found these asynchronous cues to be at

play17:20

to be at

play17:20

to be at we're kind of ranging from incredibly

play17:22

we're kind of ranging from incredibly

play17:22

we're kind of ranging from incredibly convenience to absolutely essential and

play17:24

convenience to absolutely essential and

play17:24

convenience to absolutely essential and I think this is another construct that

play17:26

I think this is another construct that

play17:26

I think this is another construct that you should at least be aware of

play17:32

all right here we are maximum fear level

play17:32

all right here we are maximum fear level five scared beakers

play17:35

five scared beakers

play17:35

five scared beakers this is an xcode build setting

play17:38

this is an xcode build setting

play17:38

this is an xcode build setting and what it does is control how strict

play17:41

and what it does is control how strict

play17:41

and what it does is control how strict the Swift compiler is about checking

play17:43

the Swift compiler is about checking

play17:43

the Swift compiler is about checking your use of concurrency

play17:44

your use of concurrency

play17:44

your use of concurrency it has three levels and the default is

play17:47

it has three levels and the default is

play17:47

it has three levels and the default is the lowest and I kind of just left it

play17:49

the lowest and I kind of just left it

play17:49

the lowest and I kind of just left it that way because I figured somebody at

play17:51

that way because I figured somebody at

play17:51

that way because I figured somebody at Apple must have thought this through

play17:53

Apple must have thought this through

play17:53

Apple must have thought this through um

play17:59

and then there was xcode 14.3 beta 1 and

play17:59

and then there was xcode 14.3 beta 1 and that introduced this weird bug that

play18:01

that introduced this weird bug that

play18:01

that introduced this weird bug that bumped this thing up from the default

play18:02

bumped this thing up from the default

play18:02

bumped this thing up from the default minimum to just one level up and that

play18:05

minimum to just one level up and that

play18:05

minimum to just one level up and that introduced warnings in a whole bunch of

play18:07

introduced warnings in a whole bunch of

play18:07

introduced warnings in a whole bunch of people's projects including my own

play18:09

people's projects including my own

play18:09

people's projects including my own and that got me interested in what was

play18:11

and that got me interested in what was

play18:11

and that got me interested in what was going on here so then I kind of I bumped

play18:13

going on here so then I kind of I bumped

play18:13

going on here so then I kind of I bumped it up to the maximum I want to get the

play18:14

it up to the maximum I want to get the

play18:14

it up to the maximum I want to get the full picture and I ended up with 1600

play18:16

full picture and I ended up with 1600

play18:16

full picture and I ended up with 1600 warnings

play18:18

warnings

play18:18

warnings now I have to be fair some of these

play18:20

now I have to be fair some of these

play18:20

now I have to be fair some of these things were really easy to fix now you

play18:22

things were really easy to fix now you

play18:22

things were really easy to fix now you put a main actor over here or you just

play18:23

put a main actor over here or you just

play18:23

put a main actor over here or you just throw like ascendable over there these

play18:25

throw like ascendable over there these

play18:25

throw like ascendable over there these are no big deal and there were some

play18:26

are no big deal and there were some

play18:26

are no big deal and there were some others that I resolved using some of the

play18:28

others that I resolved using some of the

play18:28

others that I resolved using some of the things we just spoke about

play18:30

things we just spoke about

play18:30

things we just spoke about but there was this class of problem that

play18:32

but there was this class of problem that

play18:32

but there was this class of problem that was much more difficult to solve and I

play18:34

was much more difficult to solve and I

play18:34

was much more difficult to solve and I want to talk about that one and I'm

play18:36

want to talk about that one and I'm

play18:36

want to talk about that one and I'm going to use a protocol as an example so

play18:38

going to use a protocol as an example so

play18:38

going to use a protocol as an example so here it is very simple it's got one

play18:40

here it is very simple it's got one

play18:40

here it is very simple it's got one value get her and a Setter and

play18:43

value get her and a Setter and

play18:43

value get her and a Setter and critically there's no Global actor

play18:45

critically there's no Global actor

play18:45

critically there's no Global actor isolation here

play18:47

isolation here

play18:47

isolation here now you're using this protocol on some

play18:49

now you're using this protocol on some

play18:49

now you're using this protocol on some class and you discover that in order to

play18:52

class and you discover that in order to

play18:52

class and you discover that in order to fix other warnings you're going to have

play18:54

fix other warnings you're going to have

play18:54

fix other warnings you're going to have to mark this thing main actor but when

play18:56

to mark this thing main actor but when

play18:56

to mark this thing main actor but when you do that now the compiler is

play18:58

you do that now the compiler is

play18:58

you do that now the compiler is complaining about your conformance and

play19:00

complaining about your conformance and

play19:00

complaining about your conformance and what's happening is you've applied

play19:02

what's happening is you've applied

play19:02

what's happening is you've applied Global actor isolation to the type but

play19:05

Global actor isolation to the type but

play19:05

Global actor isolation to the type but that protocol did not specify actor

play19:08

that protocol did not specify actor

play19:08

that protocol did not specify actor isolation and this is a conflict that is

play19:10

isolation and this is a conflict that is

play19:10

isolation and this is a conflict that is not allowed

play19:11

not allowed

play19:11

not allowed what are your options here well you

play19:13

what are your options here well you

play19:13

what are your options here well you could potentially add main actor to that

play19:16

could potentially add main actor to that

play19:16

could potentially add main actor to that protocol I mean if you look at if you

play19:18

protocol I mean if you look at if you

play19:18

protocol I mean if you look at if you look at UI kit and app kit apple did

play19:20

look at UI kit and app kit apple did

play19:20

look at UI kit and app kit apple did this all over the place that might be

play19:22

this all over the place that might be

play19:22

this all over the place that might be appropriate

play19:23

appropriate

play19:23

appropriate um but you know maybe you use this

play19:25

um but you know maybe you use this

play19:25

um but you know maybe you use this protocol in other places that are not

play19:27

protocol in other places that are not

play19:27

protocol in other places that are not main actor isolated so maybe that's not

play19:29

main actor isolated so maybe that's not

play19:29

main actor isolated so maybe that's not going to work out it can also be a very

play19:31

going to work out it can also be a very

play19:31

going to work out it can also be a very viral change change One's little thing

play19:32

viral change change One's little thing

play19:32

viral change change One's little thing and all of a sudden you have to change

play19:34

and all of a sudden you have to change

play19:34

and all of a sudden you have to change everything so you think okay

play19:37

everything so you think okay

play19:37

everything so you think okay um the other option is uh apply the

play19:40

um the other option is uh apply the

play19:40

um the other option is uh apply the non-isolated keyword this removes actor

play19:42

non-isolated keyword this removes actor

play19:42

non-isolated keyword this removes actor isolation and this also could work but

play19:45

isolation and this also could work but

play19:45

isolation and this also could work but you may need that it could be that it's

play19:47

you may need that it could be that it's

play19:47

you may need that it could be that it's not safe to do this

play19:49

not safe to do this

play19:49

not safe to do this and what's really happening here is

play19:52

and what's really happening here is

play19:52

and what's really happening here is we're discovering that we have this API

play19:53

we're discovering that we have this API

play19:54

we're discovering that we have this API that just doesn't really fit nicely with

play19:56

that just doesn't really fit nicely with

play19:56

that just doesn't really fit nicely with how swift concurrency works and that is

play19:59

how swift concurrency works and that is

play19:59

how swift concurrency works and that is the five beakers of fear issue

play20:03

the five beakers of fear issue

play20:03

the five beakers of fear issue right I have found it easy to make API

play20:06

right I have found it easy to make API

play20:06

right I have found it easy to make API that is just fundamentally incompatible

play20:08

that is just fundamentally incompatible

play20:08

that is just fundamentally incompatible with concurrency

play20:10

with concurrency

play20:10

with concurrency and how how did this happen well the

play20:13

and how how did this happen well the

play20:13

and how how did this happen well the reason it happened is I had this build

play20:16

reason it happened is I had this build

play20:16

reason it happened is I had this build setting

play20:17

setting

play20:17

setting turn to the minimum so I was building up

play20:19

turn to the minimum so I was building up

play20:19

turn to the minimum so I was building up this mental model of how swift

play20:21

this mental model of how swift

play20:21

this mental model of how swift concurrency Works where nobody was

play20:22

concurrency Works where nobody was

play20:22

concurrency Works where nobody was checking that I was doing it correctly

play20:25

checking that I was doing it correctly

play20:25

checking that I was doing it correctly uh and that's this is dangerous because

play20:28

uh and that's this is dangerous because

play20:28

uh and that's this is dangerous because I really need you to start turning these

play20:30

I really need you to start turning these

play20:30

I really need you to start turning these things on so you can find the problems

play20:32

things on so you can find the problems

play20:33

things on so you can find the problems in your code now

play20:34

in your code now

play20:34

in your code now and it is true that this is a little bit

play20:37

and it is true that this is a little bit

play20:37

and it is true that this is a little bit in flux there's a lot of things changing

play20:39

in flux there's a lot of things changing

play20:39

in flux there's a lot of things changing including Apple's own API so you may

play20:41

including Apple's own API so you may

play20:41

including Apple's own API so you may uncover warnings that are either going

play20:43

uncover warnings that are either going

play20:43

uncover warnings that are either going to be fixed or maybe you're going to uh

play20:45

to be fixed or maybe you're going to uh

play20:45

to be fixed or maybe you're going to uh by the um by the Frameworks or maybe by

play20:47

by the um by the Frameworks or maybe by

play20:47

by the um by the Frameworks or maybe by the language itself but I would rather

play20:49

the language itself but I would rather

play20:49

the language itself but I would rather you know about those things now so that

play20:51

you know about those things now so that

play20:51

you know about those things now so that you can come up with a plan for how

play20:53

you can come up with a plan for how

play20:53

you can come up with a plan for how you're going to address them and avoid

play20:55

you're going to address them and avoid

play20:55

you're going to address them and avoid building things that are just

play20:56

building things that are just

play20:56

building things that are just fundamentally not going to work

play20:58

fundamentally not going to work

play20:58

fundamentally not going to work uh and this is going to be necessary

play20:59

uh and this is going to be necessary

play20:59

uh and this is going to be necessary because remember that in Swift 6 we're

play21:02

because remember that in Swift 6 we're

play21:02

because remember that in Swift 6 we're going from a state of no warnings at all

play21:04

going from a state of no warnings at all

play21:04

going from a state of no warnings at all to errors so I think you want a little

play21:06

to errors so I think you want a little

play21:06

to errors so I think you want a little bit of early warning here

play21:11

and that's my that's it thank you very

play21:11

and that's my that's it thank you very much

play21:13

much

play21:13

much thank you

Rate This

5.0 / 5 (0 votes)

関連タグ
Swift ConcurrencyProgrammingConcurrency IssuesCode ExamplesTechnical PresentationDeveloper InsightsAsync ChallengesActor ModelConcurrency BugsSwift Language
英語で要約が必要ですか?