Browse Source

[1698-test] Avoid static initialization fiasco for MessageInitializer

Statically-declared MessageInitializer objects now store the address
of the message array in a pre-defined array of pointers.  Loading
the global message dictionary is done after main() is called.  This
avoids used of heap storage during static initialization, which
appears to cause problems on some systems.
Stephen Morris 13 years ago
parent
commit
4e7ace34db

+ 4 - 0
src/lib/log/logger_manager.cc

@@ -95,6 +95,10 @@ void
 LoggerManager::init(const std::string& root, isc::log::Severity severity,
                     int dbglevel, const char* file)
 {
+    // Load in the messages declared in the program and registered by 
+    // statically-declared MessageInitializer objects.
+    MessageInitializer::loadDictionary();
+
     // Save name, severity and debug level for later.  No need to save the
     // file name as once the local message file is read the messages will
     // not be lost.

+ 59 - 8
src/lib/log/message_initializer.cc

@@ -12,25 +12,76 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <cassert>
+#include <cstdlib>
 #include <log/message_dictionary.h>
 #include <log/message_initializer.h>
 
+
+// As explained in the header file, initialization of the message dictionary
+// is a two-stage process:
+// 1) In the MessageInitializer constructor, the a pointer to the array of
+//    messages is stored in a pre-defined array.  Since the MessageInitializers
+//    are declared external to 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 {
+
+// Declare the array of pointers to value arrays.  At the time of writing
+// (end of BIND 10's third year of development), only 25 such arrays have
+// been defined, so a value of 64 should allow for significant expansion.
+// (Besides, an assertion will be triggered if the array overflows.)
+const int MAX_LOGGERS = 64;
+const char** logger_values[MAX_LOGGERS];
+
+// Declare the index used to access the array.  As this needs to be initialized
+// at first used, it is accessed it via a function.
+size_t& getIndex() {
+    static size_t index = 0;
+    return (index);
+}
+
+}
+
+
 namespace isc {
 namespace log {
 
-// Constructor.  Just retrieve the global dictionary and load the IDs and
-// associated text into it.
+// Constructor.  Add the pointer to the message array to the global array.
+// This method will trigger an assertion failure if the array overflows.
 
 MessageInitializer::MessageInitializer(const char* values[]) {
+    assert(getIndex() < MAX_LOGGERS);
+    logger_values[getIndex()++] = values;
+}
+
+// Load the messages in the arrays registered in the logger_values array
+// into the global dictionary.
+
+void
+MessageInitializer::loadDictionary() {
     MessageDictionary& global = MessageDictionary::globalDictionary();
-    std::vector<std::string> repeats = global.load(values);
 
-    // Append the IDs in the list just loaded (the "repeats") to the global list
-    // of duplicate IDs.
-    if (!repeats.empty()) {
-        std::vector<std::string>& duplicates = getDuplicates();
-        duplicates.insert(duplicates.end(), repeats.begin(), repeats.end());
+    for (size_t i = 0; i < getIndex(); ++i) {
+        std::vector<std::string> repeats = global.load(logger_values[i]);
+
+        // Append the IDs in the list just loaded (the "repeats") to the
+        // global list of duplicate IDs.
+        if (!repeats.empty()) {
+            std::vector<std::string>& duplicates = getDuplicates();
+            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.)
+    getIndex() = 0;
 }
 
 // Return reference to duplicate array

+ 25 - 9
src/lib/log/message_initializer.h

@@ -37,28 +37,44 @@ namespace log {
 ///             :
 ///         NULL
 ///     };
-///     MessageDictionaryHelper xyz(values);
+///     MessageInitializer xyz(values);
 ///
-/// This will automatically add the message ID/text pairs to the global
-/// dictionary during initialization - all that is required is that the module
-/// containing the definition is included into the final executable.
+/// All that needed is for the module containing the definitions to be
+/// included in the execution unit.
 ///
-/// Messages are added via the MessageDictionary::add() method, so any
-/// duplicates are stored in the the global dictionary's overflow vector whence
-/// they can be retrieved at run-time.
+/// To avoid static initialization fiasco problems, the initialization is
+/// carried out in two stages:
+/// -# The constructor 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.
+///
+/// When messages are added to the dictionary, the are added via the
+/// MessageDictionary::add() method, so any duplicates are stored in the the
+/// global dictionary's overflow vector whence they can be retrieved at
+/// run-time.
 
 class MessageInitializer {
 public:
 
     /// \brief Constructor
     ///
-    /// Adds the array of values to the global dictionary, and notes any
-    /// duplicates.
+    /// Adds a pointer to the array of messages to the global array of
+    /// pointers to message arrays.
     ///
     /// \param values NULL-terminated array of alternating identifier strings
     /// and associated message text.
     MessageInitializer(const char* values[]);
 
+    /// \brief Run-Time Initialization
+    ///
+    /// Loops through the internal array of pointers to message arrays
+    /// and adds the messages to the internal dictionary.  This is called
+    /// during run-time initialization.
+    static void loadDictionary();
+
     /// \brief Return Duplicates
     ///
     /// When messages are added to the global dictionary, any duplicates are