Skip to content

Conversation

@AlexGuteniev
Copy link
Contributor

  • Only use __builtin_zero_non_value_bits in compare_exchange_strong:
    • Using in constructor will not work properly for atomic_ref
    • Using in constructor will force to use it in store and exchange which is sub-optiomal
    • Computing mask in CAS might be useful for LL/SC implementation, if masked CAS is exposed as intrinsic
  • Only expose the feature for C++20

@AlexGuteniev AlexGuteniev requested a review from a team as a code owner July 10, 2020 19:44
@StephanTLavavej StephanTLavavej added the cxx20 C++20 feature label Jul 10, 2020
@AlexGuteniev AlexGuteniev marked this pull request as ready for review July 11, 2020 12:49
@AlexGuteniev
Copy link
Contributor Author

Now the test pass, I also improved a bit coverage by using more interesting bit pattern, and filed LLVM-46685. It is ready for review.

@AlexGuteniev
Copy link
Contributor Author

I use _HAS_CXX20. It might make sense to have this feature unconditionally.

Copy link
Member

@StephanTLavavej StephanTLavavej left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last batch of questions, then I think this will be ready - thanks!

AlexGuteniev and others added 2 commits July 15, 2020 12:03
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
Copy link
Member

@BillyONeal BillyONeal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is ready to merge once the default constructed _Ty issue is resolved.

// Padding bits should not participate in cmpxchg comparison starting in C++20.
// Clang does not have __builtin_zero_non_value_bits to exclude these bits to implement this C++20 feature.
// The EDG front-end substitutes everything and runs into incomplete types passed to atomic<T>.
#if _HAS_CXX20 && !defined(__clang__) /* TRANSITION, LLVM-46685 */ && !defined(__EDG__)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Sure that's not a normal use case problem, but neither is this whole feature)

Indeed.

I'm OK with leaving it '20 only.

Copy link
Member

@BillyONeal BillyONeal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This passed in the DevDiv system.

@BillyONeal
Copy link
Member

stl/inc/atomic Outdated
__builtin_zero_non_value_bits(_Ptr());
}

_Ty& _Ref() noexcept {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-existing:

Suggested change
_Ty& _Ref() noexcept {
_NODISCARD _Ty& _Ref() noexcept {

stl/inc/atomic Outdated
return reinterpret_cast<_Ty&>(_Storage);
}

_Ty* _Ptr() noexcept {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this function is newly added, let's make it nodiscard:

Suggested change
_Ty* _Ptr() noexcept {
_NODISCARD _Ty* _Ptr() noexcept {

stl/inc/atomic Outdated
}

_Ty* _Ptr() noexcept {
return reinterpret_cast<_Ty*>(_STD addressof(_Storage));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_Storage is unsigned char[N] so it's immune to operator& evil:

Suggested change
return reinterpret_cast<_Ty*>(_STD addressof(_Storage));
return reinterpret_cast<_Ty*>(_Storage);

stl/inc/atomic Outdated
_STD_BEGIN
// STRUCT TEMPLATE _Storage_for
struct _Form_mask_t {};
_INLINE_VAR constexpr _Form_mask_t _Form_mask;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C++14 had an annoying rule about default-init of const objects. That was fixed in C++17, but (1) even if it's not causing compiler errors in the tests, I'm worried about our compiler support matrix and (2) there are 0 occurrences of _INLINE_VAR constexpr \w+ \w+; in the STL, but 6 of _INLINE_VAR constexpr \w+ \w+\{\}; (namely adopt_lock, defer_lock, try_to_lock, ignore, piecewise_construct, and allocator_arg). There are an additional 5 occurrences of inline constexpr \w+ \w+\{\}; (all in C++17+ code) where the braces aren't necessary, but provided. There are 5 occurrences of inline constexpr \w+ \w+; all in ranges (ranges::cbegin etc.) where we omit the braces because it's C++20.

Suggested change
_INLINE_VAR constexpr _Form_mask_t _Form_mask;
_INLINE_VAR constexpr _Form_mask_t _Form_mask{};

stl/inc/atomic Outdated
template <class _Ty>
inline constexpr bool _Cmpxchg_has_padding_bits_v =
!has_unique_object_representations_v<_Ty> && !is_floating_point_v<_Ty>;
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing #endif comment.

stl/inc/atomic Outdated
break;
if constexpr (_Cmpxchg_has_padding_bits_v<_Ty>) {
_Storage_for<_Ty> _Mask{_Form_mask};
const long long _Mask_val = _Atomic_reinterpret_as<long long>(_Mask._Ref());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

long long here appears to be incorrect, since this is for 1-byte _Ty. (Indeed, 2-byte below uses short.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦‍♂️ Sorry for murdering your poor boy @AlexGuteniev

};
#pragma warning(pop)

#pragma pack(push)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is packing being pushed/popped here, when it's apparently not being altered?


void operator&() const = delete;

void set(char v) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: Some, but not all, of the set() functions are being changed to take their parameters by const value.

template <class X, std::size_t S>
void test() {
static_assert(sizeof(X) == S, "Unexpected size");
static_assert(!std::has_unique_object_representations_v<X>, "No padding type");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test should directly include <type_traits> for std::has_unique_object_representations_v.

template <class X, std::size_t S>
void test() {
static_assert(sizeof(X) == S, "Unexpected size");
static_assert(!std::has_unique_object_representations_v<X>, "No padding type");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this static_assert message hard to understand (it is actually less clear than the condition being tested). Something like "Expected type to contain padding" would make sense.

Copy link
Contributor

@CaseyCarter CaseyCarter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find anything Stephan missed; LGTM after fixing his comments.

Co-authored-by: Casey Carter <cartec69@gmail.com>
@AlexGuteniev
Copy link
Contributor Author

@BillyONeal, have you noticed a comment where I am proposing to get back to the almost original design except handling atomic_ref differently - clear pading with atomic and after first fail?

#1029 (comment)

@BillyONeal
Copy link
Member

@BillyONeal, have you noticed a comment where I am proposing to get back to the almost original design except handling atomic_ref differently - clear pading with atomic and after first fail?

#1029 (comment)

That looks OK to me (although since this is ~ready to land and atomic_ref is not yet ready to land I would prefer this land first)

@AlexGuteniev
Copy link
Contributor Author

Ok, let's have this, and I'll propose another option with another PR after that (and by having PR on top of this, it would be easier to evaluate just the difference)

Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
@BillyONeal
Copy link
Member

@AlexGuteniev I guess I misunderstood then? Since it seems your proposal there only affects atomic_ref, not atomic.

@AlexGuteniev
Copy link
Contributor Author

AlexGuteniev commented Jul 21, 2020

@AlexGuteniev I guess I misunderstood then? Since it seems your proposal there only affects atomic_ref, not atomic.

The proposal

  • changes atomic to the originally designed (use __builtin_zero_non_value_bits in constructor of atomic, store, exchange, and for desired value of compare_exchange_*)
  • on top of that, for atomic_ref does not add anything to constructor. But adds another step in compare_exchange_*: if type may have padding, and padding bits mismatched, atomically zero them in storage lock and, and retry CAS just once.

Unfortunately, yes, this proposal is:

  • not uniform for atomic and atomic_ref, so has to go after atomic_ref
  • has even slightly more complexity that the original design (atomically zero atomic_ref in constructor vs in failed CAS).

The good side is avoiding inner CAS loop while still keeping atomic_ref constructor no-op.

@BillyONeal
Copy link
Member

@AlexGuteniev: If it's more complex than the current plan of record and is only optimizing for the exceedingly rare atomic<type with padding bits> then I'm not sure it's worth it.

@BillyONeal
Copy link
Member

Thanks for your contribution! 🎉

@BillyONeal BillyONeal merged commit c10ae01 into microsoft:master Jul 21, 2020
@AlexGuteniev
Copy link
Contributor Author

is only optimizing for the exceedingly rare atomic<type with padding bits> then I'm not sure it's worth it.

Yes, for the simplicity, the current one is the best bet I think.
The proposal is only somewhat better for some use cases of atomic<type with padding bits> at the expense of being slightly worse for some others cases of atomic<type with padding bits>.

@AlexGuteniev AlexGuteniev deleted the cmpxchg branch July 21, 2020 08:05
@BillyONeal
Copy link
Member

And thanks for landing your first feature :D

@AlexGuteniev
Copy link
Contributor Author

And thank you for your patience :)

@StephanTLavavej StephanTLavavej linked an issue Jul 21, 2020 that may be closed by this pull request
CaseyCarter added a commit to CaseyCarter/STL that referenced this pull request Jul 28, 2020
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
Co-authored-by: Billy Robert O'Neal III <bion@microsoft.com>
Co-authored-by: Casey Carter <cartec69@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cxx20 C++20 feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P0528R3 Atomic Compare-And-Exchange With Padding Bits

4 participants