Commit ddab6445 authored by Joseph Mirabel's avatar Joseph Mirabel Committed by Olivier Stasse
Browse files

In RealTimeLogger, add thread safety for writting + add doc.

parent 73a016f0
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
# include <boost/circular_buffer.hpp> # include <boost/circular_buffer.hpp>
# include <boost/shared_ptr.hpp> # include <boost/shared_ptr.hpp>
# include <boost/thread/mutex.hpp>
# include <dynamic-graph/config.hh> # include <dynamic-graph/config.hh>
# include <dynamic-graph/debug.h> # include <dynamic-graph/debug.h>
...@@ -31,12 +32,20 @@ namespace dynamicgraph ...@@ -31,12 +32,20 @@ namespace dynamicgraph
/// \ingroup debug /// \ingroup debug
/// ///
/// \brief Stream for the real-time logger. /// \brief Stream for the real-time logger.
///
/// You should inherit from this class in order to redirect the logs where you
/// want.
/// \sa LoggerIOStream
class LoggerStream class LoggerStream
{ {
public: public:
virtual void write (const char* c) = 0; virtual void write (const char* c) = 0;
}; };
/// Write to an ostream object.
///
/// The easieast is to use the macro \ref dgADD_OSTREAM_TO_RTLOG(ostr) where
/// `ostr` can be `std::cout` or an std::ofstream...
class LoggerIOStream : public LoggerStream class LoggerIOStream : public LoggerStream
{ {
public: public:
...@@ -48,12 +57,18 @@ namespace dynamicgraph ...@@ -48,12 +57,18 @@ namespace dynamicgraph
typedef boost::shared_ptr <LoggerStream> LoggerStreamPtr_t; typedef boost::shared_ptr <LoggerStream> LoggerStreamPtr_t;
class RealTimeLogger; class RealTimeLogger;
/// \cond DEVEL
/// \brief write entries to intenal buffer.
///
/// The entry starts when an instance is created and ends when is is deleted.
/// This class is only used by RealTimeLogger.
class RTLoggerStream class RTLoggerStream
{ {
public: public:
RTLoggerStream (RealTimeLogger* logger, std::ostream& os) : logger_(logger), os_ (os) {} RTLoggerStream (RealTimeLogger* logger, std::ostream& os) : logger_(logger), os_ (os) {}
template <typename T> inline RTLoggerStream& operator<< (T t) { os_ << t; return *this; } template <typename T> inline RTLoggerStream& operator<< (T t) { if (logger_!=NULL) os_ << t; return *this; }
inline RTLoggerStream& operator<< (std::ostream& (*pf)(std::ostream&)) { os_ << pf; return *this; } inline RTLoggerStream& operator<< (std::ostream& (*pf)(std::ostream&)) { if (logger_!=NULL) os_ << pf; return *this; }
~RTLoggerStream(); ~RTLoggerStream();
private: private:
...@@ -61,10 +76,30 @@ namespace dynamicgraph ...@@ -61,10 +76,30 @@ namespace dynamicgraph
RealTimeLogger* logger_; RealTimeLogger* logger_;
std::ostream& os_; std::ostream& os_;
}; };
/// \endcond DEVEL
/// \ingroup debug /// \ingroup debug
/// ///
/// \brief Main class of the real-time logger. /// \brief Main class of the real-time logger.
///
/// It is intended to be used like this:
/// \code
/// #define ENABLE_RT_LOG
/// #include <dynamic-graph/real-time-logger.h>
///
/// // Somewhere in the main function of your executable
/// int main (int argc, char** argv) {
/// dgADD_OSTREAM_TO_RTLOG (std::cout);
/// }
///
/// // Somewhere in your library
/// dgRTLOG() << "your message. Prefer to use \n than std::endl."
/// \endcode
///
/// \note Thread safety. This class expects to have:
/// - only one reader: the one who take the log entries and write them somewhere.
/// - one writer at a time. Writing to the logs is **never** a blocking
/// operation. If the resource is busy, the log entry is discarded.
class DYNAMIC_GRAPH_DLLAPI RealTimeLogger class DYNAMIC_GRAPH_DLLAPI RealTimeLogger
{ {
public: public:
...@@ -88,7 +123,7 @@ namespace dynamicgraph ...@@ -88,7 +123,7 @@ namespace dynamicgraph
/// The message is considered finished when the object is destroyed. /// The message is considered finished when the object is destroyed.
RTLoggerStream front(); RTLoggerStream front();
inline void frontReady() { backIdx_ = (backIdx_+1) % buffer_.size(); } inline void frontReady() { backIdx_ = (backIdx_+1) % buffer_.size(); wmutex.unlock(); }
inline bool empty () const inline bool empty () const
{ {
...@@ -126,6 +161,10 @@ namespace dynamicgraph ...@@ -126,6 +161,10 @@ namespace dynamicgraph
std::size_t backIdx_; std::size_t backIdx_;
std::ostream oss_; std::ostream oss_;
/// The writer mutex.
boost::mutex wmutex;
std::size_t nbDiscarded_;
struct thread; struct thread;
static RealTimeLogger* instance_; static RealTimeLogger* instance_;
......
...@@ -27,6 +27,7 @@ namespace dynamicgraph ...@@ -27,6 +27,7 @@ namespace dynamicgraph
RealTimeLogger::RealTimeLogger (const std::size_t& bufferSize) RealTimeLogger::RealTimeLogger (const std::size_t& bufferSize)
: buffer_(bufferSize, NULL) : buffer_(bufferSize, NULL)
, oss_ (NULL) , oss_ (NULL)
, nbDiscarded_ (0)
{ {
for (std::size_t i = 0; i < buffer_.size(); ++i) for (std::size_t i = 0; i < buffer_.size(); ++i)
buffer_[i] = new Data; buffer_[i] = new Data;
...@@ -54,8 +55,15 @@ namespace dynamicgraph ...@@ -54,8 +55,15 @@ namespace dynamicgraph
RTLoggerStream RealTimeLogger::front () RTLoggerStream RealTimeLogger::front ()
{ {
// If no output or if buffer is full, discard message.
if (outputs_.empty() || full()) { if (outputs_.empty() || full()) {
oss_.rdbuf(NULL); nbDiscarded_++;
return RTLoggerStream (NULL, oss_);
}
bool alone = wmutex.try_lock();
// If someone is writting, discard message.
if (!alone) {
nbDiscarded_++;
return RTLoggerStream (NULL, oss_); return RTLoggerStream (NULL, oss_);
} }
Data* data = buffer_[backIdx_]; Data* data = buffer_[backIdx_];
......
...@@ -46,7 +46,7 @@ BOOST_AUTO_TEST_CASE (monothread) ...@@ -46,7 +46,7 @@ BOOST_AUTO_TEST_CASE (monothread)
int spinNb = 0; int spinNb = 0;
while (rtl.spinOnce()) { spinNb++; } while (rtl.spinOnce()) { spinNb++; }
BOOST_CHECK(spinNb == 9); BOOST_CHECK_EQUAL(spinNb, 9);
rtl.front() << "This msg should be short." << '\n'; rtl.front() << "This msg should be short." << '\n';
rtl.spinOnce(); rtl.spinOnce();
...@@ -58,10 +58,10 @@ BOOST_AUTO_TEST_CASE (multithread) ...@@ -58,10 +58,10 @@ BOOST_AUTO_TEST_CASE (multithread)
dgADD_OSTREAM_TO_RTLOG (std::cout); dgADD_OSTREAM_TO_RTLOG (std::cout);
for (std::size_t i = 0; i < rtl.getBufferSize()-1; ++i) for (std::size_t i = 0; i < rtl.getBufferSize()-1; ++i)
rtl.front() << "Call number " << i << '\n'; dgRTLOG() << "Call number " << i << '\n';
for (std::size_t i = 0; i < 12; ++i) { for (std::size_t i = 0; i < 12; ++i) {
boost::this_thread::sleep(boost::posix_time::milliseconds(20)); boost::this_thread::sleep(boost::posix_time::milliseconds(20));
rtl.front() << "Call number " << i << std::endl; dgRTLOG() << "Call number " << i << std::endl;
BOOST_CHECK (!rtl.full()); BOOST_CHECK (!rtl.full());
} }
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment