Comment by kouteiheika

7 hours ago

> the shared pointer implementation uses a POSIX mutex [...] C++’s shared pointer has the same problem

It doesn't. C++'s shared pointers use atomics, just like Rust's Arc does. There's no good reason (unless you have some very exotic requirements, into which I won't get into here) to implement shared pointers with mutexes. The implementation in the blog post here is just suboptimal.

(But it's true that C++ doesn't have Rust's equivalent of Rc, which means that if you just need a reference counted pointer then using std::shared_ptr is not a zero cost abstraction.)

To be clear, the “same problem” is that it’s not a zero-cost abstraction, not that it uses the same specific suboptimal approach as this blog post.

  • I think that's an orthogonal issue. It's not that C++'s shared pointer is not a zero cost abstraction (it's as much a zero cost abstraction as in Rust), but that it only provides one type of a shared pointer.

    But I suppose we're wasting time on useless nitpicking. So, fair enough.

    • I think they’re one and the same: C++ doesn’t have program-level thread safety by construction, so primitives like shared pointers need to be defensive by default instead of letting the user pick the right properties for their use case.

      Edit: in other words C++ could provide an equivalent of Rc, but we’d see no end of people complaining when they shoot themselves in the foot with it.

      (This is what “zero cost abstraction” means: it doesn’t mean no cost, just that the abstraction’s cost is no greater than the semantically equivalent version written by the user. So both Arc and shared_ptr are zero-cost in a MT setting, but only Rust has a zero-cost abstraction in a single-threaded setting.)

      4 replies →

> very exotic requirements

I'd be interested to know what you are thinking.

The primary exotic thing I can imagine is an architecture lacking the ability to do atomic operations. But even in that case, C11 has atomic operations [1] built in. So worst case, the C library for the target architecture would likely boil down to mutex operations.

[1] https://en.cppreference.com/w/c/atomic.html

  • Well, basically, yeah, if your platform lacks support for atomics, or if you'd need some extra functionality around the shared pointer like e.g. logging the shared pointer refcounts while enforcing consistent ordering of logs (which can be useful if you're unfortunate enough to have to debug a race condition where you need to pay attention to refcounts, assuming the extra mutex won't make your heisenbug disappear), or synchronizing something else along with the refcount (basically a "fat", custom shared pointer that does more than just shared-pointering).

    • Does there exist any platform which has multithreading but not atomics? Such a platform would be quite impractical as you can't really implement locks or any other threading primitive without atomics.

      5 replies →

  • Which platforms might that be? Even MIPS has atomics (at least pointer sized last i checked).

    • AFIAK, and I'm not MIPS expert, but I believe it doesn't have the ability to add a value directly to a memory address. You have to do something like

          // Not real MIPS, just what I've gleaned from a brief look at some docs
          LOAD addr, register
          ADD 1, register
          STORE register, addr
      

      The LOAD and STORE are atomic, but the `ADD` happens out of band.

      That's a problem if any sort of interrupt happens (if you are multi-threading then a possibility). If it happens at the load, then a separate thread can update "addr" which mean the later STORE will stomp on what's there.

      x86 and ARM can do

          ADD 1, addr
      

      as well as other instructions like "compare and swap"

          LOAD addr, register
          MOV register, register2
          ADD 1, register2
          COMPARE_AND_SWAP addr, register, register2
          if (cas_failed) { try again }

      1 reply →