Line data Source code
1 : /* 2 : * 3 : * Copyright (c) 2023 Project CHIP Authors 4 : * 5 : * Licensed under the Apache License, Version 2.0 (the "License"); 6 : * you may not use this file except in compliance with the License. 7 : * You may obtain a copy of the License at 8 : * 9 : * http://www.apache.org/licenses/LICENSE-2.0 10 : * 11 : * Unless required by applicable law or agreed to in writing, software 12 : * distributed under the License is distributed on an "AS IS" BASIS, 13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 : * See the License for the specific language governing permissions and 15 : * limitations under the License. 16 : */ 17 : 18 : #pragma once 19 : 20 : #include <lib/core/CHIPConfig.h> 21 : 22 : #include <new> 23 : 24 : namespace chip { 25 : 26 : /** 27 : * A wrapper for global object that enables initialization and destruction to 28 : * be configured by the platform via `CHIP_CONFIG_GLOBALS_*` options. 29 : * 30 : * The contained object of type T is default constructed, possibly lazily. 31 : * 32 : * This class is generally NOT thread-safe; external synchronization is required. 33 : */ 34 : template <class T> 35 : class Global 36 : { 37 : public: 38 : /// Returns the global object, initializing it if necessary. 39 : /// NOT thread-safe, external synchronization is required. 40 11469 : T & get() { return _get(); } 41 10 : T * operator->() { return &_get(); } 42 : 43 : #if CHIP_CONFIG_GLOBALS_LAZY_INIT 44 : public: 45 : constexpr Global() = default; 46 : ~Global() = default; 47 : 48 : private: 49 : // Zero-initialize everything. We should technically leave mStorage uninitialized, 50 : // but that can sometimes cause clang to be unable to constant-initialize the object. 51 : alignas(T) unsigned char mStorage[sizeof(T)] = {}; 52 : bool mInitialized = false; 53 : 54 : T & _value() { return *reinterpret_cast<T *>(mStorage); } 55 : 56 : T & _get() 57 : { 58 : if (!mInitialized) 59 : { 60 : new (mStorage) T(); 61 : mInitialized = true; 62 : #if !CHIP_CONFIG_GLOBALS_NO_DESTRUCT 63 : CHIP_CXA_ATEXIT(&destroy, this); 64 : #endif // CHIP_CONFIG_GLOBALS_NO_DESTRUCT 65 : } 66 : return _value(); 67 : } 68 : 69 : static void destroy(void * context) { static_cast<Global<T> *>(context)->_value().~T(); } 70 : 71 : #else // CHIP_CONFIG_GLOBALS_LAZY_INIT 72 : public: 73 96 : constexpr Global() : mValue() {} 74 : 75 : private: 76 11479 : T & _get() { return mValue; } 77 : 78 : #if CHIP_CONFIG_GLOBALS_NO_DESTRUCT 79 : public: 80 : // For not-trivially-destructible T, hiding it inside a union means the destructor 81 : // won't get called automatically, however our own default destructor will be implicitly 82 : // deleted. So Global<T> is technically not trivially-destructible, however the compiler 83 : // tends to be able to optimize away the empty destructor call in practice. Getting around 84 : // this cleanly requires using the "storage" approach used in the lazy variant, however 85 : // this would require std::construct_at from C++20 to get constexpr initialization. 86 : ~Global() {} // not "= default" 87 : 88 : private: 89 : union 90 : { 91 : T mValue; 92 : }; 93 : #else // CHIP_CONFIG_GLOBALS_NO_DESTRUCT 94 : public: 95 177 : ~Global() = default; 96 : 97 : private: 98 : T mValue; 99 : #endif // CHIP_CONFIG_GLOBALS_NO_DESTRUCT 100 : #endif // CHIP_CONFIG_GLOBALS_LAZY_INIT 101 : }; 102 : 103 : } // namespace chip