116 lines
4.7 KiB
C++
116 lines
4.7 KiB
C++
// Copyright 2019 The MediaPipe Authors.
|
|
//
|
|
// 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.
|
|
|
|
#ifndef MEDIAPIPE_DEPS_NO_DESTRUCTOR_H_
|
|
#define MEDIAPIPE_DEPS_NO_DESTRUCTOR_H_
|
|
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
namespace mediapipe {
|
|
|
|
// NoDestructor<T> is a wrapper around an object of type T that
|
|
// * stores the object of type T inline inside NoDestructor<T>
|
|
// * eagerly forwards constructor arguments to it (i.e. acts like T in terms
|
|
// of construction)
|
|
// * provides access to the object of type T like a pointer via ->, *, and get()
|
|
// (note that const NoDestructor<T> works like a pointer to const T)
|
|
// * never calls T's destructor for the object
|
|
// (hence NoDestructor<T> objects created on the stack or as member variables
|
|
// will lead to memory and/or resource leaks)
|
|
//
|
|
// One key use case of NoDestructor (which in itself is not lazy) is optimizing
|
|
// the following pattern of safe on-demand construction of an object with
|
|
// non-trivial constructor in static storage without destruction ever happening:
|
|
// const std::string& MyString() {
|
|
// static std::string* x = new std::string("foo"); // note the "static"
|
|
// return *x;
|
|
// }
|
|
// By using NoDestructor we do not need to involve heap allocation and
|
|
// corresponding pointer following (and hence extra CPU cache usage/needs)
|
|
// on each access:
|
|
// const std::string& MyString() {
|
|
// static NoDestructor<std::string> x("foo");
|
|
// return *x;
|
|
// }
|
|
// Since C++11 this static-in-a-function pattern results in exactly-once,
|
|
// thread-safe, on-demand construction of an object, and very fast access
|
|
// thereafter (the cost is a few extra cycles).
|
|
// NoDestructor makes accesses even faster by storing the object inline in
|
|
// static storage.
|
|
//
|
|
// Note that:
|
|
// * Since destructor is never called, the object lives on during program exit
|
|
// and can be safely accessed by any threads that have not been joined.
|
|
// * This static-in-a-function NoDestructor usage pattern should be preferred
|
|
// to uses of gtl::LazyStaticPtr in new code.
|
|
//
|
|
// Also note that
|
|
// static NoDestructor<NonPOD> ptr(whatever);
|
|
// can safely replace
|
|
// static NonPOD* ptr = new NonPOD(whatever);
|
|
// or
|
|
// static NonPOD obj(whatever);
|
|
// at file-level scope when the safe static-in-a-function pattern is infeasible
|
|
// to use for some good reason.
|
|
// All three of the NonPOD patterns above suffer from the same issue that
|
|
// initialization of that object happens non-thread-safely at
|
|
// a globally-undefined point during initialization of static-storage objects,
|
|
// but NoDestructor<> usage provides both the safety of having the object alive
|
|
// during program exit sequence and the performance of not doing extra memory
|
|
// dereference on access.
|
|
//
|
|
template <typename T>
|
|
class NoDestructor {
|
|
public:
|
|
typedef T element_type;
|
|
|
|
// Forwards arguments to the T's constructor: calls T(args...).
|
|
template <typename... Ts,
|
|
// Disable this overload when it might collide with copy/move.
|
|
typename std::enable_if<
|
|
!std::is_same<void(typename std::decay<Ts>::type...),
|
|
void(NoDestructor)>::value,
|
|
int>::type = 0>
|
|
explicit NoDestructor(Ts&&... args) {
|
|
new (&space_) T(std::forward<Ts>(args)...);
|
|
}
|
|
|
|
// Forwards copy and move construction for T. Enables usage like this:
|
|
// static NoDestructor<std::array<std::string, 3>> x{{{"1", "2", "3"}}};
|
|
// static NoDestructor<std::vector<int>> x{{1, 2, 3}};
|
|
explicit NoDestructor(const T& x) { new (&space_) T(x); }
|
|
explicit NoDestructor(T&& x) { new (&space_) T(std::move(x)); }
|
|
|
|
// No copying.
|
|
NoDestructor(const NoDestructor&) = delete;
|
|
NoDestructor& operator=(const NoDestructor&) = delete;
|
|
|
|
// Pretend to be a smart pointer to T with deep constness.
|
|
// Never returns a null pointer.
|
|
T& operator*() { return *get(); }
|
|
T* operator->() { return get(); }
|
|
T* get() { return reinterpret_cast<T*>(&space_); }
|
|
const T& operator*() const { return *get(); }
|
|
const T* operator->() const { return get(); }
|
|
const T* get() const { return reinterpret_cast<const T*>(&space_); }
|
|
|
|
private:
|
|
typename std::aligned_storage<sizeof(T), alignof(T)>::type space_;
|
|
};
|
|
|
|
} // namespace mediapipe
|
|
|
|
#endif // MEDIAPIPE_DEPS_NO_DESTRUCTOR_H_
|