Simple-Log  alpha-v0.7
Core.hpp
Go to the documentation of this file.
1 // Copyright Dominic Koepke 2021 - 2021.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE_1_0.txt or copy at
4 // https://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef SL_LOG_CORE_HPP
7 #define SL_LOG_CORE_HPP
8 
9 #pragma once
10 
11 #include "ISink.hpp"
12 #include "Record.hpp"
13 #include "RecordQueue.hpp"
14 
15 #include <algorithm>
16 #include <execution>
17 #include <future>
18 #include <memory>
19 #include <mutex>
20 #include <vector>
21 
22 namespace sl::log
23 {
49  template <Record TRecord>
50  class Core
51  {
52  public:
53  using Record_t = std::remove_cvref_t<TRecord>;
54 
55  private:
56  using ISink_t = ISink<Record_t>;
58  using SinkContainer_t = std::vector<std::unique_ptr<ISink_t>>;
59 
60  public:
65  Core() noexcept :
66  m_Worker{ m_WorkerInstruction, m_Records, m_SinkMx, m_Sinks }
67  {
68  m_WorkerFuture = std::async(std::launch::async, m_Worker);
69  }
70 
75  ~Core() noexcept
76  {
77  try
78  {
79  m_WorkerInstruction = Worker::Instruction::quit;
80  m_WorkerFuture.wait();
81  }
82  catch (...)
83  {
84  m_WorkerInstruction = Worker::Instruction::forceQuit;
85  }
86  }
87 
91  Core(const Core&) = delete;
95  Core& operator =(const Core&) = delete;
96 
100  Core(Core&&) = delete;
104  Core& operator =(Core&&) = delete;
105 
111  void log(Record_t&& record)
112  {
113  // will reject newly generated records, after run has become false
114  if (m_WorkerInstruction == Worker::Instruction::run)
115  {
116  m_Records.push(std::move(record));
117  }
118  }
119 
128  template <std::derived_from<ISink_t> TSink, class... TArgs>
129  requires std::constructible_from<TSink, TArgs...>
130  TSink& makeSink(TArgs&&... args)
131  {
132  auto& ref = makeSinkImpl<TSink>(std::forward<TArgs>(args)...);
133  ref.setEnabled();
134  return ref;
135  }
136 
147  template <std::derived_from<ISink_t> TSink, class... TArgs>
148  requires std::constructible_from<TSink, TArgs...>
150  {
151  auto& ref = makeSinkImpl<TSink>(std::forward<TArgs>(args)...);
153  }
154 
161  bool removeSink(const ISink_t& sink)
162  {
163  std::scoped_lock lock{ m_SinkMx };
164 
165  if (auto itr = std::ranges::find(m_Sinks, &sink, &std::unique_ptr<ISink_t>::get); itr != std::end(m_Sinks))
166  {
167  using std::swap;
168  swap(*itr, m_Sinks.back());
169  m_Sinks.resize(std::size(m_Sinks) - 1);
170  return true;
171  }
172  return false;
173  }
174 
175  private:
176  class Worker
177  {
178  public:
179  enum class Instruction
180  {
181  run,
182  quit,
183  forceQuit
184  };
185 
186  Worker(
187  const std::atomic<Instruction>& instruction,
188  RecordQueue_t& records,
189  std::mutex& sinkMx,
190  const SinkContainer_t& sinks
191  ) :
192  m_Instruction{ instruction },
193  m_Records{ records },
194  m_SinkMx{ sinkMx },
195  m_Sinks{ sinks }
196  {
197  }
198 
199  void operator ()() const
200  {
201  for (auto instruction = m_Instruction.load();
202  instruction != Instruction::forceQuit &&
203  (instruction != Instruction::quit || !std::empty(m_Records));
204  instruction = m_Instruction)
205  {
206  if (auto optRecord = m_Records.take(std::chrono::milliseconds{ 200 }))
207  {
208  std::scoped_lock lock{ m_SinkMx };
209  std::for_each(
210  std::execution::par,
211  std::begin(m_Sinks),
212  std::end(m_Sinks),
213  [&record = *optRecord](auto& sink)
214  {
215  sink->log(record);
216  }
217  );
218  }
219  }
220  }
221 
222  private:
223  const std::atomic<Instruction>& m_Instruction;
224 
225  RecordQueue_t& m_Records;
226 
227  std::mutex& m_SinkMx;
228  const SinkContainer_t& m_Sinks;
229  };
230 
231  RecordQueue_t m_Records;
232 
233  std::mutex m_SinkMx;
234  SinkContainer_t m_Sinks;
235 
236  std::atomic<typename Worker::Instruction> m_WorkerInstruction{ Worker::Instruction::run };
237  Worker m_Worker;
238  std::future<void> m_WorkerFuture;
239 
240  template <std::derived_from<ISink_t> TSink, class... TArgs>
241  requires std::constructible_from<TSink, TArgs...>
242  TSink& makeSinkImpl(TArgs&&... args)
243  {
244  auto sink = std::make_unique<TSink>(std::forward<TArgs>(args)...);
245  auto& ref = *sink;
246  std::scoped_lock lock{ m_SinkMx };
247  m_Sinks.emplace_back(std::move(sink));
248  return ref;
249  }
250  };
251 
253 }
254 
255 #endif
The central point of the whole library. Needs to be instantiated at least once.
Definition: Core.hpp:51
std::remove_cvref_t< TRecord > Record_t
Definition: Core.hpp:53
requires std::constructible_from< TSink, TArgs... > ScopedSinkDisabling< Record_t, TSink > makeDisabledSink(TArgs &&... args)
Creates Sink disabled and registers it at this Core instance.
Definition: Core.hpp:149
Core(const Core &)=delete
Deleted copy constructor.
~Core() noexcept
Destructor.
Definition: Core.hpp:75
Core(Core &&)=delete
Deleted move constructor.
Core() noexcept
Default Constructor.
Definition: Core.hpp:65
void log(Record_t &&record)
Queues the Record internally.
Definition: Core.hpp:111
bool removeSink(const ISink_t &sink)
Removes the given Sink and destroys it.
Definition: Core.hpp:161
requires std::constructible_from< TSink, TArgs... > TSink & makeSink(TArgs &&... args)
Creates Sink and registers it at this Core instance.
Definition: Core.hpp:130
Core & operator=(const Core &)=delete
Deleted copy assign operator.
Sink interface class.
Definition: ISink.hpp:29
void push(Record_t record)
Pushes Record s to the internal queue.
Definition: RecordQueue.hpp:43
Wrapper class which disables Sinks on construction and enables them on destruction.
Definition: ISink.hpp:95
Definition: BasicSink.hpp:22