c++ - Thread safe operator<< -


i have quite complicated problem logger class. singleton pattern logging class. thread created taking out items queue , logging them. works fine , error occurs segmentation fault. happening more before decided put mutex on whole method chain. mutex dont understand why segmentation fault occuring. class got quite complicated because of operator<< usage. problem operator template run many times, many items passed using <<. because of other threads can cut in between templete calls.

the general usage looks this: 1. instance method called (creating or pointing instance pointer (singleton). mutex locked @ moment. 2. called methods called, example operator<< template. 3. finishing method called, placing log in queue , unlocking mutex.

i have edited code , tried take fata gathering out of singleton class proxy class.

main.c:

#include <iostream> #include <stdio.h> #include <stdlib.h> #include <unistd.h>  #include "clogger.h" #include "ctestclass.h"  using namespace std;  int main() {     clogger::instance().log.startlog("/home/lukasz/pulpit/test.txt", true);     clogger::instance().log.setloglevel(clogger::eloglevel::edebug);      clogger::instance().printf("print test");     clogger::instance().printf("print test");     clogger::instance().printf("print test");     clogger::instance() << "stream test" ;     clogger::instance() << "stream test" ;     clogger::instance() << "stream test" ;       //ctestclass test1(1);     //ctestclass test2(2);     //ctestclass test3(3);      sleep(3);     clogger::instance().log.stoplog();      return 0; } 

clogger.h:

#ifndef clogger_h_ #define clogger_h_  #include <iostream> #include <deque> #include <string> #include <mutex> #include <condition_variable> #include <pthread.h> #include <ostream> #include <fstream> #include <sstream> #include <ctime> #include <iomanip> #include <sys/time.h> #include <stdarg.h> #include <assert.h>  #include "cteebuf.h"  using namespace std;  class cloggerproxy;  /*!  * \brief singleton class used logging  */ class clogger { public:     /*!      * \brief describes log level of called \ref clogger object.      */     enum class eloglevel { enone = 0, eerror, ewarning, einfo, edebug };      /*!      * structure describing single log item:      */     struct logline_t     {         string logstring; /*!< string line saved file (and printed cout). */         eloglevel loglevel; /*!< \ref eloglevel of line. */         timeval currenttime; /*!< time stamp of current log line */     };      static clogger* internalinstance(eloglevel llevel = eloglevel::edebug);     static cloggerproxy instance(eloglevel llevel = eloglevel::edebug);      bool startlog(string filename, bool verbose);     void setloglevel(eloglevel ll);     void stoplog();     void finaliseline(logline_t* log);  protected:     virtual void threadloop();  private:     clogger() {};                               // private can  not called     clogger(clogger const&) {};                 // copy constructor private     clogger& operator= (clogger const&) {};     // assignment operator private      /*!< global static pointer used ensure single instance of class */     static clogger* mp_instance;     bool m_logstarted;     eloglevel m_userdefinedloglevel;     ofstream m_logfilestream;     bool m_verbose;     bool m_finishlog;     timeval m_initialtime;      static void * threadhelper(void* handler)     {         ((clogger*)handler)->threadloop();         return null;     }      deque<logline_t*> m_data;     mutex m_mutex2;     condition_variable m_cv;     pthread_t   m_thread;      logline_t pop_front();     void push_back(logline_t* s); };  /*!  * raii class used destructor, add log item queue  */ class cloggerproxy { public:     clogger &log;     cloggerproxy(clogger &logger) : log(logger)     {         mp_logline = new clogger::logline_t;         gettimeofday(&mp_logline->currenttime, null);     }      ~cloggerproxy() { log.finaliseline(mp_logline); }      void printf(const char* text, ...);      /*!      * takes data stream , adds current string.      * @param t stream item      * @return \ref object address      */     template <typename t>     cloggerproxy& operator<< (const t &t)     {         ostringstream stream;         stream << t;         mp_logline->logstring = (stream.str() + " ");         return *this;     }  private:     clogger::logline_t* mp_logline;  };  #endif /* clogger_h_ */ 

clogger.cpp:

#include "clogger.h"  using namespace std;  clogger* clogger::mp_instance = null;  /*!  *  function called create instance of class.  *  calling constructor publicly not allowed. constructor  *  private , called instance function.  *  @param llevel  log level current object  */ clogger* clogger::internalinstance(eloglevel llevel) {     // allow 1 instance of class generated.     if (!mp_instance)     {         mp_instance = new clogger;         assert(mp_instance);     }      return mp_instance; }  /*!  * method called in order use methods  * within objects.  * @param llevel  log level current object  */ cloggerproxy clogger::instance(eloglevel llevel) {     return cloggerproxy(*internalinstance(llevel)); }  /*!  * \brief starts logging system.  *  * method creates , opens log file,  * opens , creates threadloop messages deque.  * @param filename desired log file path,  * @param verbose when set true, logging printed standard output.  */ bool clogger::startlog(string filename, bool verbose) {     if(remove(filename.c_str()) != 0)         perror( "error deleting file" );      m_logfilestream.open(filename.c_str(), ios::out | ios::app);     if (!m_logfilestream.is_open())     {         cout << "could not open log file " << filename << endl;         return false;     }      m_finishlog = false;     m_verbose = verbose;     m_logstarted = true;     gettimeofday(&m_initialtime, null);      return (pthread_create(&(m_thread), null, threadhelper, this) == 0); }  /*!  * \brief puts \ref logline_t object @ end of queue  * @param s object added queue  */ void clogger::push_back(logline_t* s) {     unique_lock<mutex> ul(m_mutex2);     m_data.emplace_back(move(s));     m_cv.notify_all(); }  /*!  * \brief takes \ref logline_t object beggining of queue  * @return first \ref logline_t object  */ clogger::logline_t clogger::pop_front() {     unique_lock<mutex> ul(m_mutex2);     m_cv.wait(ul, [this]() { return !m_data.empty(); });      logline_t retval = move(*m_data.front());      assert(m_data.front());     delete m_data.front();     m_data.front() = null;      m_data.pop_front();      return retval; }  /*!  * \brief sets log level whole \ref clogger object.  * if \ref m_logline equal or higher set level, log  * going printed.  * @param llevel desired user define log level.  */ void clogger::setloglevel(eloglevel llevel) {     m_userdefinedloglevel = llevel; }  /*!  * \brief stops logging system.  * last final logline being added , logging thread  * being closed.  */ void clogger::stoplog() {     m_finishlog = true;     //instance(eloglevel::enone).log << "clogger stop";      //pthread_join(m_thread, null); }  /*!  * function should run in \ref cloggerproxy destructor.  * pushes gathered stream queue.  */ void clogger::finaliseline(logline_t* log) {     if (log->logstring.size() > 0)         push_back(log);     else         delete log; }  /*!  * \brief adds text log string in printf c way.  * works faster operator<< , more atomic.  * @param text pointer character string.  * @param ... argptr parameters  */ void cloggerproxy::printf(const char* text, ...) {     va_list argptr;     va_start(argptr, text);      char* output = null;     vasprintf(&output, text, argptr);      mp_logline->logstring = output;     va_end(argptr); }  /*!  * loop running in separate thread. take items of  * log deque object (if there any) , saves them file.  */ void clogger::threadloop() {     logline_t logline;     const string loglevelsstrings[] = {"enone", "eerror", "ewarning", "einfo", "edebug" };      coteestream tee;     tee.add(m_logfilestream);      if (m_verbose)         tee.add(cout);      struct sched_param param;     param.__sched_priority = 0;      if(!sched_setscheduler(0, sched_idle, &param))         instance().printf("clogger scheduler policy set %d", sched_getscheduler(0));      int secs = 0;     int h = 0;     int m = 0;     int s = 0;          {         logline = pop_front(); // waits here new lines          secs = logline.currenttime.tv_sec - m_initialtime.tv_sec;         h = secs / 3600;         m = ( secs % 3600 ) / 60;         s = ( secs % 3600 ) % 60;          tee     << "["                 << setw(2) << setfill('0') << h                 << ":"                 << setw(2) << setfill('0') << m                 << ":"                 << setw(2) << setfill('0') << s                 << "."                 << setw(6) << setfill('0') << logline.currenttime.tv_usec                 << "]"                 << "["                 << setw(2) << setfill('0') << m_data.size()                 << "]"                 << "["                 << loglevelsstrings[(int)logline.loglevel]                 << "] "                 << logline.logstring << "\n" << flush;     }     //while(!(m_finishlog && m_data.empty()));     while(1);      m_logfilestream.close(); } 

there several problems code.

singleton

// allow 1 instance of class generated. if (!mp_instance) {     mp_instance = new clogger;     assert(mp_instance); } 

this classic problem. may called different threads @ same time, , not thread safe. may end several instances of singleton.

the queue (m_data)

clients of logger put messages queue (apparently secured m_mutext).

m_data.emplace_back(move(s)); m_cv.notify_all(); 

your logger thread removes messages in own thread (secured m_mutex2).

unique_lock<mutex> ul(m_mutex2); m_cv.wait(ul, [this]() { return !m_data.empty(); });  logline_t retval = move(*m_data.front());  assert(m_data.front()); delete m_data.front(); m_data.front() = null;  m_data.pop_front();  return retval; 

the problem here is, use 2 different mutexes synchronize access same object. cannot work.

in addition access m_data in thread without locking @ all:

            << setw(2) << setfill('0') << m_data.size() 

or

while(!(m_finishlog && m_data.empty())); 

the log message (mp_logline)

you try lock data. pointer log message meant used single thread @ time. store in main logger class accessed threads. have proxy logger private thread using it. store message there until it's finished, , add queue.

generally said, minimize amount of data locked. if rework code , object needing locking queue, on right way.


Comments

Popular posts from this blog

javascript - Karma not able to start PhantomJS on Windows - Error: spawn UNKNOWN -

c# - Display ASPX Popup control in RowDeleteing Event (ASPX Gridview) -

Nuget pack csproj using nuspec -