damon_dam
Joined 1 year ago
Joined 1 year ago
Allocate the variable normally, then compute the offset in one thread (e.g. offset = uintptr_t(&variable) - get_fs()), then access it by adding the offset to FS in any thread (e.g. (vartype *) (offset + get_fs())). The only difference from how it normally works is that you can manually force it to be inlined, sidestepping the codegen problems you described in your post. But if you can avoid those problems by not using constructors instead, that's definitely better.
I used "FS/GS" because GS is used instead of FS on some systems for the same purpose.
The shared library-specific issues are one of the reasons I was suggesting maybe looking into hashing, e.g. perhaps as a fallback solution when the TLS approach fails.
An alternative that might be worth looking into is just hashing the FS/GS into a table index. It will be slower than the well-optimized case, but it will let you opt out of the TLS allocation process altogether. This might be a good thing in some cases for a low-level facility like a function tracer.
Hey Google, here's a modest proposal: nerdsearch.google.com. Return the same results, but in a spartan HN-like format, with no JS or fingerprinting or other "did be evil" things. Non-techies will never find out about it if you don't advertise it. You can still fingerprint people who don't know or care about it.
> Gmail? YouTube? Gemini? Chrome? Android
Most of these are best-in-class, so I think I detect some bias in the question.
- No reference variables in user code (reference parameters are OK.)
- Any classes that hold reference member variables can only be instantiated on the stack. The objects the references refer to must either be stack objects or reference parameters to the instantiating function.
- For durable links to sub-objects, use handles (tuples of std::shared_ptr to the main object plus a sub-object link or identifier.)
- Alternatively, require externally-accessible sub-objects to be held in shared_ptr's by the main object. They can then have superset lifetimes when needed.
I'll stick to the memory safety topic, but similar solutions exist for some of the other topics you mention, e.g. by avoiding std::optional.
If you stick with "21st Century C++" [1], you never have to use a pointer. At some point, there will be (say) a -W20 flag that produces a warning if you use a pointer or other potentially-unsafe idiom without marking it with a [[20th_century]] attribute.
That attribute is equivalent to Rust's unsafe keyword. At that point, the safety difference will boil down to opt-in versus opt-out. But you can already opt-in today by policing yourself.
That's quite far from the radical language difference it's made out to be.
Of course you can. I just meant that the linked article didn't.
> On LR/SC: to any atomics experts listening, isn’t it technically “obstruction-free” (as per the Wikipedia definitions at least) rather than lock-free?
The better criterion IMO is loop-free, which makes it a little easier to understand. Consider the following spin-locking code (with overabundant memory barriers):
do { p = *a; } while (p == 0x1 || !atomic_compare_and_swap(p, 0x1, a));
memory_barrier();
// do stuff that looks at *p
q->next = p;
memory_barrier();
atomic_store(q, a);
Here's the equivalent LL/SC version: do {
p = ll(a);
memory_barrier();
// do stuff that looks at *p
q->next = p;
memory_barrier();
} while (!sc(q, a));
The pointer-tagging version is also obviously not loop-free. Which is faster, in which cases, and by how much?The oversimplified answer is that LL/SC is probably slightly faster than spin-locking on most platforms and cases, but pointer-tagging might not be.
The vagueness was intentional. There is of course no homogeneous way of combining advantages into a cardinal measure. It's just a rhetorical device.
The point is that it falls short of the amount needed. Another, more subtle point is that I didn't count disadvantages. The argument applies even if you think Rust doesn't have any.
Unfortunately x86 is not one of them. That's when CMPXCHG16B on x86-64 comes in handy, as per the linked article. But if you're going to write library code in 2025, it should at least support x86 and ARM.
Which brings up a more interesting argument IMO: platform fragmentation. Rust has approximately 1.5 advantages over C/C++. To displace them in a reasonable time, it probably needed 4 or 5. Its most significant contribution for the foreseeable future is platform fragmentation.
[1] https://en.wikipedia.org/wiki/Load-link/store-conditional
Everything but PostScript itself was closed-source. We could have definitely used 5086 pages of documentation. Even 2 would have been nice. Young whippersnappers don't know how good they have it...