timemory 3.3.0
Modular C++ Toolkit for Performance Analysis and Logging. Profiling API and Tools for C, C++, CUDA, Fortran, and Python. The C++ template API is essentially a framework to creating tools: it is designed to provide a unifying interface for recording various performance measurements alongside data logging and interfaces to other tools.
singleton.hpp
Go to the documentation of this file.
1// MIT License
2//
3// Copyright (c) 2020, The Regents of the University of California,
4// through Lawrence Berkeley National Laboratory (subject to receipt of any
5// required approvals from the U.S. Dept. of Energy). All rights reserved.
6//
7// Permission is hereby granted, free of charge, to any person obtaining a copy
8// of this software and associated documentation files (the "Software"), to
9// deal in the Software without restriction, including without limitation the
10// rights to use, copy, modify, merge, publish, distribute, sublicense, and
11// copies of the Software, and to permit persons to whom the Software is
12// furnished to do so, subject to the following conditions:
13//
14// The above copyright notice and this permission notice shall be included in
15// all copies or substantial portions of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23// IN THE SOFTWARE.
24
25/** \file utility/singleton.hpp
26 * \headerfile utility/singleton.hpp "timemory/utility/singleton.hpp"
27 * This is the C++ class provides thread-local singleton functionality with
28 * the ability to overload the destruction of the singleton (critical to automatic
29 * output at the end of the application)
30 *
31 */
32
33#pragma once
34
35#include "timemory/api.hpp"
37
38#include <cstddef>
39#include <functional>
40#include <list>
41#include <map>
42#include <memory>
43#include <mutex>
44#include <set>
45#include <thread>
46
47//======================================================================================//
48
49namespace tim
50{
51//
52template <typename Type,
53 typename PointerT = std::unique_ptr<Type, std::default_delete<Type>>,
54 typename TagT = TIMEMORY_API>
55class singleton;
56//
57template <>
58class singleton<void, void, void>
59{
60public:
61 using thread_t = std::thread;
62 using thread_id_t = std::thread::id;
63
64 // returns whether current thread is primary thread
65 static bool is_main_thread();
66
67 // the thread the main instance was created on
68 static thread_id_t main_thread_id() { return f_main_thread(); }
69
70 // function which just sets the main thread
71 static bool init();
72
73private:
74 template <typename TypeT, typename PointerT, typename TagT>
75 friend class singleton;
76
77 static TIMEMORY_NOINLINE thread_id_t& f_main_thread()
78 {
79 static auto _instance = std::this_thread::get_id();
80 return _instance;
81 }
82};
83
84//--------------------------------------------------------------------------------------//
85
86inline bool
88{
89 return (std::this_thread::get_id() == f_main_thread());
90}
91
92//--------------------------------------------------------------------------------------//
93
94inline bool
96{
97 (void) f_main_thread();
98 return true;
99}
100
101//--------------------------------------------------------------------------------------//
102
104
105//======================================================================================//
106
107/// \class tim::singleton
108/// \brief Thread-safe singleton management
109///
110template <typename Type, typename PointerT, typename TagT>
112{
113public:
115 using thread_id_t = std::thread::id;
116 using mutex_t = std::recursive_mutex;
117 using auto_lock_t = std::unique_lock<mutex_t>;
118 using pointer = Type*;
119 using children_t = std::set<pointer>;
120 using smart_pointer = PointerT;
121 using deleter_t = std::function<void(PointerT&)>;
122 using dtor_map_t = std::map<pointer, std::function<void()>>;
123
124 template <bool B, typename T = int>
125 using enable_if_t = typename std::enable_if<B, T>::type;
126
127public:
128 singleton();
129 ~singleton();
130
131 singleton(const singleton&) = delete;
132 singleton(singleton&&) = delete;
133 singleton& operator=(const singleton&) = delete;
135
136public:
137 // instance functions that initialize if nullptr
138 static pointer instance();
139 static pointer master_instance();
140
141 // instance functions that do not initialize
142 static smart_pointer& smart_instance() { return _local_instance(); }
143 static smart_pointer& smart_master_instance() { return _master_instance(); }
144
145 // for checking but not allocating
146 static pointer instance_ptr();
147 static pointer master_instance_ptr() { return f_master_instance(); }
148
149 // the thread the master instance was created on
150 static thread_id_t master_thread_id() { return f_master_thread(); }
151
152 static children_t children() { return f_children(); }
153 static bool is_master(pointer ptr) { return ptr == master_instance_ptr(); }
154 static bool is_master_thread();
155 static void insert(smart_pointer& itr);
156 static void remove(pointer itr);
157 static mutex_t& get_mutex() { return f_mutex(); }
158
159 void initialize();
160 void reset(pointer ptr);
161 void reset();
162
163private:
164 static TIMEMORY_NOINLINE smart_pointer& _local_instance()
165 {
166 static thread_local smart_pointer _instance = smart_pointer();
167 return _instance;
168 }
169 static TIMEMORY_NOINLINE smart_pointer& _master_instance()
170 {
171 static smart_pointer _instance = smart_pointer();
172 return _instance;
173 }
174
175 template <typename PtrT = PointerT>
176 deleter_t& get_deleter(
177 enable_if_t<std::is_same<PtrT, std::shared_ptr<Type>>::value> = 0);
178
179 template <typename PtrT = PointerT>
180 deleter_t& get_deleter(
181 enable_if_t<!std::is_same<PtrT, std::shared_ptr<Type>>::value> = 0);
182
183private:
184 struct persistent_data
185 {
186 mutex_t m_mutex;
187 thread_id_t m_master_thread = std::this_thread::get_id();
188 pointer m_master_instance = nullptr;
189 children_t m_children = {};
190 dtor_map_t m_dtors = {};
191
192 persistent_data() = default;
193 ~persistent_data() = default;
194 persistent_data(const persistent_data&) = delete;
195 persistent_data(persistent_data&&) = delete;
196 persistent_data& operator=(const persistent_data&) = delete;
197 persistent_data& operator=(persistent_data&&) = delete;
198
199 void reset()
200 {
201 m_master_instance = nullptr;
202 m_children.clear();
203 }
204 };
205
206 static TIMEMORY_NOINLINE persistent_data& f_persistent_data()
207 {
208 static persistent_data _instance{};
209 return _instance;
210 }
211
212 static thread_id_t& f_master_thread();
213 static mutex_t& f_mutex();
214 static pointer& f_master_instance();
215 static children_t& f_children();
216 static dtor_map_t& f_dtors();
217};
218
219//======================================================================================//
220
221template <typename Type, typename PointerT, typename TagT>
223singleton<Type, PointerT, TagT>::f_master_thread()
224{
225 return f_persistent_data().m_master_thread;
226}
227
228//--------------------------------------------------------------------------------------//
229
230template <typename Type, typename PointerT, typename TagT>
232singleton<Type, PointerT, TagT>::f_master_instance()
233{
234 return f_persistent_data().m_master_instance;
235}
236
237//--------------------------------------------------------------------------------------//
238
239template <typename Type, typename PointerT, typename TagT>
241singleton<Type, PointerT, TagT>::f_mutex()
242{
243 return f_persistent_data().m_mutex;
244}
245
246//--------------------------------------------------------------------------------------//
247
248template <typename Type, typename PointerT, typename TagT>
250singleton<Type, PointerT, TagT>::f_children()
251{
252 return f_persistent_data().m_children;
253}
254
255//--------------------------------------------------------------------------------------//
256
257template <typename Type, typename PointerT, typename TagT>
259singleton<Type, PointerT, TagT>::f_dtors()
260{
261 return f_persistent_data().m_dtors;
262}
263
264//--------------------------------------------------------------------------------------//
265
266template <typename Type, typename PointerT, typename TagT>
268{
269 initialize();
270}
271
272//--------------------------------------------------------------------------------------//
273
274template <typename Type, typename PointerT, typename TagT>
276{
277 if(std::this_thread::get_id() == f_master_thread())
278 f_master_instance() = nullptr;
279 auto& del = get_deleter();
280 if(del)
281 del(_master_instance());
282}
283
284//--------------------------------------------------------------------------------------//
285
286template <typename Type, typename PointerT, typename TagT>
287void
289{
290 if(!f_master_instance())
291 {
292 f_master_thread() = std::this_thread::get_id();
293 f_master_instance() = new Type{};
294 }
295}
296
297//--------------------------------------------------------------------------------------//
298
299template <typename Type, typename PointerT, typename TagT>
302{
303 if(std::this_thread::get_id() == f_master_thread())
304 {
305 return master_instance();
306 }
307 if(!_local_instance().get())
308 {
309 _local_instance().reset(new Type{});
310 insert(_local_instance());
311 }
312 return _local_instance().get();
313}
314
315//--------------------------------------------------------------------------------------//
316
317template <typename Type, typename PointerT, typename TagT>
320{
321 if(!f_master_instance())
322 {
323 f_master_thread() = std::this_thread::get_id();
324 f_master_instance() = new Type{};
325 }
326 return f_master_instance();
327}
328
329//--------------------------------------------------------------------------------------//
330
331template <typename Type, typename PointerT, typename TagT>
334{
335 return is_master_thread() ? f_master_instance() : _local_instance().get();
336}
337
338//--------------------------------------------------------------------------------------//
339
340template <typename Type, typename PointerT, typename TagT>
341bool
343{
344 return std::this_thread::get_id() == f_master_thread();
345}
346
347//--------------------------------------------------------------------------------------//
348
349template <typename Type, typename PointerT, typename TagT>
350void
352{
353 auto_lock_t _lk{ f_mutex(), std::defer_lock };
354 if(!_lk.owns_lock())
355 _lk.lock();
356 f_children().insert(itr.get());
357 f_dtors().emplace(itr.get(), [&]() { itr.reset(); });
358}
359
360//--------------------------------------------------------------------------------------//
361
362template <typename Type, typename PointerT, typename TagT>
363void
365{
366 if(!itr)
367 return;
368 auto_lock_t _lk{ f_mutex(), std::defer_lock };
369 if(!_lk.owns_lock())
370 _lk.lock();
371 for(auto litr = f_children().begin(); litr != f_children().end(); ++litr)
372 {
373 if(*litr == itr)
374 {
375 f_children().erase(litr);
376 break;
377 }
378 }
379 auto ditr = f_dtors().find(itr);
380 if(ditr != f_dtors().end())
381 f_dtors().erase(ditr);
382}
383
384//--------------------------------------------------------------------------------------//
385
386template <typename Type, typename PointerT, typename TagT>
387void
389{
390 if(is_master(ptr))
391 {
392 if(!f_dtors().empty())
393 {
394 dtor_map_t _dtors{};
395 std::swap(f_dtors(), _dtors);
396 for(auto& itr : _dtors)
397 itr.second();
398 }
399
400 if(_master_instance().get())
401 {
402 _master_instance().reset();
403 }
404 else if(f_master_instance())
405 {
406 auto& del = get_deleter();
407 del(_master_instance());
408 f_master_instance() = nullptr;
409 }
410 f_persistent_data().reset();
411 }
412 else
413 {
414 remove(_local_instance().get());
415 _local_instance().reset();
416 }
417}
418
419//--------------------------------------------------------------------------------------//
420
421template <typename Type, typename PointerT, typename TagT>
422void
424{
425 if(_local_instance())
426 {
427 remove(_local_instance().get());
428 _local_instance().reset();
429 }
430
431 if(is_master_thread())
432 {
433 if(!f_dtors().empty())
434 {
435 dtor_map_t _dtors{};
436 std::swap(f_dtors(), _dtors);
437 for(auto& itr : _dtors)
438 itr.second();
439 }
440
441 if(_master_instance().get())
442 {
443 _master_instance().reset();
444 }
445 else if(f_master_instance())
446 {
447 auto& del = get_deleter();
448 del(_master_instance());
449 f_master_instance() = nullptr;
450 }
451 }
452 f_persistent_data().reset();
453}
454
455//--------------------------------------------------------------------------------------//
456
457template <typename Type, typename PointerT, typename TagT>
458template <typename PtrT>
461 enable_if_t<std::is_same<PtrT, std::shared_ptr<Type>>::value>)
462{
463 static deleter_t _instance = [](PointerT&) {};
464 return _instance;
465}
466
467//--------------------------------------------------------------------------------------//
468
469template <typename Type, typename PointerT, typename TagT>
470template <typename PtrT>
472 singleton<Type, PointerT, TagT>::get_deleter(
473 enable_if_t<!std::is_same<PtrT, std::shared_ptr<Type>>::value>)
474{
475 static deleter_t _instance = [](PointerT& _master) {
476 auto& del = _master.get_deleter();
477 del(_master.get());
478 _master.reset(nullptr);
479 };
480 return _instance;
481}
482
483//--------------------------------------------------------------------------------------//
484
485namespace internal
486{
487namespace
488{
489// ensure assigned before main
490// NOLINTNEXTLINE
491bool singleton_main_thread_assigned = ::tim::singleton<void, void, void>::init();
492} // namespace
493} // namespace internal
494
495//--------------------------------------------------------------------------------------//
496
497} // namespace tim
static thread_id_t main_thread_id()
Definition: singleton.hpp:68
Thread-safe singleton management.
Definition: singleton.hpp:112
static children_t children()
Definition: singleton.hpp:152
static mutex_t & get_mutex()
Definition: singleton.hpp:157
std::thread::id thread_id_t
Definition: singleton.hpp:115
std::recursive_mutex mutex_t
Definition: singleton.hpp:116
std::map< pointer, std::function< void()> > dtor_map_t
Definition: singleton.hpp:122
static smart_pointer & smart_instance()
Definition: singleton.hpp:142
static pointer master_instance()
Definition: singleton.hpp:319
std::unique_lock< mutex_t > auto_lock_t
Definition: singleton.hpp:117
singleton & operator=(const singleton &)=delete
std::function< void(PointerT &)> deleter_t
Definition: singleton.hpp:121
typename std::enable_if< B, T >::type enable_if_t
Definition: singleton.hpp:125
static pointer instance_ptr()
Definition: singleton.hpp:333
static pointer instance()
Definition: singleton.hpp:301
void initialize()
Definition: singleton.hpp:288
static bool is_master_thread()
Definition: singleton.hpp:342
PointerT smart_pointer
Definition: singleton.hpp:120
singleton & operator=(singleton &&)=delete
static pointer master_instance_ptr()
Definition: singleton.hpp:147
static void insert(smart_pointer &itr)
Definition: singleton.hpp:351
static bool is_master(pointer ptr)
Definition: singleton.hpp:153
static smart_pointer & smart_master_instance()
Definition: singleton.hpp:143
singleton(const singleton &)=delete
static thread_id_t master_thread_id()
Definition: singleton.hpp:150
static void remove(pointer itr)
Definition: singleton.hpp:364
std::set< pointer > children_t
Definition: singleton.hpp:119
singleton(singleton &&)=delete
return _hash_map end()
_reported insert(_hash_id)
Definition: kokkosp.cpp:39
void initialize(CompList< CompTypes... > &obj, std::initializer_list< EnumT > components)
Definition: initialize.hpp:53
typename std::enable_if< B, T >::type enable_if_t
Alias template for enable_if.
Definition: types.hpp:190
void init(Args &&... args)
Definition: types.hpp:111
auto get(const auto_bundle< Tag, Types... > &_obj)