base: steal Chromium's NoDestructor.
Pillage from Chromium a wrapper type that skips destruction of its wrapped type, to avoid problems with premature destruction of variables with static lifetime. Test: libbase_test on host Change-Id: I7d4541f7b59f467b232d5c4f8250dc1ea45e28fa
This commit is contained in:
parent
8a243118db
commit
efc9a63bde
3 changed files with 161 additions and 0 deletions
|
@ -148,6 +148,7 @@ cc_test {
|
|||
"logging_test.cpp",
|
||||
"macros_test.cpp",
|
||||
"mapped_file_test.cpp",
|
||||
"no_destructor_test.cpp",
|
||||
"parsedouble_test.cpp",
|
||||
"parseint_test.cpp",
|
||||
"parsenetaddress_test.cpp",
|
||||
|
|
94
base/include/android-base/no_destructor.h
Normal file
94
base/include/android-base/no_destructor.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
// A wrapper that makes it easy to create an object of type T with static
|
||||
// storage duration that:
|
||||
// - is only constructed on first access
|
||||
// - never invokes the destructor
|
||||
// in order to satisfy the styleguide ban on global constructors and
|
||||
// destructors.
|
||||
//
|
||||
// Runtime constant example:
|
||||
// const std::string& GetLineSeparator() {
|
||||
// // Forwards to std::string(size_t, char, const Allocator&) constructor.
|
||||
// static const base::NoDestructor<std::string> s(5, '-');
|
||||
// return *s;
|
||||
// }
|
||||
//
|
||||
// More complex initialization with a lambda:
|
||||
// const std::string& GetSessionNonce() {
|
||||
// static const base::NoDestructor<std::string> nonce([] {
|
||||
// std::string s(16);
|
||||
// crypto::RandString(s.data(), s.size());
|
||||
// return s;
|
||||
// }());
|
||||
// return *nonce;
|
||||
// }
|
||||
//
|
||||
// NoDestructor<T> stores the object inline, so it also avoids a pointer
|
||||
// indirection and a malloc. Also note that since C++11 static local variable
|
||||
// initialization is thread-safe and so is this pattern. Code should prefer to
|
||||
// use NoDestructor<T> over:
|
||||
// - A function scoped static T* or T& that is dynamically initialized.
|
||||
// - A global base::LazyInstance<T>.
|
||||
//
|
||||
// Note that since the destructor is never run, this *will* leak memory if used
|
||||
// as a stack or member variable. Furthermore, a NoDestructor<T> should never
|
||||
// have global scope as that may require a static initializer.
|
||||
template <typename T>
|
||||
class NoDestructor {
|
||||
public:
|
||||
// Not constexpr; just write static constexpr T x = ...; if the value should
|
||||
// be a constexpr.
|
||||
template <typename... Args>
|
||||
explicit NoDestructor(Args&&... args) {
|
||||
new (storage_) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Allows copy and move construction of the contained type, to allow
|
||||
// construction from an initializer list, e.g. for std::vector.
|
||||
explicit NoDestructor(const T& x) { new (storage_) T(x); }
|
||||
explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); }
|
||||
|
||||
NoDestructor(const NoDestructor&) = delete;
|
||||
NoDestructor& operator=(const NoDestructor&) = delete;
|
||||
|
||||
~NoDestructor() = default;
|
||||
|
||||
const T& operator*() const { return *get(); }
|
||||
T& operator*() { return *get(); }
|
||||
|
||||
const T* operator->() const { return get(); }
|
||||
T* operator->() { return get(); }
|
||||
|
||||
const T* get() const { return reinterpret_cast<const T*>(storage_); }
|
||||
T* get() { return reinterpret_cast<T*>(storage_); }
|
||||
|
||||
private:
|
||||
alignas(T) char storage_[sizeof(T)];
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
66
base/no_destructor_test.cpp
Normal file
66
base/no_destructor_test.cpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "android-base/no_destructor.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
struct __attribute__((packed)) Bomb {
|
||||
Bomb() : magic_(123) {}
|
||||
|
||||
~Bomb() { exit(42); }
|
||||
|
||||
int get() const { return magic_; }
|
||||
|
||||
private:
|
||||
[[maybe_unused]] char padding_;
|
||||
int magic_;
|
||||
};
|
||||
|
||||
TEST(no_destructor, bomb) {
|
||||
ASSERT_EXIT(({
|
||||
{
|
||||
Bomb b;
|
||||
if (b.get() != 123) exit(1);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}),
|
||||
::testing::ExitedWithCode(42), "");
|
||||
}
|
||||
|
||||
TEST(no_destructor, defused) {
|
||||
ASSERT_EXIT(({
|
||||
{
|
||||
android::base::NoDestructor<Bomb> b;
|
||||
if (b->get() != 123) exit(1);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}),
|
||||
::testing::ExitedWithCode(0), "");
|
||||
}
|
||||
|
||||
TEST(no_destructor, operators) {
|
||||
android::base::NoDestructor<Bomb> b;
|
||||
const android::base::NoDestructor<Bomb>& c = b;
|
||||
ASSERT_EQ(123, b.get()->get());
|
||||
ASSERT_EQ(123, b->get());
|
||||
ASSERT_EQ(123, (*b).get());
|
||||
ASSERT_EQ(123, c.get()->get());
|
||||
ASSERT_EQ(123, c->get());
|
||||
ASSERT_EQ(123, (*c).get());
|
||||
}
|
Loading…
Reference in a new issue