-
Notifications
You must be signed in to change notification settings - Fork 407
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
Warn in ScopeGuard about illegal usage #5250
Conversation
class KOKKOS_ATTRIBUTE_NODISCARD ScopeGuard { | ||
public: | ||
#if defined(__has_cpp_attribute) && __has_cpp_attribute(nodiscard) >= 201907 | ||
KOKKOS_ATTRIBUTE_NODISCARD | ||
#endif | ||
ScopeGuard(int& argc, char* argv[]) { | ||
sg_init = false; | ||
#ifdef KOKKOS_ENABLE_DEPRECATION_WARNINGS | ||
if (is_initialized()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (is_initialized()) { | |
if (is_initialized() || is_finalized()) { |
I hope you will appreciate the irony here. Technically the code you propose here has a bug that the change I suggest above fixes. With the definition proposed in #5245 that would have be right.
If you are trying to only emit one of the warning your code should read
if (is_finalized()) {
std::cerr << Impl::scopeguard_create_after_finalize_warning()
<< std::endl;
} else if (is_initialized() || is_finalized()) { // obviously the || is_finallized() can be discarded
// but I can't believe you can write that code and not see
// I was right and that the option 2 in #5208 is more logical
std::cerr << Impl::scopeguard_create_after_initialize_warning()
<< std::endl;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the use of function to produce the strings and the general organization besides the comments I made
core/src/Kokkos_Core.hpp
Outdated
ScopeGuard(int& argc, char* argv[]) { | ||
if (is_initialized()) { | ||
Kokkos::abort(Impl::scopeguard_create_after_init_warning().c_str()); | ||
} | ||
if (is_finalized()) { | ||
Kokkos::abort(Impl::scopeguard_create_after_finalize_warning().c_str()); | ||
} | ||
initialize(argc, argv); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Commenting so I get a souvenir after Christian renames everything
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good now.
Did you plan on adding tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me but you still want to add tests, right?
core/src/Kokkos_Core.hpp
Outdated
#if defined(__has_cpp_attribute) && __has_cpp_attribute(nodiscard) >= 201907 | ||
KOKKOS_ATTRIBUTE_NODISCARD | ||
#endif | ||
|
||
ScopeGuard(int& argc, char* argv[]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#if defined(__has_cpp_attribute) && __has_cpp_attribute(nodiscard) >= 201907 | |
KOKKOS_ATTRIBUTE_NODISCARD | |
#endif | |
ScopeGuard(int& argc, char* argv[]) { | |
#if defined(__has_cpp_attribute) && __has_cpp_attribute(nodiscard) >= 201907 | |
KOKKOS_ATTRIBUTE_NODISCARD | |
#endif | |
ScopeGuard(int& argc, char* argv[]) { |
core/src/Kokkos_Core.hpp
Outdated
finalize(); | ||
} | ||
|
||
// private: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// private: |
core/src/Kokkos_Core.hpp
Outdated
@@ -287,6 +393,7 @@ std::vector<ExecSpace> partition_space(ExecSpace space, | |||
|
|||
// Specializations requires after core definitions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Specializations requires after core definitions | |
// Specializations required after core definitions |
?
#if defined(__has_cpp_attribute) && __has_cpp_attribute(nodiscard) >= 201907 | ||
KOKKOS_ATTRIBUTE_NODISCARD | ||
#endif | ||
ScopeGuard( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The single argument constructor should be explicit
.
explicit ScopeGuard(const InitializationSettings& settings) { /* ... */ }
ScopeGuard() : ScopeGuard(InitializationSettings()) {}
Note: I haven't quite thought through the implications if we end up keeping InitArguments
around for a while as well, as that might require its own (possibly implicit) forwarding constructor for backwards compatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed on the explicit
comment
Interaction with InitArguments
will be fine https://godbolt.org/z/7scMhjMj6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before we added InitializationSettings
, the following three initialization syntaxes work:
InitArguments ia;
ScopeGuard sc0 = ia;
ScopeGuard sc1(ia);
ScopeGuard sc2{ia};
If we only do explicit ScopeGuard(const InitializationSettings& settings) { /* ... */ }
, sc0
no longer compiles.
For the record, I'm fine with breaking that. I'm just pointing out that it is a breaking change (albeit unrelated to the explicitness of the constructor).
If we don't want it to break, we need to add:
/* implicit */ ScopeGuard(const InitArguments& ia) : ScopeGuard(InitializationSettings(ia)) {}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend to think it is fine to break the ScopeGuard sc0 = ia;
use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@crtrott you forgot to add explicit
here. Considering that this is the new code path, I would have been fine with omitting at the other place but it must be added here.
core/src/Kokkos_Core.hpp
Outdated
|
||
// private: | ||
ScopeGuard& operator=(const ScopeGuard&) = delete; | ||
ScopeGuard(const ScopeGuard&) = delete; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While not strictly necessary, please add the other two special member functions (Rule of 5):
ScopeGuard& operator=(ScopeGuard&&) = delete;
ScopeGuard(ScopeGuard&&) = delete;
I won't have time to write tests for 3.7 here is a standalone manual test #include <Kokkos_Core.hpp>
#include <cmath>
int main(int argc, char* argv[]) {
{
int create_after_init = argc > 1 ? atoi(argv[1]) : 0;
int create_after_final = argc > 2 ? atoi(argv[2]) : 0;
int create_after_other = argc > 3 ? atoi(argv[3]) : 0;
int destroy_after_final = argc > 4 ? atoi(argv[4]) : 0;
if(create_after_other) {
Kokkos::ScopeGuard guard1(argc,argv);
Kokkos::ScopeGuard guard2(argc,argv);
} else {
if(create_after_init || create_after_final)
Kokkos::initialize(argc, argv);
if(create_after_final)
Kokkos::finalize();
Kokkos::ScopeGuard guard(argc,argv);
if((!create_after_final && create_after_init) || destroy_after_final)
Kokkos::finalize();
}
}
} Test results deprecated_code disabled is this, I also confirmed that I get that output with just deprecation warnings on.
|
core/src/Kokkos_Core.hpp
Outdated
@@ -334,9 +335,11 @@ class KOKKOS_ATTRIBUTE_NODISCARD ScopeGuard { | |||
finalize(); | |||
} | |||
|
|||
// private: | |||
private: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am going to pretend I don't see that guy
"Do instead:\n" | ||
" std::unique_ptr<Kokkos::ScopeGuard> guard =\n" | ||
" !Kokkos::is_initialized() && !Kokkos::is_finalized()?\n" | ||
" new ScopeGuard(argc,argv) : nullptr;\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can live with it but I wanted to point out that this error will be raised even when the InitializationSettings
argument was used.
ScopeGuard& operator=(const ScopeGuard&) = delete; | ||
ScopeGuard& operator=(ScopeGuard&&) = delete; | ||
ScopeGuard(const ScopeGuard&) = delete; | ||
ScopeGuard(ScopeGuard&&) = delete; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these private?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because they were private before
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or at least meant to be private??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's more idiomatic not to have private copy constructors if you meant to delete them, right? This feels like part of a pre-C++11 approach where you couldn't use delete
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @masterleinad here.
Before C++11, the copy operations always existed for every class, so to make them noncopyable one made them private
and unimplemented (or privately derived from something like boost::noncopyable
, which did the same thing). Unimplemented always gave a linker error on an attempted copy, and making them private
sometimes gave a compile time access error (depending on usage).
No need for that now that we can delete
functions.
ScopeGuard& operator=(const ScopeGuard&) = delete; | ||
ScopeGuard& operator=(ScopeGuard&&) = delete; | ||
ScopeGuard(const ScopeGuard&) = delete; | ||
ScopeGuard(ScopeGuard&&) = delete; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @masterleinad here.
Before C++11, the copy operations always existed for every class, so to make them noncopyable one made them private
and unimplemented (or privately derived from something like boost::noncopyable
, which did the same thing). Unimplemented always gave a linker error on an attempted copy, and making them private
sometimes gave a compile time access error (depending on usage).
No need for that now that we can delete
functions.
I removed the private ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Failures were unrelated (machine running out of space) |
We (Damien and I) are considering a change of behavior and how to use it for ScopeGuard.
Specifically, we believe its current design is problematic in various ways, one of them being the silent ignoring of arguments handed to it if Kokkos was already initialized.
Besides better warnings in cases which were previously already broken (like creating a ScopeGuard after Kokkos::finalize() was called), the primary change is that you can't create a ScopeGuard after Kokkos::initialize was called. This also means that you can't create a ScopeGuard after any other ScopeGuard was created. The error message provided shows a way how to do nested scopeguards (as they might currently be in use) if really desired, but essentially its pushing more of the correctness check behavior on the user.
Please let us know what you think.
Note: this PR is missing tests, which I am working on.