From 7b02b9f73ae154f67c9e6f5a16ca5a099ec8100b Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Mon, 15 Jun 2020 17:00:50 +0100 Subject: [PATCH] Add dap::initialize() and terminate() functions Can be used to explicitly control when the TypeInfo static initializers / destructors are called. Usually not needed. Issue: #40 --- include/dap/dap.h | 35 +++++++++++ include/dap/typeinfo.h | 15 +++++ include/dap/typeof.h | 48 ++++++--------- src/dap_test.cpp | 51 ++++++++++++++++ src/typeof.cpp | 129 ++++++++++++++++++++++++++++++++--------- 5 files changed, 221 insertions(+), 57 deletions(-) create mode 100644 include/dap/dap.h diff --git a/include/dap/dap.h b/include/dap/dap.h new file mode 100644 index 0000000..587e80c --- /dev/null +++ b/include/dap/dap.h @@ -0,0 +1,35 @@ +// Copyright 2020 Google LLC +// +// 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 +// +// https://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 dap_dap_h +#define dap_dap_h + +namespace dap { + +// Explicit library initialization and termination functions. +// +// cppdap automatically initializes and terminates its internal state using lazy +// static initialization, and so will usually work fine without explicit calls +// to these functions. +// However, if you use cppdap types in global state, you may need to call these +// functions to ensure that cppdap is not uninitialized before the last usage. +// +// Each call to initialize() must have a corresponding call to terminate(). +// It is undefined behaviour to call initialize() after terminate(). +void initialize(); +void terminate(); + +} // namespace dap + +#endif // dap_dap_h diff --git a/include/dap/typeinfo.h b/include/dap/typeinfo.h index 11f1f12..d99f277 100644 --- a/include/dap/typeinfo.h +++ b/include/dap/typeinfo.h @@ -37,6 +37,21 @@ struct TypeInfo { virtual void destruct(void*) const = 0; virtual bool deserialize(const Deserializer*, void*) const = 0; virtual bool serialize(Serializer*, const void*) const = 0; + + // create() allocates and constructs the TypeInfo of type T, registers the + // pointer for deletion on cppdap library termination, and returns the pointer + // to T. + template + static T* create(ARGS&&... args) { + auto typeinfo = new T(std::forward(args)...); + deleteOnExit(typeinfo); + return typeinfo; + } + + private: + // deleteOnExit() ensures that the TypeInfo is destructed and deleted on + // library termination. + static void deleteOnExit(TypeInfo*); }; } // namespace dap diff --git a/include/dap/typeof.h b/include/dap/typeof.h index ba2b045..1ed0dc7 100644 --- a/include/dap/typeof.h +++ b/include/dap/typeof.h @@ -29,18 +29,20 @@ struct BasicTypeInfo : public TypeInfo { constexpr BasicTypeInfo(std::string&& name) : name_(std::move(name)) {} // TypeInfo compliance - inline std::string name() const { return name_; } - inline size_t size() const { return sizeof(T); } - inline size_t alignment() const { return alignof(T); } - inline void construct(void* ptr) const { new (ptr) T(); } - inline void copyConstruct(void* dst, const void* src) const { + inline std::string name() const override { return name_; } + inline size_t size() const override { return sizeof(T); } + inline size_t alignment() const override { return alignof(T); } + inline void construct(void* ptr) const override { new (ptr) T(); } + inline void copyConstruct(void* dst, const void* src) const override { new (dst) T(*reinterpret_cast(src)); } - inline void destruct(void* ptr) const { reinterpret_cast(ptr)->~T(); } - inline bool deserialize(const Deserializer* d, void* ptr) const { + inline void destruct(void* ptr) const override { + reinterpret_cast(ptr)->~T(); + } + inline bool deserialize(const Deserializer* d, void* ptr) const override { return d->deserialize(reinterpret_cast(ptr)); } - inline bool serialize(Serializer* s, const void* ptr) const { + inline bool serialize(Serializer* s, const void* ptr) const override { return s->serialize(*reinterpret_cast(ptr)); } @@ -88,45 +90,33 @@ struct TypeOf { static const TypeInfo* type(); }; -// TypeOf for template types requires dynamic generation of type information, -// triggering the clang -Wexit-time-destructors warning. -// TODO(bclayton): See if there's a way to avoid this, without requiring manual -// instantiation of each type. -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wexit-time-destructors" -#endif // __clang__ - template struct TypeOf> { static inline const TypeInfo* type() { - static BasicTypeInfo> typeinfo("array<" + - TypeOf::type()->name() + ">"); - return &typeinfo; + static auto typeinfo = TypeInfo::create>>( + "array<" + TypeOf::type()->name() + ">"); + return typeinfo; } }; template struct TypeOf> { static inline const TypeInfo* type() { - static BasicTypeInfo> typeinfo("variant"); - return &typeinfo; + static auto typeinfo = + TypeInfo::create>>("variant"); + return typeinfo; } }; template struct TypeOf> { static inline const TypeInfo* type() { - static BasicTypeInfo> typeinfo("optional<" + - TypeOf::type()->name() + ">"); - return &typeinfo; + static auto typeinfo = TypeInfo::create>>( + "optional<" + TypeOf::type()->name() + ">"); + return typeinfo; } }; -#ifdef __clang__ -#pragma clang diagnostic pop -#endif // __clang__ - // DAP_OFFSETOF() macro is a generalization of the offsetof() macro defined in // . It evaluates to the offset of the given field, with fewer // restrictions than offsetof(). We cast the address '32' and subtract it again, diff --git a/src/dap_test.cpp b/src/dap_test.cpp index c895d0f..f31be46 100644 --- a/src/dap_test.cpp +++ b/src/dap_test.cpp @@ -12,10 +12,61 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "dap/dap.h" + #include "gmock/gmock.h" #include "gtest/gtest.h" +#include +#include +#include +#include + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } + +TEST(DAP, PairedInitializeTerminate) { + dap::initialize(); + dap::terminate(); +} + +TEST(DAP, NestedInitializeTerminate) { + dap::initialize(); + dap::initialize(); + dap::initialize(); + dap::terminate(); + dap::terminate(); + dap::terminate(); +} + +TEST(DAP, MultiThreadedInitializeTerminate) { + const size_t numThreads = 64; + + std::mutex mutex; + std::condition_variable cv; + size_t numInits = 0; + + std::vector threads; + threads.reserve(numThreads); + for (size_t i = 0; i < numThreads; i++) { + threads.emplace_back([&] { + dap::initialize(); + { + std::unique_lock lock(mutex); + numInits++; + if (numInits == numThreads) { + cv.notify_all(); + } else { + cv.wait(lock, [&] { return numInits == numThreads; }); + } + } + dap::terminate(); + }); + } + + for (auto& thread : threads) { + thread.join(); + } +} diff --git a/src/typeof.cpp b/src/typeof.cpp index 77c85ae..a5685d3 100644 --- a/src/typeof.cpp +++ b/src/typeof.cpp @@ -14,58 +14,131 @@ #include "dap/typeof.h" +#include +#include +#include + namespace { -struct NullTI : public dap::TypeInfo { - using null = dap::null; - inline std::string name() const { return "null"; } - inline size_t size() const { return sizeof(null); } - inline size_t alignment() const { return alignof(null); } - inline void construct(void* ptr) const { new (ptr) null(); } - inline void copyConstruct(void* dst, const void* src) const { - new (dst) null(*reinterpret_cast(src)); +// TypeInfos owns all the dap::TypeInfo instances. +struct TypeInfos { + // get() returns the TypeInfos singleton pointer. + // TypeInfos is constructed with an internal reference count of 1. + static TypeInfos* get(); + + // reference() increments the TypeInfos reference count. + inline void reference() { + assert(refcount.load() > 0); + refcount++; } - inline void destruct(void* ptr) const { - reinterpret_cast(ptr)->~null(); + + // release() decrements the TypeInfos reference count. + // If the reference count becomes 0, then the TypeInfos is destructed. + inline void release() { + if (--refcount == 0) { + this->~TypeInfos(); + } } - inline bool deserialize(const dap::Deserializer*, void*) const { - return true; - } - inline bool serialize(dap::Serializer*, const void*) const { return true; } + + struct NullTI : public dap::TypeInfo { + using null = dap::null; + inline std::string name() const override { return "null"; } + inline size_t size() const override { return sizeof(null); } + inline size_t alignment() const override { return alignof(null); } + inline void construct(void* ptr) const override { new (ptr) null(); } + inline void copyConstruct(void* dst, const void* src) const override { + new (dst) null(*reinterpret_cast(src)); + } + inline void destruct(void* ptr) const override { + reinterpret_cast(ptr)->~null(); + } + inline bool deserialize(const dap::Deserializer*, void*) const override { + return true; + } + inline bool serialize(dap::Serializer*, const void*) const override { + return true; + } + }; + + dap::BasicTypeInfo boolean = {"boolean"}; + dap::BasicTypeInfo string = {"string"}; + dap::BasicTypeInfo integer = {"integer"}; + dap::BasicTypeInfo number = {"number"}; + dap::BasicTypeInfo object = {"object"}; + dap::BasicTypeInfo any = {"any"}; + NullTI null; + std::vector> types; + + private: + TypeInfos() = default; + ~TypeInfos() = default; + std::atomic refcount = {1}; }; -static dap::BasicTypeInfo booleanTI("boolean"); -static dap::BasicTypeInfo stringTI("string"); -static dap::BasicTypeInfo integerTI("integer"); -static dap::BasicTypeInfo numberTI("number"); -static dap::BasicTypeInfo objectTI("object"); -static dap::BasicTypeInfo anyTI("any"); -static NullTI nullTI; +// aligned_storage() is a replacement for std::aligned_storage that isn't busted +// on older versions of MSVC. +template +struct aligned_storage { + struct alignas(ALIGNMENT) type { + unsigned char data[SIZE]; + }; +}; + +TypeInfos* TypeInfos::get() { + static aligned_storage::type memory; + + struct Instance { + TypeInfos* ptr() { return reinterpret_cast(memory.data); } + Instance() { new (ptr()) TypeInfos(); } + ~Instance() { ptr()->release(); } + }; + + static Instance instance; + return instance.ptr(); +} } // namespace namespace dap { const TypeInfo* TypeOf::type() { - return &booleanTI; + return &TypeInfos::get()->boolean; } + const TypeInfo* TypeOf::type() { - return &stringTI; + return &TypeInfos::get()->string; } + const TypeInfo* TypeOf::type() { - return &integerTI; + return &TypeInfos::get()->integer; } + const TypeInfo* TypeOf::type() { - return &numberTI; + return &TypeInfos::get()->number; } + const TypeInfo* TypeOf::type() { - return &objectTI; + return &TypeInfos::get()->object; } + const TypeInfo* TypeOf::type() { - return &anyTI; + return &TypeInfos::get()->any; } + const TypeInfo* TypeOf::type() { - return &nullTI; + return &TypeInfos::get()->null; +} + +void TypeInfo::deleteOnExit(TypeInfo* ti) { + return TypeInfos::get()->types.emplace_back(std::unique_ptr(ti)); +} + +void initialize() { + TypeInfos::get()->reference(); +} + +void terminate() { + TypeInfos::get()->release(); } } // namespace dap