Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable trivially_constructible test for atomic on MSVC++. #654

Merged
merged 2 commits into from
Jun 24, 2020
Merged

Disable trivially_constructible test for atomic on MSVC++. #654

merged 2 commits into from
Jun 24, 2020

Conversation

BillyONeal
Copy link
Contributor

MSVC++ implements P0883 unconditionally, which changes the rules for std::atomic. It removes atomic's trivial constructor, and makes the default constructor value initialize the T.

Note that Mongo was not following the C++11 rules, because it used the atomic before calling atomic_init first. MSVC++ never implemented the C++11 rules and previously default initialized the T.

All versions of MSVC++ will provide constant initialization of the guarded value "current_instance". In old versions, atomic didn't implement P0883 due to bugs in the constexpr evaluator; in current versions the constexpr evaluator was fixed and atomic value initializes unconditionally. Therefore, this PR disables the check whenever MSVC++'s standard library is detected.

See microsoft/STL#661 for further discussion.

MSVC++ implements P0883 unconditionally, which changes the rules for std::atomic. It removes atomic's trivial constructor, and makes the default constructor value initialize the T.

Note that Mongo was not following the C++11 rules, because it used the atomic before calling atomic_init first. MSVC++ never implemented the C++11 rules and previously default initialized the T.

All versions of MSVC++ will provide constant initialization of the guarded value "current_instance". In old versions, atomic didn't implement P0883 due to bugs in the constexpr evaluator; in current versions the constexpr evaluator was fixed and atomic value initializes unconditionally. Therefore, this PR disables the check whenever MSVC++'s standard library is detected.

See microsoft/STL#661 for further discussion.
@acmorrow
Copy link
Contributor

@BillyONeal - I find the following language in the documentation for std::atomic_init on cppreference:

This function is provided for compatibility with C. If the compatibility is not required, std::atomic may be initialized through their non-default constructors.

The atomic is being constructed with an argument, so I think we are invoking one of its non-default constructors. Or perhaps we have fallen into one of the traps that motivated P0883? Are we actually violating the rules? Could you clarify?

Regarding the removal of the static_assert, I think this can't be the right answer. It has been many years since I worked on this code (and it has changed since my original formulation). But the static assertion was there for a reason. If the rules have changed and that static_assert is no longer true, it suggests that an important assumption is no longer valid.

@BillyONeal
Copy link
Contributor Author

The atomic is being constructed with an argument, so I think we are invoking one of its non-default constructors.

Good point. But if that's true, the trivial constructability static assert doesn't mean anything, since you're not engaging the trivial ctor even if it were trivial. So maybe the right fix is to delete it outright?

@BillyONeal
Copy link
Contributor Author

If the rules have changed and that static_assert is no longer true, it suggests that an important assumption is no longer valid.

Maybe; I'm ill equipped to make global assumptions about the code in question.

@BillyONeal
Copy link
Contributor Author

BillyONeal commented May 27, 2020

@acmorrow According to "blame" that static assert was added in db943d5 as a means of avoiding a dynamic initializer but the author didn't realize that the assertion they made is unrelated to what they actually wrote.

EDIT: And by "they" in this case turns out I meant "you" :)

@acmorrow
Copy link
Contributor

@BillyONeal - Yup, I started this. As I noted, I don't work actively on this project right now, though others do, and it has been quite a few years since I wrote that. I will need to go do some digging to uncover the reasoning behind the code. I remember that we spent considerable time evaluating solutions and that we felt we were in the right with out decision, but of course we may have got it wrong!

@BillyONeal
Copy link
Contributor Author

I will need to go do some digging to uncover the reasoning behind the code.

The comment on the commit indicates that it's intending to avoid a dynamic initializer (presumably to avoid the 'static initialization order fiasco') which would make sense given that it's asserting triviality. Unfortunately both I and the original commit didn't pay attention that the default ctor isn't being used here so old versions of VC would give you a dynamic initializer anyway.

BillyONeal added a commit to microsoft/vcpkg that referenced this pull request May 29, 2020
@lidanger
Copy link

lidanger commented May 30, 2020

So should I remove this line in v3.5 or change decltype(current_instance) to v_noabi::instance *?

@acmorrow
Copy link
Contributor

@BillyONeal - Sorry for the delay. I did some archaeology on this. The relevant background is in:

It appears that the code has evolved since then, notably I think the ability to recreate the instance was removed as part of https://jira.mongodb.org/browse/CXX-1300, since the underlying C driver couldn't faithfully support it.

The idea in #513 was indeed to avoid a dynamic initializer, by ensuring that current_instance gets static/constant initialization.

The intention of the static_asserts was probably aimed as best as we knew how at ensuring that 1) we would get such initialization and 2) that if someone "improved" the code in such a way that violated that intention that they would be alerted.

Given that I spend a lot less time on the details of C++ these days, can you clarify for me a few things?

  • Given that this is the C++11 driver, we must abide by C++11 rules. Does C++11 ensure constant initialization fo the declaration of current_instance as declared in this code?
  • If later versions of the standard have changed the rules here, is there anything we need to do differently on a per-version basis?
  • What are the strongest assertions we can write that will trip if someone were to alter this code in a way that broke the assumption of constant initialization for current_instance?

@BillyONeal
Copy link
Contributor Author

Does C++11 ensure constant initialization fo the declaration of current_instance as declared in this code?

Yes, but lots of compilers (e.g. VS2019 before 16.6) were buggy about it.

If later versions of the standard have changed the rules here, is there anything we need to do differently on a per-version basis?

Since you're using the constructor that initializes the contained value, I don't believe so. If you were default initializing it you would have needed to call atomic_init under the C++11 rules (that AFAIK nobody implements).

What are the strongest assertions we can write that will trip if someone were to alter this code in a way that broke the assumption of constant initialization for current_instance?

I don't believe there are any assertions you can write. Note that the existing assertion does not actually assert anything about current_instance.

@acmorrow
Copy link
Contributor

What are the strongest assertions we can write that will trip if someone were to alter this code in a way that broke the assumption of constant initialization for current_instance?

I don't believe there are any assertions you can write. Note that the existing assertion does not actually assert anything about current_instance.

@BillyONeal

Sure, but it asserts something about the type of current instance. However, that assertion is also irrelevant since we are using the value-taking constructor per prior discussion.

Given all this, and your other answers above, my recommendation would be to update this PR to unconditionally remove the trivially_constructible test on all platforms, and then I'll be happy to get this merged.

@BillyONeal
Copy link
Contributor Author

Done.

@acmorrow
Copy link
Contributor

@BillyONeal - LGTM, thank you for being patient while I sorted through the history. @kevinAlbs, I think this should get merged, but probably by one of the active C++ driver developers.

@kevinAlbs kevinAlbs self-requested a review June 18, 2020 17:24
@samantharitter
Copy link
Contributor

These changes LGTM, with P0883 that static_assert is not useful and I think it's a good idea to get rid of it. But, @BillyONeal can you explain in more detail why we expect constant initialization for current_instance?

Copy link
Collaborator

@kevinAlbs kevinAlbs left a comment

Choose a reason for hiding this comment

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

LGTM. Thank you @BillyONeal for the contribution and explanation, and to @acm for the background. It seems reasonable to remove this assertion if we were not invoking the default constructor.

@BillyONeal
Copy link
Contributor Author

@BillyONeal can you explain in more detail why we expect constant initialization for current_instance?

Because it's calling a constexpr constructor.

Note that on Visual Studio before 2019 version 16.6, it is not getting constant initialization.

@kevinAlbs
Copy link
Collaborator

Merging, thanks again @BillyONeal!

@kevinAlbs kevinAlbs merged commit 458cd40 into mongodb:master Jun 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants