WEBVTT

NOTE
This file was generated by Descript <www.descript.com>

00:00:13.674 --> 00:00:15.364
<v Amanda Majorowicz>This
is Self-Directed Research.

00:00:15.564 --> 00:00:18.414
James and Amos, our hosts, get
really hyped about different

00:00:18.414 --> 00:00:21.334
things, and each week they take
turns presenting their hot takes.

00:00:21.706 --> 00:00:24.426
This week's discussion didn't follow
the slide deck completely, but it's

00:00:24.426 --> 00:00:26.976
available for your entertainment
and curiosity nonetheless.

00:00:27.433 --> 00:00:32.223
Check it out at sdr-podcast.com/episodes
to see the presentation for each topic,

00:00:32.323 --> 00:00:34.023
as well as the show notes and transcripts.

00:00:34.453 --> 00:00:36.363
New episodes are
published every Wednesday.

00:00:36.933 --> 00:00:40.113
Once again, thank you to Ladybird web
browser for sponsoring this episode.

00:00:40.588 --> 00:00:42.818
For more information, listen to the
end of the episode and check out

00:00:42.818 --> 00:00:44.328
the description and our show notes.

00:00:44.808 --> 00:00:47.758
This week, Amos presents,
"You Might Not Need Arc<T>".

00:00:48.328 --> 00:00:49.238
<v Amos Wenger>Hey everyone, Amos here.

00:00:49.238 --> 00:00:51.938
Just wanted to apologize for the
audio quality of this episode: I had

00:00:51.938 --> 00:00:54.688
my gain set up too high, I was too
far away from the mic, there was AC

00:00:54.688 --> 00:00:57.788
in the background, we had to get rid
of echo, had to get rid of clipping.

00:00:57.958 --> 00:00:59.438
You can hear that it's heavily processed.

00:00:59.448 --> 00:01:02.098
It was interesting, so we
didn't want to throw away the

00:01:02.108 --> 00:01:03.998
episode or record it over again.

00:01:04.268 --> 00:01:05.818
It gets better in the next few episodes.

00:01:05.818 --> 00:01:07.168
So thanks for your patience.

00:01:07.418 --> 00:01:08.048
I hear you.

00:01:08.288 --> 00:01:08.888
I love you.

00:01:09.535 --> 00:01:09.765
G'bye.

00:01:14.872 --> 00:01:17.242
<v James Munns>I'm, I'm
digging the new slide format.

00:01:17.252 --> 00:01:19.402
You went very like, uh, what is it?

00:01:19.462 --> 00:01:22.972
American Psycho eggshell
and embossed print on us.

00:01:23.092 --> 00:01:25.672
<v Amos Wenger>Oh, that's
what the, the GIF was about!

00:01:25.672 --> 00:01:25.792
<v James Munns>Oh, yeah.

00:01:27.192 --> 00:01:29.012
<v Amos Wenger>I didn't know
how to interpret that.

00:01:29.032 --> 00:01:30.172
And I was too scared to ask.

00:01:30.592 --> 00:01:33.332
It is one of the built-in Keynote themes.

00:01:33.722 --> 00:01:36.102
Now you might be wondering:
"Amos, why are you using Keynote?

00:01:36.102 --> 00:01:37.502
Do you, do you love Apple that much?"

00:01:37.502 --> 00:01:41.542
No, but I do hate Google and its
Slides product that doesn't let you

00:01:41.542 --> 00:01:43.868
paste any vector format in there.

00:01:44.283 --> 00:01:45.603
So you can't paste SVG.

00:01:46.163 --> 00:01:49.943
It prints an error about
EPS: embedded PostScript.

00:01:50.463 --> 00:01:51.803
Which I know what that is-

00:01:51.887 --> 00:01:53.277
<v James Munns>The other vector format.

00:01:53.277 --> 00:01:55.243
<v Amos Wenger>I don't know what
the fuck that's doing in their

00:01:55.253 --> 00:01:57.023
dialogue, but I know what that is.

00:01:57.023 --> 00:01:57.363
Yeah.

00:01:57.838 --> 00:02:01.498
So apparently there's like a
13-step workaround to still import

00:02:01.548 --> 00:02:03.048
vector things in Google Slides.

00:02:03.048 --> 00:02:07.261
And of course, everybody who wants
to get stuff done, just rasterizes it

00:02:07.331 --> 00:02:09.331
and gets a huge PNG in there instead.

00:02:09.331 --> 00:02:11.401
But I don't want my
presentations to be like.

00:02:11.746 --> 00:02:13.086
Really large, I don't know.

00:02:13.400 --> 00:02:13.840
<v James Munns>Gotcha.

00:02:13.937 --> 00:02:14.267
Okay.

00:02:14.487 --> 00:02:15.457
I'm excited about this.

00:02:15.857 --> 00:02:16.287
<v Amos Wenger>Cool!

00:02:16.897 --> 00:02:17.807
So should I just go?

00:02:17.867 --> 00:02:19.817
I, I'm gonna share my screen, I suppose.

00:02:20.265 --> 00:02:21.735
I can't see y'all anymore.

00:02:21.795 --> 00:02:23.015
This is just a tiny laptop.

00:02:23.565 --> 00:02:25.565
Okay, I guess I will do this one blind.

00:02:25.850 --> 00:02:26.360
That's okay.

00:02:27.733 --> 00:02:31.433
Today I want to talk about
Arc<Mutex<T>> and ways to avoid it.

00:02:31.443 --> 00:02:33.123
First off, James, how do you say mutex?

00:02:33.283 --> 00:02:34.253
Do you say "mutt-ex"?

00:02:34.872 --> 00:02:35.073
"Mew-tek"?

00:02:35.133 --> 00:02:35.693
<v James Munns>I say "mew-tex."

00:02:35.693 --> 00:02:37.233
<v Amos Wenger>You say "mew-tex!"

00:02:37.323 --> 00:02:41.709
<v James Munns>'mu' as in like the SI
prefix and then 'tex' as in Texas.

00:02:41.783 --> 00:02:44.343
<v Amos Wenger>I noticed you say
"mutt", you don't say "mute" for

00:02:44.533 --> 00:02:46.293
references, exclusive references.

00:02:46.568 --> 00:02:49.278
<v James Munns>There's a couple things that
I feel like I say contextually different.

00:02:49.288 --> 00:02:51.198
Like mut is definitely one.

00:02:51.998 --> 00:02:52.698
There's a bunch.

00:02:52.698 --> 00:02:56.458
Like, Rust has a bunch of
non-word  abbreviations.

00:02:56.523 --> 00:02:57.323
<v Amos Wenger>That is true.

00:02:57.563 --> 00:03:00.353
It's a struggle when I make videos,
I have to pick one and stick to it.

00:03:00.453 --> 00:03:01.263
<v James Munns>Heh heh heh heh heh

00:03:01.498 --> 00:03:04.448
<v Amos Wenger>And I think I'm switching
from 'immutable reference' and

00:03:04.448 --> 00:03:08.028
'mutable reference' to 'exclusive
reference' and 'shared reference'.

00:03:08.388 --> 00:03:10.408
Even though those don't
have the keyword in there.

00:03:11.078 --> 00:03:14.144
<v James Munns>Yeah, Aleksey is the one
that, when we were teaching together,

00:03:14.144 --> 00:03:17.994
he's the one that got me on that because
he's like, it's better because one of

00:03:17.994 --> 00:03:21.734
the things we always had to teach when we
were teaching was intermutability, which

00:03:21.744 --> 00:03:25.264
immediately breaks like the immutable
reference and mutable reference thing

00:03:25.274 --> 00:03:29.024
because you're passing around an immutable
reference to something that you mutate-

00:03:29.054 --> 00:03:29.604
<v Amos Wenger>Yeah, yeah yeah.

00:03:29.784 --> 00:03:31.284
<v James Munns>Inside of it, like a mutex.

00:03:31.409 --> 00:03:32.189
<v Amos Wenger>Also...

00:03:32.279 --> 00:03:37.599
yeah, also, because of Rust's, like,
mutability XOR sharing property, you

00:03:37.599 --> 00:03:41.809
sometimes use mutable references just
to enforce some other invariants.

00:03:41.819 --> 00:03:43.959
You don't actually care about
mutating it, but it's just a way

00:03:43.959 --> 00:03:45.229
to only have one of something.

00:03:45.829 --> 00:03:48.769
Um, anyway, that was the
first, the title slide.

00:03:49.179 --> 00:03:51.019
Uh, let's get going.

00:03:51.299 --> 00:03:56.959
So the thing with Arc<Mutex<T>> or
Arc<RWLock<T>> or things like that is

00:03:56.969 --> 00:03:59.690
that Rust beginners use a lot of it.

00:03:59.690 --> 00:04:02.960
Like we recommend Rust beginners
use it to get out of situations and

00:04:02.960 --> 00:04:04.300
not have to think about lifetimes.

00:04:04.710 --> 00:04:09.160
And I think that's good, but there
comes a time where you want to get

00:04:09.160 --> 00:04:11.950
rid of them and I want to talk about
some of the alternatives you can use

00:04:12.170 --> 00:04:14.740
specifically in the context of my website.

00:04:15.330 --> 00:04:22.360
My website, uh, fasterthanli.me is
18,000 lines of Rust, which I realize

00:04:22.360 --> 00:04:24.430
actually doesn't sound like a big
number, but it is a big number.

00:04:24.440 --> 00:04:27.340
And when you have to maintain it,
especially when a lot of it is async Rust

00:04:27.340 --> 00:04:32.190
code and it mixes, uh, sync and async-
I started writing it four years ago, and

00:04:32.190 --> 00:04:33.790
I've been maintaining it more or less.

00:04:33.790 --> 00:04:36.480
Like every couple of months I go
like, "Oh, there's new major versions

00:04:36.480 --> 00:04:38.020
of all the crates I rely on."

00:04:38.540 --> 00:04:41.860
There's lots of synchronous code and
asynchronous code that are mixed up,

00:04:41.900 --> 00:04:44.230
but that's, that's not today's topic.

00:04:44.340 --> 00:04:45.750
Short version is just use channels.

00:04:46.450 --> 00:04:49.480
[laughter] And, uh, it's,
it's pretty typical.

00:04:49.510 --> 00:04:51.110
I do weird things with HTTP.

00:04:51.110 --> 00:04:54.680
I have, um HTTP implementation based on
io_uring, which I'm getting funding for.

00:04:54.880 --> 00:04:56.780
But my website does not use it yet.

00:04:56.880 --> 00:05:00.560
I am planning on using that, but
because my website is web application,

00:05:00.560 --> 00:05:02.260
it's not directly facing the web.

00:05:02.290 --> 00:05:06.870
It is actually on Kubernetes with
a reverse proxy in front, I don't

00:05:06.870 --> 00:05:08.470
actually need to care about HTTPS.

00:05:08.770 --> 00:05:12.720
I don't need to care about HTTP/2,
I just do plain text HTTP/1 and

00:05:12.720 --> 00:05:15.690
then the reverse proxy in front
takes care of everything else.

00:05:15.850 --> 00:05:19.520
So my plan is to actually use my
own HTTP implementation for the

00:05:19.520 --> 00:05:21.120
reverse proxy as part of Kubernetes.

00:05:21.220 --> 00:05:24.520
So my website is just axum on `tokio`.

00:05:24.750 --> 00:05:30.055
I used to use `warp`, which is
also based on `tokio`, but has

00:05:30.055 --> 00:05:32.745
weird combinators that I didn't
like and blew up compilation times.

00:05:33.220 --> 00:05:38.190
And before that, I used `tide`, which
is in the `async-std` ecosystem,

00:05:38.260 --> 00:05:39.660
let's say- cinematic universe.

00:05:39.970 --> 00:05:40.710
I don't know if you remember that.

00:05:40.720 --> 00:05:43.980
Have you ever used, uh `tide` and
`surf` and all that stuff, James?

00:05:44.805 --> 00:05:45.735
<v James Munns>I'm definitely
familiar with it.

00:05:45.755 --> 00:05:49.265
When I was at Ferrous, we were, I
think, the one company that funded

00:05:49.275 --> 00:05:52.205
`async-std` because the people who
were working on it worked at Ferrous.

00:05:52.225 --> 00:05:55.805
So, I'm familiar with it, but this
was before I did a lot of web stuff.

00:05:56.055 --> 00:05:58.825
But I was wondering if there's a
specific French word for dogfooding.

00:05:59.180 --> 00:06:00.420
<v Amos Wenger>Not that I know of...

00:06:00.810 --> 00:06:04.270
but I don't really talk
about my work in French.

00:06:04.710 --> 00:06:07.570
That would require going out of the
house and talking to other developers,

00:06:07.580 --> 00:06:10.790
which I do on occasion, but it's
mostly an excuse to drink alcohol and

00:06:10.790 --> 00:06:12.690
in like socially acceptable setting.

00:06:13.220 --> 00:06:13.610
I don't know.

00:06:13.610 --> 00:06:15.250
<v James Munns>I was gonna
say the same in German.

00:06:15.250 --> 00:06:18.490
Like, my restaurant German is way better
than my technical German because all

00:06:18.500 --> 00:06:22.070
the technical stuff that I do, even
locally, everyone's speaking English for.

00:06:22.130 --> 00:06:22.960
So, yeah.

00:06:23.010 --> 00:06:23.340
Okay.

00:06:23.610 --> 00:06:24.510
We'll stick with dogfooding.

00:06:25.985 --> 00:06:28.665
<v Amos Wenger>So `tokio`
works with a thread pool.

00:06:28.962 --> 00:06:30.422
What that means is...

00:06:30.792 --> 00:06:34.894
let me- I want to try and give a short
explanation, but I'm in danger of James

00:06:34.894 --> 00:06:37.894
jumping in and correcting me at any
time, which is good- a good thing, this

00:06:37.894 --> 00:06:39.684
is why we have two hosts, I suppose.

00:06:40.031 --> 00:06:44.860
But basically what it means is that:
you can do work in the background.

00:06:44.860 --> 00:06:50.400
You can spawn `tokio` tasks like you
could spawn libstd, "lib stud" threads

00:06:50.410 --> 00:06:52.620
for the operating system level threads.

00:06:52.947 --> 00:06:58.147
But then because those tasks are
futures, they can be polled, they

00:06:58.147 --> 00:07:01.427
can return saying, poll me later
when something else has happened.

00:07:01.832 --> 00:07:07.052
There's no- there's nothing saying
that the poll function of the future

00:07:07.052 --> 00:07:11.712
trait of that future will be called
from the same thread the next time.

00:07:12.302 --> 00:07:13.582
So they can move across threads.

00:07:13.592 --> 00:07:17.082
So they are `Send` by definition, which
is a huge headache because there's a

00:07:17.082 --> 00:07:20.082
lot of things that aren't `Send` or a
lot of things that aren't `Sync` and

00:07:20.102 --> 00:07:21.452
you want to hold a reference to them.

00:07:21.732 --> 00:07:24.042
So that kind of restricts what you do.

00:07:24.222 --> 00:07:28.692
And specifically in `tokio` codebases
with that model, you do end up having a

00:07:28.692 --> 00:07:37.067
lot Arc all the things, because you want
several tasks to hold a reference to the

00:07:37.087 --> 00:07:41.527
configuration of your server or reference
to any sort of context, any sort of

00:07:41.527 --> 00:07:46.537
secrets, anything, anything that like you
might want to access in all the workers

00:07:46.537 --> 00:07:48.427
that service HTTP requests, for example.

00:07:48.427 --> 00:07:49.447
James, do you have any thoughts so far?

00:07:49.557 --> 00:07:50.307
I'm catching my breath.

00:07:51.107 --> 00:07:51.917
<v James Munns>Nah, you nailed it.

00:07:51.967 --> 00:07:56.317
I was gonna say, it's a, the "has to
be `Send`" is a `tokio` specificism,

00:07:56.327 --> 00:07:59.457
just because they use, like you said,
thread-stealing executors, where if

00:07:59.457 --> 00:08:03.327
one worker thread gets really busy,
then the other ones can come along and

00:08:03.327 --> 00:08:04.727
go, "I'll take that off your plate."

00:08:04.767 --> 00:08:07.277
And so stuff doesn't get
stalled for a very long time.

00:08:07.277 --> 00:08:12.217
It's not specific, or it's not required by
Rust async, but for `tokio`, it's a design

00:08:12.217 --> 00:08:15.327
decision they make because they have...

00:08:15.327 --> 00:08:16.947
essentially, they looked at
a lot of different people.

00:08:16.957 --> 00:08:17.567
Well, I don't know.

00:08:17.617 --> 00:08:18.587
I don't need to go into why.

00:08:18.797 --> 00:08:21.827
But yeah, it's a design decision they
made, which is usually pretty reasonable.

00:08:21.827 --> 00:08:25.657
But it is a point of a lot
of half-informed blog posts

00:08:25.657 --> 00:08:26.897
discussing that decision.

00:08:27.687 --> 00:08:28.397
<v Amos Wenger>That is true.

00:08:28.417 --> 00:08:31.857
Every, every month at least, every
couple of weeks there's a new

00:08:31.857 --> 00:08:33.787
article saying why Rust async is bad.

00:08:33.787 --> 00:08:37.917
But that is, that is a `tokio` specific
thing or like this style executor.

00:08:37.917 --> 00:08:42.011
The alternative being thread-per-core
pretty much where you, you have, can

00:08:42.011 --> 00:08:46.011
imagine, a current thread executor per
CPU core, however many cores you want

00:08:46.011 --> 00:08:50.071
to dedicate to the app, and then you
explicitly pass things between those.

00:08:50.071 --> 00:08:53.206
You do message passing, for example,
if you want to have a central, I

00:08:53.206 --> 00:08:54.666
don't know, API, that you'll query.

00:08:54.666 --> 00:08:56.766
If you want to share something,
you usually do message passing...

00:08:56.973 --> 00:08:57.983
it's a different take on it.

00:08:58.144 --> 00:08:59.884
<v James Munns>I know Boats has a
better name than thread-per-core.

00:08:59.884 --> 00:09:02.474
Because thread-per-core Because like,
`tokio` does have a thread-per-core.

00:09:02.494 --> 00:09:05.214
It's just a worker thread, and
stuff can move between that.

00:09:05.214 --> 00:09:07.664
I guess, like, there's
like Glommio, I think...

00:09:07.764 --> 00:09:10.169
<v Amos Wenger>`tokio` can
have less or more or...

00:09:10.169 --> 00:09:10.859
<v James Munns>Yeah, it's true...

00:09:10.976 --> 00:09:13.416
<v Amos Wenger>Like it, it
manages the pool automatically.

00:09:13.416 --> 00:09:16.096
And then it has blocking threads,
which is a separate pool and not

00:09:16.096 --> 00:09:18.616
really a pool because you can have
any number of them if you just do

00:09:18.616 --> 00:09:20.196
a lot of file reads, for example.

00:09:20.409 --> 00:09:21.989
Anyway, we're trying to get rid of Arc.

00:09:22.779 --> 00:09:24.329
So let me talk about the alternative.

00:09:24.329 --> 00:09:27.749
The first, the first thing is,
for example, for my website.

00:09:27.954 --> 00:09:31.824
There's a config, it's a bunch of
jsonc, I think, it's parsed with

00:09:31.824 --> 00:09:33.984
`serde`, but it never changes.

00:09:33.994 --> 00:09:38.354
Sometimes you need to reload the config
in some codebases, because you have a,

00:09:38.684 --> 00:09:40.114
yeah, dynamic reloading configuration.

00:09:40.114 --> 00:09:42.654
In that case, you might want to
reach for something like `arc-swap`.

00:09:42.784 --> 00:09:44.954
which you just told me about,
I just remembered about.

00:09:45.344 --> 00:09:47.414
But in my case, it never ever changes.

00:09:47.561 --> 00:09:48.841
And it's a long-running program.

00:09:48.841 --> 00:09:52.371
We need the config from the very
start to the very end of the program.

00:09:52.721 --> 00:09:53.581
So you can just leak it.

00:09:53.591 --> 00:09:56.681
You just put it on the heap with
`Box::new`, and then do `Box::leak`,

00:09:56.701 --> 00:10:01.821
and then you get a "mut", uh, well,
an exclusive reference to it, that

00:10:01.821 --> 00:10:04.601
has lifetime `'static` and then
you can just have that everywhere.

00:10:04.621 --> 00:10:08.711
You can have a global function that
returns an immutable reference to that

00:10:08.711 --> 00:10:12.361
and that's something you don't need
to pass everywhere and you know that

00:10:12.381 --> 00:10:16.581
except for the first few milliseconds
of the program it is always initialized.

00:10:16.774 --> 00:10:20.444
The other thing is that, you know, as a
beginner you might naturally reach for

00:10:20.444 --> 00:10:24.469
`Arc<Mutex<T>>` or `Arc<RwLock<T>>` But
if you don't actually ever change it,

00:10:24.479 --> 00:10:26.359
you could just do `Arc<T>` directly.

00:10:26.359 --> 00:10:28.983
You don't need to wrap it in
some sort of locking thing.

00:10:28.983 --> 00:10:30.123
It's not a magical combo.

00:10:30.143 --> 00:10:31.673
They can be used separately.

00:10:32.763 --> 00:10:37.463
Next up, if your value fits in an atomic,
for example, on my website I have a dev

00:10:37.463 --> 00:10:40.253
environment and a production environment
that's part of a config, but it's also

00:10:40.253 --> 00:10:41.883
handy to have in various functions.

00:10:41.893 --> 00:10:45.266
For example, in development I
show stack traces, in production

00:10:45.466 --> 00:10:45.466
I

00:10:45.591 --> 00:10:46.591
<v Amos Wenger>set secure cookies.

00:10:47.208 --> 00:10:51.888
If it's just a boolean, you can fit any
`u8`, you have an atomic `u8` type in the

00:10:51.888 --> 00:10:54.019
standard library, you can just do that.

00:10:54.169 --> 00:10:57.069
You don't need an Arc, you don't
need reference counting at all,

00:10:57.179 --> 00:11:00.959
it's just a `u8` somewhere that you
write to and read from atomically.

00:11:01.632 --> 00:11:08.085
The trick I do want to talk about today
is: if your global does not change while

00:11:08.085 --> 00:11:11.416
blocking, then you can use a thread-local.

00:11:11.436 --> 00:11:12.886
<v James Munns>What do you mean by blocking?

00:11:13.032 --> 00:11:15.932
Do you want to describe to me the use
case that you usually use this for?

00:11:15.962 --> 00:11:20.482
Is it easier to work backwards from
reality rather than trying to teach it?

00:11:20.482 --> 00:11:23.297
<v Amos Wenger>Yes, So let's say
you have an async task and you're

00:11:23.297 --> 00:11:24.687
going to call a blocking function.

00:11:24.697 --> 00:11:30.507
My use case is I have a bunch of SASS
files in my website that define style

00:11:30.527 --> 00:11:34.687
sheets, they compile down to CSS and
I use a crate called `grass_compiler`.

00:11:34.687 --> 00:11:39.362
`grass_compiler` lets you define
functions that you can call from SASS

00:11:39.632 --> 00:11:42.242
and they end up being Rust functions
so they can evaluate to anything

00:11:42.242 --> 00:11:43.615
you can call anything from it.

00:11:43.975 --> 00:11:46.855
But those Rust functions
are freestanding functions.

00:11:46.865 --> 00:11:47.725
They're not closures.

00:11:47.725 --> 00:11:49.305
You cannot capture any context.

00:11:49.305 --> 00:11:50.655
So in my case, that doesn't work.

00:11:50.685 --> 00:11:53.365
Maybe I need to do a database
query to figure out the

00:11:53.375 --> 00:11:55.795
result of the function call.

00:11:55.835 --> 00:11:58.025
I need to know whether we're
in development or environment.

00:11:58.485 --> 00:11:59.875
I need to know a lot of things.

00:12:00.175 --> 00:12:02.055
So I need to pass in context somehow.

00:12:02.055 --> 00:12:06.061
And I need, I used to do something
really dirty, which is I had a process

00:12:06.101 --> 00:12:10.131
wide lock, and then when you compile
the CSS, it first acquired that

00:12:10.131 --> 00:12:12.901
lock and then wrote something in a
process global, and then called the

00:12:12.901 --> 00:12:14.441
blocking function and then freed it.

00:12:14.651 --> 00:12:18.698
But then the insight was that even
though we're in an async task, as

00:12:18.698 --> 00:12:21.988
long as we're being polled, as long as
we don't yield back to the executor,

00:12:22.008 --> 00:12:26.098
which we can only do by returning,  we
own the current thread pretty much.

00:12:26.118 --> 00:12:27.008
It's ours.

00:12:27.738 --> 00:12:29.388
There's only two ways
we're letting go of it.

00:12:29.398 --> 00:12:33.808
Is that if we return with `Pending::Poll`
or pen, uh, what is the name of the...

00:12:34.478 --> 00:12:34.988
`Poll::Pending` or

00:12:34.988 --> 00:12:37.103
<v James Munns>`Poll::Pending`
or `Poll::Ready` or whatever.

00:12:37.528 --> 00:12:40.368
<v Amos Wenger>Or if we
panic and then we unwind.

00:12:40.618 --> 00:12:42.378
If we, if we abort,
that's not our problem.

00:12:42.378 --> 00:12:43.037
We can't deal with that.

00:12:43.147 --> 00:12:43.537
So.

00:12:44.407 --> 00:12:47.877
We're holding onto this thread, which
means we can set a thread-local and

00:12:47.877 --> 00:12:54.787
then from the Rust function that's been
registered as a `grass` extension, we

00:12:54.787 --> 00:12:58.707
can read that — as  long as we clean
up things properly and, and to cover

00:12:58.707 --> 00:13:03.757
both the return and the panic unwind
case, we can just have a guard struct

00:13:03.957 --> 00:13:06.807
that has a `Drop` implementation
that clears the thread-local when

00:13:06.807 --> 00:13:09.657
it's dropped, which happens, yeah,
either on panic or on return.

00:13:10.377 --> 00:13:13.297
<v James Munns>So the trick is that
you're calling the compilation, which

00:13:13.297 --> 00:13:16.757
might then call a bunch of other
functions like what is my base address?

00:13:16.787 --> 00:13:17.767
Am I in prod or not?

00:13:18.017 --> 00:13:21.177
So essentially you need to populate
all the settings when you start the

00:13:21.177 --> 00:13:25.267
compilation, use those values while
you're compiling one thing and then when

00:13:25.267 --> 00:13:29.037
you're done compiling one thing — which
is one big blocking operation — then

00:13:29.037 --> 00:13:33.307
you just purge the settings cache,
which is a thread-local, which means you

00:13:33.307 --> 00:13:38.047
were able to stuff context in, retrieve
it back out from the free functions.

00:13:38.327 --> 00:13:41.077
And then when you're done with the
whole compilation you go: "Cool!

00:13:41.117 --> 00:13:41.527
Just...

00:13:41.527 --> 00:13:42.207
wipe that away."

00:13:42.207 --> 00:13:45.717
So the next time I'm compiling
something else, I don't get stale

00:13:46.132 --> 00:13:49.322
"cache data" from compiling some
other page or something like that.

00:13:49.457 --> 00:13:51.887
<v Amos Wenger>I, I, I'm not
comfortable calling it a cache.

00:13:51.897 --> 00:13:55.717
I am comfortable calling it context,
which I also believe is what you

00:13:55.717 --> 00:13:57.757
would call it when `tokio` does it.

00:13:57.807 --> 00:14:00.987
I have a slide that says, uh, "implicit
context is bad unless I'm the one

00:14:00.987 --> 00:14:05.857
doing it," which is definitely the
way `tokio` does feel, but, uh,

00:14:05.857 --> 00:14:09.457
yeah, that's the first bit of the
trick, which is not a trick at all.

00:14:09.457 --> 00:14:12.157
It's just realizing, oh, we have a
thread, we can use thread-locals  and

00:14:12.157 --> 00:14:16.367
multiple concurrent invocations of
CSS compilation from different tasks

00:14:16.407 --> 00:14:19.417
are not going to interfere with each
other because they are currently

00:14:19.437 --> 00:14:21.127
being polled from different threads.

00:14:21.257 --> 00:14:22.437
So it all works.

00:14:22.887 --> 00:14:27.827
The next insight is usually you can do
that and that's fine, but you still need

00:14:27.837 --> 00:14:31.467
the thing you put in the thread-local
to be `'static`,  to be owned.

00:14:31.787 --> 00:14:35.067
So you can have an `Arc<S>` where `S` is
a struct that has strings and then you

00:14:35.067 --> 00:14:38.547
have a reference counted struct with a
bunch of strings or you could like put

00:14:38.547 --> 00:14:42.267
the struct itself which has the strings
so you're cloning every string which might

00:14:42.267 --> 00:14:44.457
be better for cache locality or something.

00:14:44.827 --> 00:14:45.967
But I don't want to do that!

00:14:46.097 --> 00:14:47.037
I don't need to do that!

00:14:47.157 --> 00:14:53.667
Because  as long as nothing is changing
the thread-local while we block, so

00:14:53.667 --> 00:14:57.147
like we're doing CSS compilation, it
only ever reads from the thread-local,

00:14:57.167 --> 00:15:02.784
it never modifies it and nothing else
modifies the thing it's pointing to, then

00:15:02.934 --> 00:15:09.707
I think, I think it's the only, like one
of the only legitimate use cases to cast

00:15:09.857 --> 00:15:12.827
a lifetime away, if that makes sense?

00:15:12.877 --> 00:15:15.782
<v James Munns>Oh no, Amos, this
got very serious very quickly.

00:15:16.287 --> 00:15:19.534
<v Amos Wenger>Because hear me out: uh,
casting pointer types and like references

00:15:19.534 --> 00:15:21.152
and stuff is generally a bad idea.

00:15:21.152 --> 00:15:21.682
Don't do it.

00:15:22.126 --> 00:15:25.296
Definitely creating an exclusive
reference from a shared reference.

00:15:25.306 --> 00:15:27.926
So going from like `const`
to `mut` is a big no-no.

00:15:28.116 --> 00:15:31.956
Because the compiler will create
optimizations because it will assume

00:15:31.956 --> 00:15:33.236
that you're not mutating anything.

00:15:33.476 --> 00:15:34.896
And so that is definitely a big no-no.

00:15:34.916 --> 00:15:37.393
And, generally: pointers
are unsafe for a reason.

00:15:37.393 --> 00:15:39.313
There's a lot of ways to get it wrong.

00:15:39.323 --> 00:15:39.603
But.

00:15:40.114 --> 00:15:41.964
I do believe this is fine.

00:15:42.184 --> 00:15:45.254
And I have a "(show code)" slide
here because, ironically, perhaps my,

00:15:45.264 --> 00:15:46.574
my website was broken at this time.

00:15:46.574 --> 00:15:48.104
I was going through a big refactoring.

00:15:48.434 --> 00:15:50.034
I do believe it is sound.

00:15:50.114 --> 00:15:51.854
<v James Munns>So, okay: let me make
sure I understand this right..

00:15:51.854 --> 00:15:53.249
So you have sort of like the...

00:15:53.389 --> 00:15:57.199
the load context step before you do
a compilation, which goes and makes

00:15:57.199 --> 00:16:00.649
all the database queries and things
like that and stores it into a, either

00:16:00.649 --> 00:16:06.909
like a stack local `&str` or a buffer
or something like that, and then what

00:16:06.909 --> 00:16:12.439
you're actually smuggling into the
thread-local is a pointer to something.

00:16:12.719 --> 00:16:15.529
So you're pretending that it's 'static
because it's a pointer inside of

00:16:15.529 --> 00:16:21.359
the thread-local, so that whenever
anyone accesses it, it's acting...

00:16:21.572 --> 00:16:22.072
I don't know.

00:16:22.142 --> 00:16:24.932
Bump allocator is not the right word,
but something that you go, "Well, it's

00:16:24.932 --> 00:16:26.272
going to live as long as it lives.

00:16:26.272 --> 00:16:30.012
And I know that I'm gonna
manually handle invalidation."

00:16:30.012 --> 00:16:32.592
So I'm going to go back to the cache
metaphor that you don't like, but I'm

00:16:32.592 --> 00:16:37.492
going to manually invalidate the cache
because when I leave the context of a

00:16:37.492 --> 00:16:41.672
single thread, so you're blocking code
is acting like a guard on, "I'm not

00:16:41.672 --> 00:16:43.232
going to be moved to another thread"..

00:16:43.242 --> 00:16:48.522
and when you leave that context, you
wipe the cache or invalidate the cache

00:16:48.522 --> 00:16:49.692
or the context and things like that.

00:16:49.742 --> 00:16:51.378
So, okay...

00:16:51.648 --> 00:16:52.628
I'm with you I think.

00:16:53.653 --> 00:16:56.863
<v Amos Wenger>So the `thread_local!`
macro from `std` gives you...

00:16:57.053 --> 00:16:59.043
you don't see it in the declaration.

00:16:59.518 --> 00:17:03.188
But it actually wraps whatever
type you give it into a `LocalKey`.

00:17:03.768 --> 00:17:07.578
Which, I've become very intimate with as
I try to do things across shared objects.

00:17:07.588 --> 00:17:08.738
So I, I know what's in there.

00:17:08.748 --> 00:17:11.948
It's just a struct with a function that
gets the address of the `ThreadLocal`.

00:17:11.948 --> 00:17:16.238
But, then the `ThreadLocal` type
has a bunch of methods defined.

00:17:16.288 --> 00:17:19.958
Some of them are only available if
the type inside of it is a `RefCell`.

00:17:20.538 --> 00:17:22.128
And so I have a RefCell in there.

00:17:22.128 --> 00:17:28.468
So the actual type is
`LocalKey<RefCell<Option<*const T>>>`

00:17:29.538 --> 00:17:30.695
which is definitely not cursed.

00:17:30.822 --> 00:17:34.118
And then I have a
`run_with_ambient_context`: it takes

00:17:34.118 --> 00:17:38.158
a reference to an ambient context,
which is definitely not `'static`.

00:17:38.188 --> 00:17:40.968
It could definitely be freed
as soon as this returns.

00:17:41.448 --> 00:17:47.388
And then it casts that lifetime away by
turning it into a raw pointer and puts

00:17:47.388 --> 00:17:53.248
it in the ambient context thread-local,
which is very naughty because if we forget

00:17:53.248 --> 00:17:54.878
to clear it, we have a dangling pointer.

00:17:54.878 --> 00:17:58.388
So anyone who dereferences it
is going to be in for surprise.

00:17:58.618 --> 00:18:00.928
Surprisingly, the
`run_with_ambient_context` function

00:18:00.958 --> 00:18:05.948
is all safe because in Rust, creating
pointers is not a problem, dereferencing

00:18:05.968 --> 00:18:08.148
them is, which, that's always the case.

00:18:08.618 --> 00:18:12.938
But yeah, there's just a `ResetOnDrop`
struct that has a `Drop` implementation

00:18:12.948 --> 00:18:15.418
that resets the context back to `None`.

00:18:15.568 --> 00:18:19.968
So we have a guard here, then we set
it to the  reference cast to pointer,

00:18:19.988 --> 00:18:23.528
and then we call the function, cause it
takes a function,  which is blocking.

00:18:23.538 --> 00:18:25.078
It cannot, it cannot yield.

00:18:25.418 --> 00:18:27.938
You know, people complain
about the fact that synchronous

00:18:27.938 --> 00:18:30.428
functions and asynchronous
functions in Rust look different.

00:18:30.508 --> 00:18:31.858
In that case, very good thing.

00:18:32.078 --> 00:18:35.728
We don't want people to actually return
all the way back to `tokio` because

00:18:35.728 --> 00:18:37.138
then this scheme would not work at all.

00:18:37.435 --> 00:18:39.865
And then the other function is
`with_ambient_context`, which is

00:18:39.865 --> 00:18:43.835
to actually get/look/read whatever
is from the `ThreadLocal`...

00:18:44.265 --> 00:18:49.355
well, there's a safety comment just
above the unsafe block, which says,

00:18:49.355 --> 00:18:52.525
"We borrowed a `ThreadLocal`, no other
thread can drop the context while

00:18:52.525 --> 00:18:53.695
we're holding a reference to it."

00:18:54.015 --> 00:18:55.735
Which I believe is true.

00:18:55.755 --> 00:18:57.115
If you look at the API.

00:18:57.411 --> 00:19:00.441
That `LocalKey` provides
for `RefCell` inners.

00:19:00.711 --> 00:19:03.051
If you do `with_borrow` and
then `with_borrow_mut` somewhere

00:19:03.051 --> 00:19:04.211
else, it's going to panic.

00:19:04.671 --> 00:19:06.241
So there is some checking going on.

00:19:06.241 --> 00:19:07.851
<v James Munns>And you don't have
to worry about `Drop` because

00:19:07.851 --> 00:19:09.141
you're passing a reference in.

00:19:09.141 --> 00:19:11.831
So the actual owned one is
outside of this context, so

00:19:11.831 --> 00:19:13.141
you're just passing a pointer in.

00:19:13.451 --> 00:19:17.571
So when you drop your `RefCell`
containing a pointer, it's not

00:19:17.571 --> 00:19:20.891
gonna call drop on the T, because...

00:19:20.891 --> 00:19:21.191
<v Amos Wenger>Yes.

00:19:21.891 --> 00:19:24.341
<v James Munns>...dropping
a `*T` doesn't drop `T`.

00:19:24.991 --> 00:19:25.335
Yeah.

00:19:25.585 --> 00:19:30.065
<v Amos Wenger>So this is safe because
sure, we are taking some lifetime and

00:19:30.065 --> 00:19:35.595
casting it away into a raw pointer,
but then we're immediately casting that

00:19:35.625 --> 00:19:37.505
again into an even smaller lifetime.

00:19:37.795 --> 00:19:40.485
So it's okay because the second,
like the inner lifetime is

00:19:40.485 --> 00:19:41.925
smaller than the outer lifetime.

00:19:41.925 --> 00:19:42.875
And that's the trick!

00:19:42.905 --> 00:19:45.095
Uh, it's, it's fast.

00:19:45.095 --> 00:19:46.755
You don't have like process-wide locks.

00:19:46.995 --> 00:19:47.845
It works great.

00:19:47.855 --> 00:19:52.075
You don't actually have to use
Arc and clone Arcs to just pass

00:19:52.075 --> 00:19:53.365
context to arbitrary functions.

00:19:53.685 --> 00:19:54.385
I use that a lot.

00:19:54.385 --> 00:19:57.466
And then the drawback is of course
that you have to make sure that

00:19:57.466 --> 00:20:00.826
it's set before you call a function,
but it panics friendly, like in a

00:20:00.826 --> 00:20:02.356
friendly way if you forgot to set it.

00:20:02.356 --> 00:20:05.909
It's just, you know, the usual
debate between do I pass the context

00:20:05.909 --> 00:20:08.789
down explicitly as a parameter
or do I set it to a thread-local

00:20:08.809 --> 00:20:09.579
and hope that it's there.

00:20:09.579 --> 00:20:11.189
And `tokio` definitely
has the same problem.

00:20:11.681 --> 00:20:12.141
<v James Munns>Gotcha.

00:20:12.891 --> 00:20:13.341
I...

00:20:13.575 --> 00:20:14.005
phew.

00:20:15.185 --> 00:20:16.607
This is some spicy code, Amos...

00:20:16.665 --> 00:20:17.395
<v Amos Wenger>I know it is!

00:20:17.672 --> 00:20:18.677
<v James Munns>I follow what you're doing.

00:20:18.895 --> 00:20:19.075
And...

00:20:19.075 --> 00:20:20.935
as far as you've explained
it, it makes sense.

00:20:21.205 --> 00:20:25.095
I don't, I don't know if I could recommend
this, and I, like, I wonder how...

00:20:25.505 --> 00:20:26.915
what are you gaining here?

00:20:26.915 --> 00:20:32.882
Like was mutex contention or `RefCell`s
or reference counting too expensive?

00:20:32.882 --> 00:20:33.372
Or is this-

00:20:33.825 --> 00:20:33.905
<v Amos Wenger>No...

00:20:33.955 --> 00:20:37.705
<v James Munns>Is, was this a fever
dive into "what can I get away with?"

00:20:38.560 --> 00:20:41.540
<v Amos Wenger>So this is one use
case and it doesn't matter because I

00:20:41.540 --> 00:20:44.410
only compile CSS when deploying new
versions of the site, so that's...

00:20:44.750 --> 00:20:45.440
very rare.

00:20:45.533 --> 00:20:50.133
But what happens a lot is
executing liquid filters.

00:20:50.563 --> 00:20:51.163
Well...

00:20:51.608 --> 00:20:53.408
this could be a whole other
episode explaining all the

00:20:53.408 --> 00:20:54.618
crimes I do in liquid filters.

00:20:54.834 --> 00:20:56.154
Liquid is a templating engine.

00:20:56.344 --> 00:20:57.684
Again, you can extend it.

00:20:57.778 --> 00:21:01.878
And  I have a filter that lets you
execute arbitrary SQL queries...

00:21:03.208 --> 00:21:07.048
which, you know, like, yeah,
why not talk to the database

00:21:07.058 --> 00:21:08.088
from the templating language?

00:21:08.128 --> 00:21:08.478
Why not?

00:21:09.088 --> 00:21:12.358
And so, yeah, it needs to
have a database connection.

00:21:13.044 --> 00:21:14.304
And again, that's a thread-local.

00:21:14.324 --> 00:21:15.474
And I do the same trick here.

00:21:15.634 --> 00:21:19.819
And in this case, it is actually
required, because it's from a thread pool.

00:21:19.819 --> 00:21:21.939
So we have a reference to
it, but we don't own it.

00:21:21.979 --> 00:21:22.989
And it's going to go back.

00:21:23.279 --> 00:21:26.919
Like we have something that dereferences
to a `Connection`, but it's actually

00:21:26.919 --> 00:21:29.094
a pooled item connection or something.

00:21:29.234 --> 00:21:30.144
It's a wrapper type.

00:21:30.444 --> 00:21:32.914
So in that scenario, it's unavoidable.

00:21:32.924 --> 00:21:34.354
<v James Munns>Like I said,
I think I follow you.

00:21:34.354 --> 00:21:37.824
I'm, I'm, I, is this a...?

00:21:37.844 --> 00:21:38.564
Yeah, I don't know.

00:21:38.604 --> 00:21:41.094
I don't want to put engineer brain
on because this is a cool thing

00:21:41.724 --> 00:21:44.854
and I could "What about different
ways to solve this problem?"

00:21:44.854 --> 00:21:47.397
but I imagine you've spent
quite a bit of time with this.

00:21:47.427 --> 00:21:48.377
<v Amos Wenger>I'm curious!

00:21:48.455 --> 00:21:49.017
Come at me!

00:21:49.017 --> 00:21:51.527
If you have a less cursed
solution in mind, I mean...

00:21:51.937 --> 00:21:55.897
just like the several copies of `tokio`
playing nice with each other thing, I

00:21:55.897 --> 00:21:57.487
wish there was a simpler solution, but...

00:21:58.162 --> 00:21:58.222
<v James Munns>Yeah...

00:21:58.762 --> 00:22:00.242
Yeah, I guess I guess the...

00:22:00.408 --> 00:22:04.928
you probably don't want to touch  the
SASS compilation library to take like a

00:22:04.938 --> 00:22:08.408
boxed closure or something because like
I've written a bunch of stuff that uses

00:22:08.408 --> 00:22:11.323
function pointers rather than `impl Fn`.

00:22:11.343 --> 00:22:14.593
Because, especially with closures,
you get unnameable types.

00:22:14.613 --> 00:22:18.153
And for example, like when I was
writing a scripting engine, it was

00:22:18.153 --> 00:22:22.713
very easy to have an array of like,
okay, I'm binding this function to

00:22:22.713 --> 00:22:25.163
this name in my scripting language.

00:22:25.163 --> 00:22:28.753
So when you type this word,
this Rust function gets called.

00:22:28.863 --> 00:22:32.470
And that's really easy when everything
is an `Fn`, even if it takes context

00:22:32.470 --> 00:22:33.960
or whatever, that's very easy.

00:22:33.960 --> 00:22:36.260
But the second that you want to
do closures- or actually the other

00:22:36.280 --> 00:22:39.840
spicy thing that gets you is async
functions because every async

00:22:39.840 --> 00:22:42.390
function is an unnameable type too.

00:22:42.789 --> 00:22:45.859
In my scripting language, I ended
up having to just have one big match

00:22:45.859 --> 00:22:49.339
statement that was like all the
text keys that you could use and

00:22:49.339 --> 00:22:50.769
it mapped them to async functions.

00:22:51.359 --> 00:22:56.159
But I guess the alternative might
be modifying the library to take a

00:22:56.169 --> 00:23:00.829
boxed closure instead, like a boxed in
`Fn`, because then you would be able

00:23:00.829 --> 00:23:04.359
to like pre populate all of them with
their context and then call them, but

00:23:04.539 --> 00:23:08.152
that'd probably be a, I imagine, a
fairly invasive change to the library.

00:23:08.572 --> 00:23:09.272
<v Amos Wenger>It would be...

00:23:09.730 --> 00:23:14.320
And my strategy to survive in open
source now, is: I'm either gonna write

00:23:14.320 --> 00:23:17.480
my own thing and then tell people
that I'm not gonna do their weird

00:23:17.480 --> 00:23:19.330
use case because I wrote it for me.

00:23:19.705 --> 00:23:24.555
Or:  use something exactly as it is and
never require any big change from it

00:23:24.625 --> 00:23:29.165
because I value the maintainers too much,
or: fork an existing thing and then, you

00:23:29.165 --> 00:23:30.965
know, make it do exactly what I need.

00:23:31.022 --> 00:23:34.032
And then, you know, thank the maintainer,
the original maintainer profusely,

00:23:34.032 --> 00:23:37.212
but never get up in their business
and ask them to change their thing

00:23:37.212 --> 00:23:38.736
completely just to suit my use case.

00:23:38.806 --> 00:23:43.842
So in this instance, you know, I
have at least two use cases where

00:23:44.332 --> 00:23:47.062
I don't want to be in charge of
maintaining a fork of the liquid crate.

00:23:47.062 --> 00:23:49.412
I don't want to be in charge of
maintaining a fork of a SASS compiler.

00:23:49.572 --> 00:23:52.962
So, this works, and
it's safe demonstrably.

00:23:53.132 --> 00:23:53.432
So

00:23:54.292 --> 00:23:55.652
<v James Munns>Hee-hee-hee-hee
Spicy But safe

00:23:56.283 --> 00:23:58.723
<v Amos Wenger>I'm happy for you to prove
me wrong or anyone for that matter.

00:24:04.720 --> 00:24:07.060
This episode is sponsored
by Ladybird browser.

00:24:07.480 --> 00:24:11.780
Today, every major web browser is funded
or powered by Google's advertising empire.

00:24:11.980 --> 00:24:14.040
Choice is good, but your
only choice is Google.

00:24:14.380 --> 00:24:16.890
The Ladybird browser wants
to do something about this.

00:24:17.290 --> 00:24:20.370
Ladybird is a brand new browser and
web engine written from scratch and

00:24:20.370 --> 00:24:22.220
free of the influences of Big Tech.

00:24:22.780 --> 00:24:25.280
Driven by web standards first
approach, Ladybird aims to

00:24:25.280 --> 00:24:28.990
render the modern web with good
performance, stability, and security.

00:24:29.232 --> 00:24:33.082
From its humble beginnings as an HTML
viewer for the SerenityOS hobby operating

00:24:33.082 --> 00:24:37.042
system project, Ladybird has since grown
into a cross-platform browser supporting

00:24:37.042 --> 00:24:39.612
Linux, macOS, and other Unix like systems.

00:24:40.202 --> 00:24:43.872
In July, Ladybird launched a non-profit
to support development and announced a

00:24:43.872 --> 00:24:47.902
first Alpha for early adopters targeting
2026, but you can support the project

00:24:47.902 --> 00:24:50.032
on GitHub or via donations today.

00:24:50.532 --> 00:24:53.742
Visit ladybird.org for more information
and to join the mailing list.

00:24:54.382 --> 00:24:57.262
Thanks to Ladybird for
sponsoring today's episode.

