GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: include/dynamic-graph/real-time-logger-def.h Lines: 29 29 100.0 %
Date: 2023-03-15 12:04:10 Branches: 5 6 83.3 %

Line Branch Exec Source
1
// -*- mode: c++ -*-
2
// Copyright 2018, Joseph Mirabel LAAS-CNRS
3
//
4
5
#ifndef DYNAMIC_GRAPH_LOGGER_REAL_TIME_DEF_H
6
#define DYNAMIC_GRAPH_LOGGER_REAL_TIME_DEF_H
7
#include <boost/shared_ptr.hpp>
8
#include <boost/thread/mutex.hpp>
9
#include <dynamic-graph/config.hh>
10
#include <sstream>
11
#include <vector>
12
13
namespace dynamicgraph {
14
/// \ingroup debug
15
///
16
/// \brief Stream for the real-time logger.
17
///
18
/// You should inherit from this class in order to redirect the logs where you
19
/// want.
20
/// \sa LoggerIOStream
21
class LoggerStream {
22
 public:
23
  virtual void write(const char *c) = 0;
24
};
25
26
/// Write to an ostream object.
27
///
28
/// The easieast is to use the macro  dgADD_OSTREAM_TO_RTLOG(ostr) where
29
/// `ostr` can be `std::cout` or an std::ofstream...
30
class LoggerIOStream : public LoggerStream {
31
 public:
32
4
  LoggerIOStream(std::ostream &os) : os_(os) {}
33
16
  virtual ~LoggerIOStream() {}
34
122032
  virtual void write(const char *c) { os_ << c; }
35
36
 private:
37
  std::ostream &os_;
38
};
39
typedef boost::shared_ptr<LoggerStream> LoggerStreamPtr_t;
40
41
class RealTimeLogger;
42
43
/// \cond DEVEL
44
/// \brief write entries to intenal buffer.
45
///
46
/// The entry starts when an instance is created and ends when is is deleted.
47
/// This class is only used by RealTimeLogger.
48
class RTLoggerStream {
49
 public:
50
161044
  inline RTLoggerStream(RealTimeLogger *logger, std::ostream &os)
51
161044
      : ok_(logger != NULL), logger_(logger), os_(os) {}
52
  template <typename T>
53
658112
  inline RTLoggerStream &operator<<(T t) {
54
658112
    if (ok_) os_ << t;
55
658112
    return *this;
56
  }
57
12
  inline RTLoggerStream &operator<<(std::ostream &(*pf)(std::ostream &)) {
58
12
    if (ok_) os_ << pf;
59
12
    return *this;
60
  }
61
62
  inline ~RTLoggerStream();
63
64
20
  inline bool isNull() { return !ok_; }
65
66
 private:
67
  const bool ok_;
68
  RealTimeLogger *logger_;
69
  std::ostream &os_;
70
};
71
/// \endcond DEVEL
72
73
/// \ingroup debug
74
///
75
/// \brief Main class of the real-time logger.
76
///
77
/// It is intended to be used like this:
78
/// \code
79
/// #define ENABLE_RT_LOG
80
/// #include <dynamic-graph/real-time-logger.h>
81
///
82
/// // Somewhere in the main function of your executable
83
/// int main (int argc, char** argv) {
84
///   dgADD_OSTREAM_TO_RTLOG (std::cout);
85
/// }
86
///
87
/// // Somewhere in your library
88
/// dgRTLOG() << "your message. Prefer to use \n than std::endl."
89
/// \endcode
90
///
91
/// \note Thread safety. This class expects to have:
92
/// - only one reader: the one who take the log entries and write them
93
///   somewhere.
94
/// - one writer at a time. Writing to the logs is **never** a blocking
95
///   operation. If the resource is busy, the log entry is discarded.
96
class DYNAMIC_GRAPH_DLLAPI RealTimeLogger {
97
 public:
98
  static RealTimeLogger &instance();
99
100
  static void destroy();
101
102
  /// \todo add an argument to preallocate the internal string
103
  /// to a given size.
104
  RealTimeLogger(const std::size_t &bufferSize);
105
106
  inline void clearOutputStreams() { outputs_.clear(); }
107
108
4
  inline void addOutputStream(const LoggerStreamPtr_t &os) {
109
4
    outputs_.push_back(os);
110
4
  }
111
112
  /// Write next message to output.
113
  /// It does nothing if the buffer is empty.
114
  /// \return true if it wrote something
115
  bool spinOnce();
116
117
  /// Return an object onto which a real-time thread can write.
118
  /// The message is considered finished when the object is destroyed.
119
  RTLoggerStream front();
120
121
  /// Return an empty stream object.
122
25210
  RTLoggerStream emptyStream() { return RTLoggerStream(NULL, oss_); }
123
124
122032
  inline void frontReady() {
125
122032
    backIdx_ = (backIdx_ + 1) % buffer_.size();
126
122032
    wmutex.unlock();
127
122032
  }
128
129
123948
  inline bool empty() const { return frontIdx_ == backIdx_; }
130
131
131048
  inline bool full() const {
132
131048
    return ((backIdx_ + 1) % buffer_.size()) == frontIdx_;
133
  }
134
135
  inline std::size_t size() const {
136
    if (frontIdx_ <= backIdx_)
137
      return backIdx_ - frontIdx_;
138
    else
139
      return backIdx_ + buffer_.size() - frontIdx_;
140
  }
141
142
1000
  inline std::size_t getBufferSize() { return buffer_.size(); }
143
144
  ~RealTimeLogger();
145
146
 private:
147
  struct Data {
148
    std::stringbuf buf;
149
  };
150
151
  std::vector<LoggerStreamPtr_t> outputs_;
152
  std::vector<Data *> buffer_;
153
  /// Index of the next value to be read.
154
  std::size_t frontIdx_;
155
  /// Index of the slot where to write next value
156
  /// (does not contain valid data).
157
  std::size_t backIdx_;
158
  std::ostream oss_;
159
160
  /// The writer mutex.
161
  boost::mutex wmutex;
162
  std::size_t nbDiscarded_;
163
164
  struct thread;
165
166
  static RealTimeLogger *instance_;
167
  static thread *thread_;
168
};
169
170
322088
RTLoggerStream::~RTLoggerStream() {
171
161044
  if (ok_) {
172
122032
    os_ << std::ends;
173
122032
    logger_->frontReady();
174
  }
175
161044
}
176
177
}  // end of namespace dynamicgraph
178
179
#endif  //! DYNAMIC_GRAPH_LOGGER_REAL_TIME_DEF_H