From: Marcin Siodelski Date: Tue, 21 Apr 2015 10:16:33 +0000 (+0200) Subject: [3823] Fixed static deinitialization fiasco in MessageInitializer. X-Git-Tag: trac3824_base~8^2~3 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b6a13514e6a0722769cdec113692cbb61794a072;p=thirdparty%2Fkea.git [3823] Fixed static deinitialization fiasco in MessageInitializer. --- diff --git a/src/lib/log/message_initializer.cc b/src/lib/log/message_initializer.cc index c187377c72..7ea8383d69 100644 --- a/src/lib/log/message_initializer.cc +++ b/src/lib/log/message_initializer.cc @@ -14,40 +14,37 @@ #include #include -#include #include #include #include - -// As explained in the header file, initialization of the message dictionary -// is a two-stage process: -// 1) In the MessageInitializer constructor, a pointer to the array of -// messages is stored in a pre-defined array. Since the MessageInitializers -// are declared statically outside a program unit, this takes place before -// main() is called. As no heap storage is allocated in this process, we -// should avoid the static initialization fiasco in cases where -// initialization of system libraries is also carried out at the same time. -// 2) After main() starts executing, loadDictionary() is called. -// -// - namespace { -/// Type definition for the list of pointers to messages. -typedef std::list LoggerValuesList; - -/// Return reference to the list of log messages. -LoggerValuesList& getNonConstLoggerValues() { - static LoggerValuesList logger_values; +using namespace isc::log; + +/// @brief Returns the shared pointer to the list of pointers to the +/// log messages defined. +/// +/// The returned pointer must be held in the \c MessageInitializer object +/// throughout its lifetime to make sure that the object doesn't outlive +/// the list and may still access it in the destructor. The returned +/// pointer is shared between all \c MessageInitializer instances. +LoggerValuesListPtr +getNonConstLoggerValues() { + static LoggerValuesListPtr logger_values(new LoggerValuesList()); return (logger_values); } -// Return the duplicates singleton version (non-const for local use) -std::list& +/// @brief Returns the pointer to the list of message duplicates. +/// +/// The returned pointer must be held in the \c MessageInitializer object +/// throughout its lifetime to make sure that the object doesn't outlive +/// the list and may still access it in the destructor. The returned +/// pointer is shared between all \c MessageInitializer instances. +LoggerDuplicatesListPtr getNonConstDuplicates() { - static std::list duplicates; + static LoggerDuplicatesListPtr duplicates(new LoggerDuplicatesList()); return (duplicates); } } // end unnamed namespace @@ -61,30 +58,30 @@ namespace log { MessageInitializer::MessageInitializer(const char* values[]) : values_(values), - global_dictionary_(MessageDictionary::globalDictionary()) { - assert(getNonConstLoggerValues().size() < MAX_MESSAGE_ARRAYS); - getNonConstLoggerValues().push_back(values); + global_dictionary_(MessageDictionary::globalDictionary()), + global_logger_values_(getNonConstLoggerValues()), + global_logger_duplicates_(getNonConstDuplicates()) { + assert(global_logger_values_->size() < MAX_MESSAGE_ARRAYS); + global_logger_values_->push_back(values); } MessageInitializer::~MessageInitializer() { - LoggerValuesList& logger_values = getNonConstLoggerValues(); - // Search for the pointer to pending messages belonging to our instance. - LoggerValuesList::iterator my_messages = std::find(logger_values.begin(), - logger_values.end(), + LoggerValuesList::iterator my_messages = std::find(global_logger_values_->begin(), + global_logger_values_->end(), values_); - bool pending = (my_messages != logger_values.end()); + bool pending = (my_messages != global_logger_values_->end()); // Our messages are still pending, so let's remove them from the list // of pending messages. if (pending) { - logger_values.erase(my_messages); + global_logger_values_->erase(my_messages); } else { // Our messages are not pending, so they might have been loaded to // the dictionary and/or duplicates. int i = 0; while (values_[i]) { - getNonConstDuplicates().remove(values_[i]); + global_logger_duplicates_->remove(values_[i]); global_dictionary_->erase(values_[i], values_[i + 1]); i += 2; } @@ -95,7 +92,7 @@ MessageInitializer::~MessageInitializer() { size_t MessageInitializer::getPendingCount() { - return (getNonConstLoggerValues().size()); + return (getNonConstLoggerValues()->size()); } // Load the messages in the arrays registered in the logger_values array @@ -104,37 +101,36 @@ MessageInitializer::getPendingCount() { void MessageInitializer::loadDictionary(bool ignore_duplicates) { const MessageDictionaryPtr& global = MessageDictionary::globalDictionary(); - LoggerValuesList& logger_values = getNonConstLoggerValues(); + const LoggerValuesListPtr& logger_values = getNonConstLoggerValues(); - for (LoggerValuesList::const_iterator values = logger_values.begin(); - values != logger_values.end(); ++values) { + for (LoggerValuesList::const_iterator values = logger_values->begin(); + values != logger_values->end(); ++values) { std::vector repeats = global->load(*values); // Append the IDs in the list just loaded (the "repeats") to the // global list of duplicate IDs. if (!ignore_duplicates && !repeats.empty()) { - std::list& duplicates = getNonConstDuplicates(); - duplicates.insert(duplicates.end(), repeats.begin(), - repeats.end()); + const LoggerDuplicatesListPtr& duplicates = getNonConstDuplicates(); + duplicates->insert(duplicates->end(), repeats.begin(), repeats.end()); } } // ... and mark that the messages have been loaded. (This avoids a lot // of "duplicate message ID" messages in some of the unit tests where the // logging initialization code may be called multiple times.) - logger_values.clear(); + logger_values->clear(); } // Return reference to duplicates vector const std::list& MessageInitializer::getDuplicates() { - return (getNonConstDuplicates()); + return (*getNonConstDuplicates()); } // Clear the duplicates vector void MessageInitializer::clearDuplicates() { - getNonConstDuplicates().clear(); + getNonConstDuplicates()->clear(); } } // namespace log diff --git a/src/lib/log/message_initializer.h b/src/lib/log/message_initializer.h index a742ea238a..e4df35c93e 100644 --- a/src/lib/log/message_initializer.h +++ b/src/lib/log/message_initializer.h @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -24,6 +25,20 @@ namespace isc { namespace log { +/// @name Type definitions for containers shared among instances of the class. +/// +//\{ +/// \brief List of pointers to the messages. +typedef std::list LoggerValuesList; +/// \brief Shared pointer to the list of pointers to the messages. +typedef boost::shared_ptr LoggerValuesListPtr; + +/// \brief List of duplicated messages. +typedef std::list LoggerDuplicatesList; +/// \brief Shared pointer to the list of duplicated messages. +typedef boost::shared_ptr LoggerDuplicatesListPtr; +//\} + /// \brief Initialize Message Dictionary /// /// This is a helper class to add a set of message IDs and associated text to @@ -47,14 +62,13 @@ namespace log { /// Dynamically loaded modules should call the initializer as well on the /// moment they are instantiated. /// -/// To avoid static initialization fiasco problems, the initialization is -/// carried out in two stages: -/// - The constructor adds a pointer to the values array to a pre-defined array -/// of pointers. -/// - During the run-time initialization of the logging system, the static -/// method loadDictionary() is called to load the message dictionary. -/// This way, no heap storage is allocated during the static initialization, -/// something that may give problems on some operating systems. +/// To avoid static initialization fiasco problems, the containers shared by +/// all instances of this class are dynamically allocated on first use, and +/// held in the smart pointers which are de-allocated only when all instances +/// of the class are destructred. After the object has been created with the +/// constructor, the \c MessageInitializer::loadDictionary static function is +/// called to populate the messages defined in various instances of the +/// \c MessageInitializer class to the global dictionary. /// /// \note The maximum number of message arrays that can be added to the /// dictionary in this way is defined by the constant @@ -144,6 +158,24 @@ private: /// before the \c MessageInitializer destructor is called, causing the /// static initialization order fiasco. MessageDictionaryPtr global_dictionary_; + + /// \brief Holds the shared pointer to the list of pointers to the + /// log messages defined by various instances of this class. + /// + /// This pointer must be initialized in the constructor and held + /// throughout the lifetime of the \c MessageInitializer object. This + /// prevents static deinitialization fiasco when trying to access the + /// values in the list from the destructor of this class. + LoggerValuesListPtr global_logger_values_; + + /// \brief Holds the shared pointer to the collection od duplicated + /// messages. + /// + /// This pointer must be initialized in the constructor and held + /// throughout the lifetime of the \c MessageInitializer object. This + /// prevents static deinitialization fiasco when trying to access the + /// values in the list from the destructor of this class. + LoggerDuplicatesListPtr global_logger_duplicates_; }; } // namespace log