25#include <boost/thread.hpp>
26#include <boost/unordered_map.hpp>
27#include <boost/shared_ptr.hpp>
28#include <boost/weak_ptr.hpp>
218template<
typename Thread_local_state_t>
224 private boost::noncopyable
260 static_assert(!std::is_convertible_v<Thread_local_state*, log::Log_context*>,
261 "Thread_local_state_t template param type should not derive from log::Log_context, as "
262 "then set_logger() is not thread-safe; please use Log_context_mt (but be mindful of "
263 "locking-while-logging perf effects in fast-paths).");
503 template<
typename Task>
512 const std::string&
nickname()
const;
715 template<
typename... Ctor_args>
807 boost::shared_ptr<Registry_ctl>
m_ctl;
818 private boost::noncopyable
988template<
typename Shared_state_t>
990 private boost::noncopyable
992 static_assert(!std::is_empty_v<Shared_state_t>,
993 "If there is no shared state to protect -- the poll-flags alone suffice -- then you need "
994 "no Polled_shared_state at all: simply embed Poll_flag(s) in your per-thread state.");
1015 template<
typename... Ctor_args>
1032 template<
typename Task>
1049template<
typename Thread_local_state_t>
1052 decltype(m_create_state_func)&& create_state_func) :
1056 m_create_state_func(std::move(create_state_func)),
1057 m_nickname(nickname_str),
1058 m_this_thread_state_or_null(cleanup),
1062 "Registry created (watched type has ID [" <<
typeid(
Thread_local_state).name() <<
"]).");
1065template<
typename Thread_local_state_t>
1069 const auto ctx = m_this_thread_state_or_null.m_tsp.get();
1070 return ctx ? ctx->m_state :
nullptr;
1073template<
typename Thread_local_state_t>
1079 auto ctx = m_this_thread_state_or_null.m_tsp.get();
1090 while_locked([&](
auto&&...)
1093 decltype(m_create_state_func) create_state_func;
1094 if (m_create_state_func.empty())
1096 if constexpr(S_TL_STATE_HAS_MT_LOG_CONTEXT && std::is_constructible_v<Thread_local_state, Logger*>)
1100 else if constexpr((!S_TL_STATE_HAS_MT_LOG_CONTEXT) && std::is_default_constructible_v<Thread_local_state>)
1106 FLOW_LOG_FATAL(
"Chose not to supply m_create_state_func at time of needing a new Thread_local_state. "
1107 "In this case you must either have <derived from Log_context_mt and supplied ctor "
1108 "form T_l_s{lgr} (where lgr is a Logger*)> or <*not* derived from Log_context_mt but "
1109 "made T_l_s default-ctible>. Breaks contract; aborting.");
1110 assert(
false &&
"Chose not to supply m_create_state_func at time of needing a new Thread_local_state. "
1111 "In this case you must either have <derived from Log_context_mt and supplied ctor "
1112 "form T_l_s{lgr} (where lgr is a Logger*)> or <*not* derived from Log_context_mt but "
1113 "made T_l_s default-ctible>. Breaks contract.");
1127 create_state_func = m_create_state_func;
1132 ctx->m_state = create_state_func();
1134 m_this_thread_state_or_null.m_tsp.reset(ctx);
1139 typename decltype(Registry_ctl::m_state_per_thread)::value_type state_and_mdt{ctx->m_state,
Metadata{}};
1140 auto& mdt = state_and_mdt.second;
1147 mdt.m_thread_id = this_thread::get_id();
1157 FLOW_LOG_INFO(
"Tl_registry[" << *
this <<
"]: Adding thread-local-state @[" << ctx->m_state <<
"] "
1158 "for thread ID [" << mdt.m_thread_id <<
"]/nickname [" << mdt.m_thread_nickname <<
"]/"
1159 "unique-token [" << mdt.m_thread_token <<
"]; "
1164 m_ctl->m_state_per_thread.insert(state_and_mdt);
1165 assert(result.second &&
"How did `state` ptr value get `new`ed, if another thread has not cleaned up same yet?");
1170 return ctx->m_state;
1173template<
typename Thread_local_state_t>
1178 vector<Thread_local_state*> states_to_delete;
1179 while_locked([&](
auto&&...)
1182 "Registry shutting down (watched type has ID [" <<
typeid(
Thread_local_state).name() <<
"]). "
1183 "Will now delete thread-local-state for each thread that has not exited before this point.");
1188 for (
const auto& state_and_mdt : m_ctl->m_state_per_thread)
1190 const auto state = state_and_mdt.first;
1191 const auto& mdt = state_and_mdt.second;
1192 FLOW_LOG_INFO(
"Tl_registry[" << *
this <<
"]: Deleting thread-local-state @[" << state <<
"] "
1193 "for thread ID [" << mdt.m_thread_id <<
"]/nickname [" << mdt.m_thread_nickname <<
"]/"
1194 "unique-token [" << mdt.m_thread_token <<
"].");
1197 states_to_delete.push_back(state);
1199 m_ctl->m_state_per_thread.clear();
1202 for (
const auto state : states_to_delete)
1220 delete m_this_thread_state_or_null.m_tsp.release();
1225template<
typename Thread_local_state_t>
1238 const auto shared_ptr_to_ctl = weak_ptr_to_ctl.lock();
1239 if (!shared_ptr_to_ctl)
1255 Lock lock{shared_ptr_to_ctl->m_mutex};
1256 do_delete = (shared_ptr_to_ctl->m_state_per_thread.erase(ctx->
m_state) == 1);
1273template<
typename Thread_local_state_t>
1274template<
typename Task>
1277 Lock lock{m_ctl->m_mutex};
1278 task(std::as_const(m_ctl->m_state_per_thread));
1281template<
typename Thread_local_state_t>
1287template<
typename Thread_local_state_t>
1292 while_locked([&](
auto&&...)
1295 Log_context::set_logger(logger_ptr);
1297 if constexpr(S_TL_STATE_HAS_MT_LOG_CONTEXT)
1299 for (
const auto& state_and_mdt : m_ctl->m_state_per_thread)
1301 const auto state = state_and_mdt.first;
1303 state->set_logger(logger_ptr);
1309template<
typename Thread_local_state_t>
1310template<
typename... Ctor_args>
1312 m_tsp(std::forward<Ctor_args>(ctor_args)...)
1317template<
typename Thread_local_state_t>
1320 return os <<
'[' << val.
nickname() <<
"]@" << &val;
1333 m_armed.store(
true, std::memory_order_release);
1363 return m_armed.exchange(
false, std::memory_order_acquire);
1373template<
typename Shared_state_t>
1374template<
typename... Ctor_args>
1376 m_shared_state(std::forward<Ctor_args>(shared_state_ctor_args)...)
1381template<
typename Shared_state_t>
1382template<
typename Task>
1386 task(&m_shared_state);
Similar mission to Log_context but enables one to safely log under the possibility of a concurrent se...
Convenience class that simply stores a Logger and/or Component passed into a constructor; and returns...
Interface that the user should implement, passing the implementing Logger into logging classes (Flow'...
static void set_thread_info(Fixed_string *call_thread_nickname, util::Thread_id *call_thread_id)
Same as set_thread_info_in_msg_metadata() but targets the given two variables as opposed to a Msg_met...
A single boolean flag, armed via arm_next_poll() (typically from another thread) and both checked and...
std::atomic< bool > m_armed
The flag; true = armed.
Poll_flag()
Creates flag in disarmed state: poll_armed() shall return false until the first arm_next_poll().
bool poll_armed()
If armed: disarms and returns true; else returns false.
void arm_next_poll()
From any thread: causes the next poll_armed() call to return true (once).
Optional-use companion to Thread_local_state_registry that (together with Poll_flag) enables the Poll...
Shared_state_t Shared_state
Short-hand for template parameter type.
void while_locked(const Task &task)
Locks the non-recursive shared-state mutex, such that no access or modification of the contents of th...
Shared_state m_shared_state
The managed Shared_state.
Mutex_non_recursive m_shared_state_mutex
Protects m_shared_state.
Polled_shared_state(Ctor_args &&... shared_state_ctor_args)
Forwards to the stored object's Shared_state ctor.
boost::thread_specific_ptr replacement (augmentation) whose fast-path .get() touches only thread_loca...
Similar to Thread_local_ptr<T> (a perf-upgraded boost::thread_specific_ptr<T>) but allows iteration t...
Thread_local_state * this_thread_state()
Returns pointer to this thread's thread-local object, first constructing it via m_create_state_func i...
Thread_local_state_registry(log::Logger *logger_ptr, String_view nickname_str, decltype(m_create_state_func)&&create_state_func={})
Create empty registry.
~Thread_local_state_registry()
Deletes each Thread_local_state to have been created so far by calls to this_thread_state() from vari...
std::ostream & operator<<(std::ostream &os, const Thread_local_state_registry< Thread_local_state_t > &val)
Serializes a Thread_local_state_registry to a standard output stream.
Function< Thread_local_state *()> m_create_state_func
this_thread_state(), when needing to create a thread's local new Thread_local_state to return,...
void set_logger(log::Logger *logger_ptr)
Performs thread-safe Log_context::set_logger(logger_ptr); and – if S_TL_STATE_HAS_MT_LOG_CONTEXT is t...
Lock::mutex_type Mutex
Short-hand for mutex type.
boost::unordered_map< Thread_local_state *, Metadata > State_per_thread_map
Type of state_per_thread to task() to while_locked(): extant Thread_local_states and info about their...
const std::string & nickname() const
Returns nickname, a brief string suitable for logging.
void while_locked(const Task &task) const
Locks the non-recursive registry mutex, such that the set of extant per-thread Thread_local_states ca...
Thread_local_state * this_thread_state_or_null()
Returns pointer to this thread's thread-local object, if it has been created via an earlier this_thre...
Tsp_wrapper m_this_thread_state_or_null
In a given thread T, m_this_thread_state_or_null.get() is null if this_thread_state() has not yet bee...
static void cleanup(Tl_context *ctx)
Called by Thread_local_ptr/thread_specific_ptr for a given thread's m_this_thread_state_or_null....
boost::shared_ptr< Registry_ctl > m_ctl
The non-thread-local state. See Registry_ctl docs. shared_ptr is used only for weak_ptr.
static constexpr bool S_TL_STATE_HAS_MT_LOG_CONTEXT
true if and only if Thread_local_state is a public sub-class of log::Log_context_mt which has implica...
Thread_local_state_t Thread_local_state
Short-hand for template parameter type. See our class doc header for requirements.
Lock_guard< Mutex_non_recursive > Lock
Short-hand for mutex lock.
const std::string m_nickname
See nickname().
#define FLOW_LOG_INFO(ARG_stream_fragment)
Logs an INFO message into flow::log::Logger *get_logger() with flow::log::Component get_log_component...
#define FLOW_LOG_FATAL(ARG_stream_fragment)
Logs a FATAL message into flow::log::Logger *get_logger() with flow::log::Component get_log_component...
Function< void()> Task
Short-hand for a task that can be posted for execution by a Concurrent_task_loop or flow::util::Task_...
Flow module containing miscellaneous general-use facilities that don't fit into any other Flow module...
Thread::id Thread_id
Short-hand for an OS-provided ID of a util::Thread.
boost::unique_lock< Mutex > Lock_guard
Short-hand for advanced-capability RAII lock guard for any mutex, ensuring exclusive ownership of tha...
Thread_token this_thread_unique_token()
Returns a value conceptually similar to this_thread::get_id() – a thread ID – but that is never recyc...
boost::mutex Mutex_non_recursive
Short-hand for non-reentrant, exclusive mutex. ("Reentrant" = one can lock an already-locked-in-that-...
Basic_string_view< char > String_view
Commonly used char-based Basic_string_view. See its doc header.
Flow_log_component
The flow::log::Component payload enumeration comprising various log components used by Flow's own int...
The entirety of the cross-thread registry state, in a struct so as to be able to wrap it in a shared_...
Mutex m_mutex
Protects the Registry_ctl (or m_state_per_thread; same difference) and log::Log_context::get_logger()...
State_per_thread_map m_state_per_thread
Registry containing each Thread_local_state, one per distinct thread to have created one via this_thr...
The actual user Thread_local_state stored per thread as lazily-created in this_thread_state(); plus a...
boost::weak_ptr< Registry_ctl > m_ctl_observer
Observer of (existent or non-existent) daddy's m_ctl. See Tl_context doc header for explanation.
Thread_local_state * m_state
The main user state.
Simply wraps a Thread_local_ptr<Tl_context>/boost::thread_specific_ptr<Tl_context>,...
Tsp_wrapper(const Tsp_wrapper &src)=delete
Forbid copy.
Thread_local_ptr< Tl_context > m_tsp
What we wrap and forward-to-and-fro: an object with functional semantics identical to those of boost:...
Tsp_wrapper & operator=(const Tsp_wrapper &src)=delete
Forbid copy.
Tsp_wrapper(Ctor_args &&... ctor_args)
Constructs payload.
An integer-like, type-safe wrapper for the thread-token concept – see this_thread_unique_token(),...