Yes, I agree. But I think it is useful to try to apply the lesson of that paper further: capabilities that we want to give programmers that change the semantics of their code in a fundamental way maybe should be part of the language. I think it's a sliding scale. Your position seems to be "if we can do it in a library, we don't need a language." I'm proposing that there is a spectrum.
> change the semantics of their code in a fundamental way
What is this semantic change you are talking about? Threads run what you give them, they don't change the semantics. Synchronization primitives are already in languages. Using the synchronization primitives to get something fast yet simple to use is the tricky part, and that is something that can be done with simple pieces and relatively small data structures.
This is about how to handle parallelism and concurrency, which goes far beyond threads.
Just as working with raw pointers and no data structures with no wrappers for length or ownership is error prone, using threads by themselves doesn't scale to complex use cases without becoming intractable at a certain point, so there needs to be structure.
Most programmers will never be able to do more than trivial threading (if that) so the difficult parts needs to separated into something that is much easier but still useful, but using a new language is incredibly misguided because you give up an enormous amount without gaining anything non trivial. A language might be able to give an ironclad guarantee that a function running concurrently doesn't mutate anything, but in practice this is not hard to avoid. The difficulty comes in data structures and dependencies, because that is ultimately where the synchronization is.