libkazv
thread-safety-helper.hpp
Go to the documentation of this file.
1 /*
2  * This file is part of libkazv.
3  * SPDX-FileCopyrightText: 2020-2021 Tusooa Zhu <tusooa@kazv.moe>
4  * SPDX-License-Identifier: AGPL-3.0-or-later
5  */
6 
7 #pragma once
8 
9 #include <libkazv-config.hpp>
10 
11 #if LIBKAZV_BUILT_WITH_DEBUG
12 #ifndef KAZV_USE_THREAD_SAFETY_HELPER
13 #define KAZV_USE_THREAD_SAFETY_HELPER
14 #endif
15 #endif
16 
17 #ifdef KAZV_USE_THREAD_SAFETY_HELPER
18 #include <mutex>
19 struct ThreadNotMatchException // Don't derive from std::exception to avoid catch-all catch clauses
20 {
21  std::string m_what;
22 
23  std::string what() const;
24 };
25 
26 struct EventLoopThreadIdKeeper
27 {
28  mutable std::mutex m_mutex;
29  std::optional<std::thread::id> m_id;
30 
32  void set(std::thread::id id) {
33  std::lock_guard<std::mutex> g(m_mutex);
34  m_id = id;
35  }
36 
37  std::optional<std::thread::id> get() const {
38  std::lock_guard<std::mutex> g(m_mutex);
39  return m_id;
40  }
41 };
42 #define KAZV_THREAD_ID_VAR _threadSafetyHelper_threadId
43 #define KAZV_ON_EVENT_LOOP_VAR _threadSafetyHelper_onEventLoop
44 #define KAZV_EVENT_LOOP_THREAD_ID_KEEPER_VAR _threadSafetyHelper_eventLoopThreadIdKeeper
45 #define KAZV_DECLARE_THREAD_ID() bool KAZV_ON_EVENT_LOOP_VAR{false}; \
46  std::thread::id KAZV_THREAD_ID_VAR = std::this_thread::get_id();
47 #define KAZV_DECLARE_EVENT_LOOP_THREAD_ID_KEEPER(_initializer) \
48  EventLoopThreadIdKeeper *KAZV_EVENT_LOOP_THREAD_ID_KEEPER_VAR = _initializer
49 //#define KAZV_INIT_THREAD_ID_FROM_KEEPER() KAZV_THREAD_ID_VAR(KAZV_EVENT_LOOP_THREAD_ID_KEEPER_VAR.get())
50 #define KAZV_VERIFY_THREAD_ID() \
51  do { \
52  auto _threadSafetyHelper_local_idActual = std::this_thread::get_id(); \
53  \
54  if (KAZV_ON_EVENT_LOOP_VAR) { \
55  auto _threadSafetyHelper_local_idExpected = \
56  KAZV_EVENT_LOOP_THREAD_ID_KEEPER_VAR ? KAZV_EVENT_LOOP_THREAD_ID_KEEPER_VAR->get() : std::nullopt; \
57  auto cond = \
58  _threadSafetyHelper_local_idExpected.has_value() \
59  ? _threadSafetyHelper_local_idExpected.value() == _threadSafetyHelper_local_idActual \
60  /* if the id is not set yet it means the event loop is not yet run, so it does not matter anyway */ \
61  : true; \
62  if (!cond) { \
63  throw ThreadNotMatchException{"Current object belongs to the event loop, but method is called outside the event loop"}; \
64  } \
65  } else { \
66  if (! (KAZV_THREAD_ID_VAR == _threadSafetyHelper_local_idActual)) { \
67  throw ThreadNotMatchException{"Current thread id does not match the id of the thread where it belongs"}; \
68  } \
69  } \
70  } while (false)
71 
72 #else
73 #define KAZV_DECLARE_THREAD_ID()
74 #define KAZV_VERIFY_THREAD_ID()
75 #define KAZV_DECLARE_EVENT_LOOP_THREAD_ID_KEEPER(_initializer)
76 #endif