Live data from Hacker News

A safe, non-owning C++ pointer class

techblog.rosemanlabs.com

31–40 of 50 posts

Re: A safe, non-owning C++ pointer class

#31
post #24
post #16

Earlier quoted context omitted.

atomics aren't free even without contention. the slogan of the language is "you don't pay for what you don't use", and it's really not great that there's no non atomic refcount in the standard. the fact that it is default atomic has also lead people to assume guarantees that it doesn't provide, which was trivially predictable when the standard first introduced it.

OP specifically mentioned contention, though -- not marginally higher cost of atomic inc/dec vs plain inc/dec. > For our use case, we in fact do not use std::shared_ptr in our implementation, but instead a single-threaded shared_ptr-like class that has no atomics (to avoid cross-core contention). A single-threaded program will not have cross-core contention whether it uses std::atomic refcounts or plain integer refco…

> not marginally higher cost of atomic inc/dec vs plain inc/dec.

Note that the difference is not so marginal, and the difference is not just in hardware instructions as the non-atomic operations generally allow for more optimizations by the compiler.

Re: A safe, non-owning C++ pointer class

#32
post #19
post #16

Earlier quoted context omitted.

atomics aren't free even without contention. the slogan of the language is "you don't pay for what you don't use", and it's really not great that there's no non atomic refcount in the standard. the fact that it is default atomic has also lead people to assume guarantees that it doesn't provide, which was trivially predictable when the standard first introduced it.

Related to this, GNU's libstdc++ shared_ptr implementation actually opts not to use atomic arithmetic when it infers that the program is not using threads.

I never heard of this and went to check in the source and it really does exist: https://codebrowser.dev/llvm/include/c++/11/ext/concurrence....

Re: A safe, non-owning C++ pointer class

#33
Interesting, but I see no real use-case where it may be useful. Usually raw pointers/references are used to pass a value to a function without ownership transfer and it's almost always true that this value remains valid until callee isn't returned. Other use-cases, like putting such pointer into a struct are dangerous and one should minimize doing this.

Re: A safe, non-owning C++ pointer class

#34
post #24

Earlier quoted context omitted.

OP specifically mentioned contention, though -- not marginally higher cost of atomic inc/dec vs plain inc/dec. > For our use case, we in fact do not use std::shared_ptr in our implementation, but instead a single-threaded shared_ptr-like class that has no atomics (to avoid cross-core contention). A single-threaded program will not have cross-core contention whether it uses std::atomic refcounts or plain integer refco…

> not marginally higher cost of atomic inc/dec vs plain inc/dec. Note that the difference is not so marginal, and the difference is not just in hardware instructions as the non-atomic operations generally allow for more optimizations by the compiler.

The actual intrinsic is like 8-9 cycles on Zen4 or Ice Lake (vs 1 for plain add). It's something if you're banging on it in a hot loop, but otherwise not a ton. (If refcounting is hot in your design, your design is bad.)

It's comparable to like, two integer multiplies, or a single integer division. Yes, there is some effect on program order.

Re: A safe, non-owning C++ pointer class

#35
post #19

Earlier quoted context omitted.

Related to this, GNU's libstdc++ shared_ptr implementation actually opts not to use atomic arithmetic when it infers that the program is not using threads.

I never heard of this and went to check in the source and it really does exist: https://codebrowser.dev/llvm/include/c++/11/ext/concurrence....

The code you linked is a compile-time configuration option, which doesn't quite match "infer" IMO. I think GP is thinking of the way that libstdc++ basically relies on the linker to tell it whether libpthread is linked in and skips atomic operations if it isn't [0].

[0]: https://snf.github.io/2019/02/13/shared-ptr-optimization/

Re: A safe, non-owning C++ pointer class

#36

Earlier quoted context omitted.

weak_ptr supports this -- it's only mt-safe if you specialize it with std::atomic

Last I checked weak_ptr is always atomic (ignoring weird attempted glibc magic when you don’t link against pthread)

? https://en.cppreference.com/w/cpp/memory/weak_ptr/atomic2

Re: A safe, non-owning C++ pointer class

#37

Earlier quoted context omitted.

Last I checked weak_ptr is always atomic (ignoring weird attempted glibc magic when you don’t link against pthread)

? https://en.cppreference.com/w/cpp/memory/weak_ptr/atomic2

Oh sure, a single weak_ptr instance itself is not safe for multiple concurrent access of non-const methods. But weak_ptr -> shared_ptr reacquisition is atomic and all control block operations are:

> Note that the control block used by std::weak_ptr and std::shared_ptr is thread-safe: different non-atomic std::weak_ptr objects can be accessed using mutable operations, such as operator= or reset, simultaneously by multiple threads, even when these instances are copies or otherwise share the same control block internally. The type T may be an incomplete type.

There’s no variant of shared_ptr / weak_ptr that is non atomic in the standard library AFAIK.

Re: A safe, non-owning C++ pointer class

#38

Interesting, but I see no real use-case where it may be useful. Usually raw pointers/references are used to pass a value to a function without ownership transfer and it's almost always true that this value remains valid until callee isn't returned. Other use-cases, like putting such pointer into a struct are dangerous and one should minimize doing this.

shared_ptr will bite you in the rear if you ever need to have well defined semantics about when an object is destructed. It has a lot of good use cases, especially in async code bases where you want to effectively cancel callbacks if the captured variable has gone away. Proactively cancelation is much more difficult by comparison. There are other ways to achieve this result but the one used in the article is a fine choice.

Re: A safe, non-owning C++ pointer class

#39

std::span is another option. Especially when paired with libc++'s hardening mode(s). Apparently, Google has deployed them in production.

Hardened std::span just adds checks during operator[] that you would normally only happen in at(). Same for operator* and operator->. It doesn't really have any relevance for the problem the article is written about.

Re: A safe, non-owning C++ pointer class

#40

This just seems intentionally bad to show where Rust would be better. This is yet another example of what I call "corner-case" instruction, which I define as, "I am going to take an obviously terrible corner-case that shows what an awful developer can do that will break a program, then demonstrate my brilliance by introducing my (highly-biased) opinionated point I wanted to make..." In this particular case, it was su…

You could implement the same smart pointer library in rust and it would be fine. Rust doesn't magically solve the problems around defined destruction ordering when using ref counted pointers. I try very hard to model my usage of Rc or Arc to be very similar to what this article is trying to showcase for basically the same reasons I imagine they do. I'm actually inspired to write a crate with these semantics to make it harder to mess it up.
Post reply on HN