text: /*
   *          Copyright Andrey Semashev 2007 - 2013.
   * Distributed under the Boost Software License, Version 1.0.
   *    (See accompanying file LICENSE_1_0.txt or copy at
   *          http://www.boost.org/LICENSE_1_0.txt)
   */
  /*!
   * \file   basic_sink_frontend.hpp
   * \author Andrey Semashev
   * \date   14.07.2009
   *
   * The header contains implementation of a base class for sink frontends.
   */
  
  #ifndef BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_
  #define BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_
  
  #include <boost/mpl/bool.hpp>
  #include <boost/log/detail/config.hpp>
  #include <boost/log/detail/cleanup_scope_guard.hpp>
  #include <boost/log/detail/code_conversion.hpp>
  #include <boost/log/detail/attachable_sstream_buf.hpp>
  #include <boost/log/detail/fake_mutex.hpp>
  #include <boost/log/core/record_view.hpp>
  #include <boost/log/sinks/sink.hpp>
  #include <boost/log/sinks/frontend_requirements.hpp>
  #include <boost/log/expressions/filter.hpp>
  #include <boost/log/expressions/formatter.hpp>
  #if !defined(BOOST_LOG_NO_THREADS)
  #include <boost/thread/exceptions.hpp>
  #include <boost/thread/tss.hpp>
  #include <boost/thread/locks.hpp>
  #include <boost/log/detail/locks.hpp>
  #include <boost/log/detail/light_rw_mutex.hpp>
  #include <boost/concept_check.hpp>
  #endif // !defined(BOOST_LOG_NO_THREADS)
  #include <boost/log/detail/header.hpp>
  
  #ifdef BOOST_LOG_HAS_PRAGMA_ONCE
  #pragma once
  #endif
  
  namespace boost {
  
  BOOST_LOG_OPEN_NAMESPACE
  
  namespace sinks {
  
  //! A base class for a logging sink frontend
  class BOOST_LOG_NO_VTABLE basic_sink_frontend :
      public sink
  {
      //! Base type
      typedef sink base_type;
  
  public:
      //! An exception handler type
      typedef base_type::exception_handler_type exception_handler_type;
  
  #if !defined(BOOST_LOG_NO_THREADS)
  protected:
      //! Mutex type
      typedef boost::log::aux::light_rw_mutex mutex_type;
  
  private:
      //! Synchronization mutex
      mutable mutex_type m_Mutex;
  #endif
  
  private:
      //! Filter
      filter m_Filter;
      //! Exception handler
      exception_handler_type m_ExceptionHandler;
  
  public:
      /*!
       * \brief Initializing constructor
       *
       * \param cross_thread The flag indicates whether the sink passes log records between different threads
       */
      explicit basic_sink_frontend(bool cross_thread) : sink(cross_thread)
      {
      }
  
      /*!
       * The method sets sink-specific filter functional object
       */
      template< typename FunT >
      void set_filter(FunT const& filter)
      {
          BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);)
          m_Filter = filter;
      }
      /*!
       * The method resets the filter
       */
      void reset_filter()
      {
          BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);)
          m_Filter.reset();
      }
  
      /*!
       * The method sets an exception handler function
       */
      template< typename FunT >
      void set_exception_handler(FunT const& handler)
      {
          BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);)
          m_ExceptionHandler = handler;
      }
  
      /*!
       * The method resets the exception handler function
       */
      void reset_exception_handler()
      {
          BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);)
          m_ExceptionHandler.clear();
      }
  
      /*!
       * The method returns \c true if no filter is set or the attribute values pass the filter
       *
       * \param attrs A set of attribute values of a logging record
       */
      bool will_consume(attribute_value_set const& attrs)
      {
          BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);)
          try
          {
              return m_Filter(attrs);
          }
  #if !defined(BOOST_LOG_NO_THREADS)
          catch (thread_interrupted&)
          {
              throw;
          }
  #endif
          catch (...)
          {
              if (m_ExceptionHandler.empty())
                  throw;
              m_ExceptionHandler();
              return false;
          }
      }
  
  protected:
  #if !defined(BOOST_LOG_NO_THREADS)
      //! Returns reference to the frontend mutex
      mutex_type& frontend_mutex() const { return m_Mutex; }
  #endif
  
      //! Returns reference to the exception handler
      exception_handler_type& exception_handler() { return m_ExceptionHandler; }
      //! Returns reference to the exception handler
      exception_handler_type const& exception_handler() const { return m_ExceptionHandler; }
  
      //! Feeds log record to the backend
      template< typename BackendMutexT, typename BackendT >
      void feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend)
      {
          try
          {
              BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);)
              backend.consume(rec);
          }
  #if !defined(BOOST_LOG_NO_THREADS)
          catch (thread_interrupted&)
          {
              throw;
          }
  #endif
          catch (...)
          {
              BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);)
              if (m_ExceptionHandler.empty())
                  throw;
              m_ExceptionHandler();
          }
      }
  
      //! Attempts to feeds log record to the backend, does not block if \a backend_mutex is locked
      template< typename BackendMutexT, typename BackendT >
      bool try_feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend)
      {
  #if !defined(BOOST_LOG_NO_THREADS)
          unique_lock< BackendMutexT > lock;
          try
          {
              unique_lock< BackendMutexT > tmp_lock(backend_mutex, try_to_lock);
              if (!tmp_lock.owns_lock())
                  return false;
              lock.swap(tmp_lock);
          }
          catch (thread_interrupted&)
          {
              throw;
          }
          catch (...)
          {
              boost::log::aux::shared_lock_guard< mutex_type > frontend_lock(this->frontend_mutex());
              if (this->exception_handler().empty())
                  throw;
              this->exception_handler()();
          }
  #endif
          // No need to lock anything in the feed_record method
          boost::log::aux::fake_mutex m;
          feed_record(rec, m, backend);
          return true;
      }
  
      //! Flushes record buffers in the backend, if one supports it
      template< typename BackendMutexT, typename BackendT >
      void flush_backend(BackendMutexT& backend_mutex, BackendT& backend)
      {
          typedef typename BackendT::frontend_requirements frontend_requirements;
          flush_backend_impl(backend_mutex, backend,
              typename has_requirement< frontend_requirements, flushing >::type());
      }
  
  private:
      //! Flushes record buffers in the backend (the actual implementation)
      template< typename BackendMutexT, typename BackendT >
      void flush_backend_impl(BackendMutexT& backend_mutex, BackendT& backend, mpl::true_)
      {
          try
          {
              BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);)
              backend.flush();
          }
  #if !defined(BOOST_LOG_NO_THREADS)
          catch (thread_interrupted&)
          {
              throw;
          }
  #endif
          catch (...)
          {
              BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);)
              if (m_ExceptionHandler.empty())
                  throw;
              m_ExceptionHandler();
          }
      }
      //! Flushes record buffers in the backend (stub for backends that don't support flushing)
      template< typename BackendMutexT, typename BackendT >
      void flush_backend_impl(BackendMutexT&, BackendT&, mpl::false_)
      {
      }
  };
  
  //! A base class for a logging sink frontend with formatting support
  template< typename CharT >
  class BOOST_LOG_NO_VTABLE basic_formatting_sink_frontend :
      public basic_sink_frontend
  {
      typedef basic_sink_frontend base_type;
  
  public:
      //! Character type
      typedef CharT char_type;
      //! Formatted string type
      typedef std::basic_string< char_type > string_type;
  
      //! Formatter function object type
      typedef basic_formatter< char_type > formatter_type;
      //! Output stream type
      typedef typename formatter_type::stream_type stream_type;
  
  #if !defined(BOOST_LOG_NO_THREADS)
  protected:
      //! Mutex type
      typedef typename base_type::mutex_type mutex_type;
  #endif
  
  private:
      struct formatting_context
      {
  #if !defined(BOOST_LOG_NO_THREADS)
          //! Object version
          const unsigned int m_Version;
  #endif
          //! Formatted log record storage
          string_type m_FormattedRecord;
          //! Formatting stream
          stream_type m_FormattingStream;
          //! Formatter functor
          formatter_type m_Formatter;
  
          formatting_context() :
  #if !defined(BOOST_LOG_NO_THREADS)
              m_Version(0),
  #endif
              m_FormattingStream(m_FormattedRecord)
          {
              m_FormattingStream.exceptions(std::ios_base::badbit | std::ios_base::failbit);
          }
  #if !defined(BOOST_LOG_NO_THREADS)
          formatting_context(unsigned int version, std::locale const& loc, formatter_type const& formatter) :
              m_Version(version),
              m_FormattingStream(m_FormattedRecord),
              m_Formatter(formatter)
          {
              m_FormattingStream.exceptions(std::ios_base::badbit | std::ios_base::failbit);
              m_FormattingStream.imbue(loc);
          }
  #endif
      };
  
  private:
  #if !defined(BOOST_LOG_NO_THREADS)
  
      //! State version
      volatile unsigned int m_Version;
  
      //! Formatter functor
      formatter_type m_Formatter;
      //! Locale to perform formatting
      std::locale m_Locale;
  
      //! Formatting state
      thread_specific_ptr< formatting_context > m_pContext;
  
  #else
  
      //! Formatting state
      formatting_context m_Context;
  
  #endif // !defined(BOOST_LOG_NO_THREADS)
  
  public:
      /*!
       * \brief Initializing constructor
       *
       * \param cross_thread The flag indicates whether the sink passes log records between different threads
       */
      explicit basic_formatting_sink_frontend(bool cross_thread) :
          basic_sink_frontend(cross_thread)
  #if !defined(BOOST_LOG_NO_THREADS)
          , m_Version(0)
  #endif
      {
      }
  
      /*!
       * The method sets sink-specific formatter function object
       */
      template< typename FunT >
      void set_formatter(FunT const& formatter)
      {
  #if !defined(BOOST_LOG_NO_THREADS)
          boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex());
          m_Formatter = formatter;
          ++m_Version;
  #else
          m_Context.m_Formatter = formatter;
  #endif
      }
      /*!
       * The method resets the formatter
       */
      void reset_formatter()
      {
  #if !defined(BOOST_LOG_NO_THREADS)
          boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex());
          m_Formatter.reset();
          ++m_Version;
  #else
          m_Context.m_Formatter.reset();
  #endif
      }
  
      /*!
       * The method returns the current locale used for formatting
       */
      std::locale getloc() const
      {
  #if !defined(BOOST_LOG_NO_THREADS)
          boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex());
          return m_Locale;
  #else
          return m_Context.m_FormattingStream.getloc();
  #endif
      }
      /*!
       * The method sets the locale used for formatting
       */
      void imbue(std::locale const& loc)
      {
  #if !defined(BOOST_LOG_NO_THREADS)
          boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex());
          m_Locale = loc;
          ++m_Version;
  #else
          m_Context.m_FormattingStream.imbue(loc);
  #endif
      }
  
  protected:
      //! Returns reference to the formatter
      formatter_type& formatter()
      {
  #if !defined(BOOST_LOG_NO_THREADS)
          return m_Formatter;
  #else
          return m_Context.m_Formatter;
  #endif
      }
  
      //! Feeds log record to the backend
      template< typename BackendMutexT, typename BackendT >
      void feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend)
      {
          formatting_context* context;
  
  #if !defined(BOOST_LOG_NO_THREADS)
          context = m_pContext.get();
          if (!context || context->m_Version != m_Version)
          {
              {
                  boost::log::aux::shared_lock_guard< mutex_type > lock(this->frontend_mutex());
                  context = new formatting_context(m_Version, m_Locale, m_Formatter);
              }
              m_pContext.reset(context);
          }
  #else
          context = &m_Context;
  #endif
  
          boost::log::aux::cleanup_guard< stream_type > cleanup1(context->m_FormattingStream);
          boost::log::aux::cleanup_guard< string_type > cleanup2(context->m_FormattedRecord);
  
          try
          {
              // Perform the formatting
              context->m_Formatter(rec, context->m_FormattingStream);
              context->m_FormattingStream.flush();
  
              // Feed the record
              BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);)
              backend.consume(rec, context->m_FormattedRecord);
          }
  #if !defined(BOOST_LOG_NO_THREADS)
          catch (thread_interrupted&)
          {
              throw;
          }
  #endif
          catch (...)
          {
              BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(this->frontend_mutex());)
              if (this->exception_handler().empty())
                  throw;
              this->exception_handler()();
          }
      }
  
      //! Attempts to feeds log record to the backend, does not block if \a backend_mutex is locked
      template< typename BackendMutexT, typename BackendT >
      bool try_feed_record(record_view const& rec, BackendMutexT& backend_mutex, BackendT& backend)
      {
  #if !defined(BOOST_LOG_NO_THREADS)
          unique_lock< BackendMutexT > lock;
          try
          {
              unique_lock< BackendMutexT > tmp_lock(backend_mutex, try_to_lock);
              if (!tmp_lock.owns_lock())
                  return false;
              lock.swap(tmp_lock);
          }
          catch (thread_interrupted&)
          {
              throw;
          }
          catch (...)
          {
              boost::log::aux::shared_lock_guard< mutex_type > frontend_lock(this->frontend_mutex());
              if (this->exception_handler().empty())
                  throw;
              this->exception_handler()();
          }
  #endif
          // No need to lock anything in the feed_record method
          boost::log::aux::fake_mutex m;
          feed_record(rec, m, backend);
          return true;
      }
  };
  
  namespace aux {
  
      template<
          typename BackendT,
          bool RequiresFormattingV = has_requirement<
              typename BackendT::frontend_requirements,
              formatted_records
          >::value
      >
      struct make_sink_frontend_base
      {
          typedef basic_sink_frontend type;
      };
      template< typename BackendT >
      struct make_sink_frontend_base< BackendT, true >
      {
          typedef basic_formatting_sink_frontend< typename BackendT::char_type > type;
      };
  
  } // namespace aux
  
  } // namespace sinks
  
  BOOST_LOG_CLOSE_NAMESPACE // namespace log
  
  } // namespace boost
  
  #include <boost/log/detail/footer.hpp>
  
  #endif // BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_
  