Live data from Hacker News

There is no memory safety without thread safety

ralfj.de

431–440 of 517 posts

Re: There is no memory safety without thread safety

#431
post #327

Earlier quoted context omitted.

Because the other teams members (IIRC brson and pcwalton) wanted Rust to be as performant as C++, which means you must have a way to have shared memory.

Which was the ultimate failure of Rust, because as Pony benchmarks have shown, you get safety and speed by proper security and architecture. Rust just survived by lying about the its safeties. What kills performance are not memory copies, but locks. Parallel nonblocking IO and a non POSIX stdlib will bring you far away from C++ or Rust performance.

> Rust just survived by lying about the its safeties.

strong claim. care to back it up?

Re: There is no memory safety without thread safety

#432
post #413

Earlier quoted context omitted.

Oh yeah, the “ultimate failure of Rust”, and tell me how industrially successful Pony has been compared to Rust? (Don't get me wrong, I liked the idea behind Pony for backend code , it's much saner than Go for the same target space. But it failed to capture that market, because Go was already there. And it was never a competitor to Rust because this model is only viable for web back end tasks, not for general computi…

It isn't it a shame that the industry always falls for the liars? The pony model was also better for compute tasks, not just IO. Because it provided safe concurrency, 10x faster than go.

No, the pony model isn't better for compute tasks…

Think for instance about how you'd do efficient matrix multiplication of two matrices with a million row and column, in Pony, versus how it works in languages with shared memory. You'd spend a gigantic amount of time copying data for no good reason…

Re: There is no memory safety without thread safety

#433
post #368

Earlier quoted context omitted.

> While you're wondering why I keep claiming Go is a memory-safe language, you can also go ask the ISRG, which says the same thing I am at And yet Go violates the definition they give -- it doesn't prevent out-of-bounds accesses. (And just to be sure we're talking about the same thing, I'm specifically talking about Go here. All the other languages on their list are actually memory safe, as far as I know.) > you have…

> So your definition of memory safety includes some notion of "plausible" and "realistic"? Neither https://www.memorysafety.org/docs/memory-safety/ nor Wikipedia have such a qualification in their definition. It would help if you could just spell out your definition in full, rather than having us guess. This is a strawman argument, you're arguing semantics here. You're a smart person, so you know exactly what he mean…

> you're arguing semantics here

Yes, semantics — what do things mean, exactly? — is the subject of the discussion here and is actually quite important in general.

Re: There is no memory safety without thread safety

#434

Wow that's a really big gotcha in go! To be fair though, go has a big emphasis on using its communication primitives instead of directly sharing memory between goroutines [1]. [1] https://go.dev/blog/codelab-share

Wow who knew concurrency is hard!

This isn't anything special, if you want to start dealing with concurrency you're going to have to know about race conditions and such. There is no language that can ever address that because your program will always be interacting with the outside world.

Re: There is no memory safety without thread safety

#435

Earlier quoted context omitted.

I think the surprise here is that failing to synchronize writes leads to a SEGFAULT, not a panic or an error. This is the point GP was making, that Go is not fully memory safe in the presence of unsynchronized concurrent writes. By contrast, in Java or C#, unsynchronized writes will either throw an exception (if you're lucky and they get detected) or let the program continue with some unexpected values (possibly ones…

Segfault sounds better than running with inconsistent data.

Well it depends on what we mean by 'inconsistent'.

In C# For example, if a structure is over CPU arch Word size (i.e. 32 or 64 bits) then you could have a torn read if it's being written. However object refs themselves are always word size so you'll never have a torn pointer read on those.

However, in either case there is still a need in multithreaded environments to remember the CPU's memory ordering rules and put proper fences (or, to be safe, locks, since memory barrier rules are different between ARM and x86 for example).

But that second bit is a fairly hard problem to solve for without having the right type of modelling around your compiler.

Re: There is no memory safety without thread safety

#436
post #4

This is a canard. What's happening here, as happens so often in other situations, is that a term of art was created to describe something complicated; in this case, "memory safety", to describe the property of programming languages that don't admit to memory corruption vulnerabilities, such as stack and heap overflows, use-after-frees, and type confusions. Later, people uninvolved with the popularization of the term…

> The fact is that Go doesn't admit memory corruption vulnerabilities, and the way you know that is the fact that there are practically zero exploits for memory corruption vulnerabilities targeting pure Go programs, despite the popularity of the language.

Another way to word it: If "Go is memory unsafe" is such a revelation after its been around for 13 years, it's more likely that such a statement is somehow wrong than that nobody's picked up on such a supposedly impactful safety issue in all this time.

As such, the burden of proof that addresses why nobody's ran into any serious safety issues in the last 13 years is on the OP. It's not enough to show some theoretical program that exhibits the issue, clearly that is not enough to cause real problems.

Re: There is no memory safety without thread safety

#437
post #436
post #4

This is a canard. What's happening here, as happens so often in other situations, is that a term of art was created to describe something complicated; in this case, "memory safety", to describe the property of programming languages that don't admit to memory corruption vulnerabilities, such as stack and heap overflows, use-after-frees, and type confusions. Later, people uninvolved with the popularization of the term…

> The fact is that Go doesn't admit memory corruption vulnerabilities, and the way you know that is the fact that there are practically zero exploits for memory corruption vulnerabilities targeting pure Go programs, despite the popularity of the language. Another way to word it: If "Go is memory unsafe" is such a revelation after its been around for 13 years, it's more likely that such a statement is somehow wrong th…

There's no "revelation" here, it's always been well known among experts that Go is not fully memory safe for concurrent code, same for previous versions of Swift. OP has simply spelled out the argument clearly and made it easier to understand for average developers.

Re: There is no memory safety without thread safety

#438
post #405

Earlier quoted context omitted.

Hardware threads are a thing.

Other than in the sense of SMT (Hyper-Threading)? I don't think so. Threads are a software concept. One can distinguish between native (OS) threads and green (language-runtime) threads which may use a different context-switching mechanism. But that's more of a spectrum in terms of thread-safety; similar to how running multiple threads on a single CPU core without SMT, single CPU core with SMT, multiple CPU cores, wit…

Runtime-switched tasks cannot lead to memory unsafety unless multiple OS threads are involved, because that's the only case where torn writes are possible. And a typical configuration of Go will not be running multiple OS threads unless multiple hardware threads (aka "logical cores", "virtual processors" etc.) are available.

Re: There is no memory safety without thread safety

#439
post #68

Earlier quoted context omitted.

Rust can yield a higher quality solution, but we can't make a perfect solution, we can only approach perfection. If we want to go further, we could introduce formally-proven code, too. Personally I'm interested in the intersection of proof assistants and Rust, like creusot-rs, and have been investigating it. But as much as I love LARPing about correctness (believe me I do,) it's just simply the case that we won't rig…

> Rust can also make some types of software more productive to write, but it is unlikely to beat Go in terms of productivity when it comes to a lot of the stuff SaaS shops deal with. And boy, the software industry sure is swamped in fucking SaaS. I just wish Go supported parametric enums (sum types) and Option, rather than copying Hoare’s billion dollar mistake. I ported some code to Go and rust a few years ago to tr…

You're not the only one who wishes Go was just a bit more like Rust: https://github.com/borgo-lang/borgo

Sadly, that project seems to be dead, but I hope someone picks up its mantle some day. A marginally better Go could, well, go far.

Re: There is no memory safety without thread safety

#440
post #30

Earlier quoted context omitted.

I think the true answer is that the moment you have to do tricky concurrency in Go, it becomes less desirable. I think that Go is still better at tricky concurrency than C, though there are some downsides too (I think it's a bit easier to sneak in a torn read issue in Go due to the presence of fat pointers and slice headers everywhere.) Go is really good at easy concurrency tasks, like things that have almost no shar…

> But I struggle to be as productive You should calculate TCO in productivity. Can you write Python/Go etc. faster? Sure! Can you operate these in production with the same TCO as Rust? Absolutely not. Most of the time the person debugging production issues and data races is different than the one who wrote the code. This gives the illusion of productivity being better with Python/Go. After spending 20+ years around p…

With the notable inclusion of Google where the SRE team is usually separate from the SWE team (but it wasn't for my particular case) I actually was always doing operations and code at all of my jobs at least at points and usually during most of the job. This is in part my own election. I do mean all of them though I don't really love listing my work history publicly everywhere just to keep some separation.

So, my first job actually started as a pure Python gig. Operations for Python/Django absolutely sucked ass. Deploying Django code reliably was a serious challenge. We got better over time by using tools like Vagrant and Docker and eventually Kubernetes, so the differences between production and dev/testing eventually faded and become less notable. But frankly no matter what we did, not causing production issues with Django/Python was a true-to-life nightmare. Causing accidental type errors not caught by tests was easy and MyPy couldn't really cover all that much of the code easily, and the Django ORM was very easy to accidentally cause horrible production behavior with (that, of course, would look okay locally with tiny amounts of data.) This is actually the original reason why I switched to Go in the first place, at my first job in around 2016. The people who I worked with are still around to attest to this fact, if you want I can probably get them to chime in on this thread, I still talk to some of them.

Go was a totally different story. Yes, we did indeed have some concurrency pains, which really didn't exist in Python for obvious reasons, but holy shit, we could really eek a lot of performance out of Go code compared to Python. We were previously afraid we might have to move data heavy workloads from Twisted (not related to the Django stuff) to something like C++ or maybe even optimized Java, but Go handily took it and allowed us to saturate the network interface on our EC2 boxes. (A lot of communications were going over Websockets, and the standards for compression in websockets took a long time to settle and become universally supported, so we actually played with implementing the lz4 compression scheme in JS. I wound up writing my own lz4 implementation based on the algorithms, I believe, from the C version. It wound up being too much compute, though. But, we had to try, anyway.)

So how much reliability problems did we wind up having doing all this? Honestly not a whole lot on the Go side of things. The biggest production issue I ever ran into was one where the Kubernetes AWS integration blew up because we wound up having too many security groups. I wound up needing to make an emergency patch to kubelet in the early hours to solve that one :) We did run into at least one serious Go related issue over time, which was indeed concurrency related: when Go 1.6 came out, it started detecting concurrent misuses of maps. And guess what? We had one! It wasn't actually triggering very often, but in some cases we could run into a fairly trivial concurrent map access. It didn't seem to crash before but it could at least cause some weird behaviors in the event that it actually triggered before Go 1.6; now it was a crash that we could debug. It was a dumb mistake and it definitely underscores the value of borrow checking; "just don't mess up" will never prevent all mistakes, obviously. I will never tell you that I think borrow checking is useless, and really, I would love to just always write 100% correct software all the time.

That said though, that really is most of the extent of the production issues we had with Go. Go was a serious workhorse and we were doing reasonably non-trivial things in Go. (I had essentially built out a message queue system for unreliable delivery of very small events. We had a firehose of data coming in with many channels of information and needed to route those to the clients that needed them and handle throttling/etc. Go was just fantastic at this task.) Over time things got easier too, as Go kept updating and improving, helping us catch more bugs.

I can only come to one conclusion: people who treat Go and Python in the same class are just ignorant to the realities of the situation. There are cases where Rust will be immensely valuable because you really can't tolerate a correctness problem, but here's the thing about that Go concurrent map access issue: while it could cause some buggy behavior and eventually caused some crashing, it never really caused any serious downtime or customer issues. The event delivery system was inherently dealing with unreliable data streams, and we had multiple instances. If there was a blip, clients would just reconnect and people would barely notice anything even if they were actively logged in. (In fact, we really didn't do anything special for rolling deployments to this service, because the frontend component was built to just handle a disconnection gracefully. If it reconnected quickly enough, there was no visual disturbance.)

That's where the cost/benefit analysis gets tricky though. Python and Django and even Twisted are actually pretty nice and I'm sure it's even better than when we originally left it (to be clear we did still have some minor things in Django after that, too, but they were mostly internal-only services.) Python and Django had great things like the built-in admin panel which, while it couldn't solve everyone's needs, was pretty extensible and usable on its own. It took us a while to outgrow it for various use cases. Go has no equivalent to many Django conveniences, so if you haven't fully outgrown e.g. the Django admin panel and ORM, it's hard to fully give up on those features.

Throughout all of this, we had a lot more issues with our JS frontend code than we ever did with either Python/Django or Go, though. We went through trying so many things to fix that, including Elm and Flow, and eventually the thing that really did fix it, TypeScript. But that is another story. (Boy, I sure learned a lot on my first real career job.)

At later jobs, Go continued to not be at the center of most of the production issues I faced running Go software. That's probably partly because Go was not doing a lot of the most complicated work, often times the most complicated bits were message queues, databases and even to some degree memory caches, and the Go bits were mostly acting like glue (albeit definitely glue with application logic, to be sure.)

So is the TCO of Go higher than Rust? I dunno. You can't really easily measure it since you don't get to explore parallel universes where you made different choices.

What I can say is that Go has been a choice I never regretted making all the way from the very first time and I would choose it again tomorrow.

Post reply on HN