Переглянути джерело

Provide ShutdownProtobufLibrary() which frees all startup-allocated objects.

kenton@google.com 16 роки тому
батько
коміт
63e646b7ad

+ 29 - 5
src/google/protobuf/compiler/cpp/cpp_file.cc

@@ -143,20 +143,23 @@ void FileGenerator::GenerateHeader(io::Printer* printer) {
   // Open namespace.
   GenerateNamespaceOpeners(printer);
 
-  // Forward-declare the AddDescriptors and AssignDescriptors functions, so
-  // that we can declare them to be friends of each class.
+  // Forward-declare the AddDescriptors, AssignDescriptors, and ShutdownFile
+  // functions, so that we can declare them to be friends of each class.
   printer->Print(
     "\n"
     "// Internal implementation detail -- do not call these.\n"
     "void $dllexport_decl$ $adddescriptorsname$();\n",
     "adddescriptorsname", GlobalAddDescriptorsName(file_->name()),
     "dllexport_decl", dllexport_decl_);
+
   printer->Print(
-    // Note that we don't put dllexport_decl on this because it is only called
-    // by the .pb.cc file in which it is defined.
+    // Note that we don't put dllexport_decl on these because they are only
+    // called by the .pb.cc file in which they are defined.
     "void $assigndescriptorsname$();\n"
+    "void $shutdownfilename$();\n"
     "\n",
-    "assigndescriptorsname", GlobalAssignDescriptorsName(file_->name()));
+    "assigndescriptorsname", GlobalAssignDescriptorsName(file_->name()),
+    "shutdownfilename", GlobalShutdownFileName(file_->name()));
 
   // Generate forward declarations of classes.
   for (int i = 0; i < file_->message_type_count(); i++) {
@@ -390,6 +393,23 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) {
 
   // -----------------------------------------------------------------
 
+  // ShutdownFile():  Deletes descriptors, default instances, etc. on shutdown.
+  printer->Print(
+    "\n"
+    "void $shutdownfilename$() {\n",
+    "shutdownfilename", GlobalShutdownFileName(file_->name()));
+  printer->Indent();
+
+  for (int i = 0; i < file_->message_type_count(); i++) {
+    message_generators_[i]->GenerateShutdownCode(printer);
+  }
+
+  printer->Outdent();
+  printer->Print(
+    "}\n");
+
+  // -----------------------------------------------------------------
+
   // Now generate the AddDescriptors() function.
   printer->Print(
     "\n"
@@ -462,6 +482,10 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) {
     message_generators_[i]->GenerateDefaultInstanceInitializer(printer);
   }
 
+  printer->Print(
+    "::google::protobuf::internal::OnShutdown(&$shutdownfilename$);\n",
+    "shutdownfilename", GlobalShutdownFileName(file_->name()));
+
   printer->Outdent();
 
   printer->Print(

+ 5 - 0
src/google/protobuf/compiler/cpp/cpp_helpers.cc

@@ -276,6 +276,11 @@ string GlobalAssignDescriptorsName(const string& filename) {
   return "protobuf_AssignDesc_" + FilenameIdentifier(filename);
 }
 
+// Return the name of the ShutdownFile() function for a given file.
+string GlobalShutdownFileName(const string& filename) {
+  return "protobuf_ShutdownFile_" + FilenameIdentifier(filename);
+}
+
 }  // namespace cpp
 }  // namespace compiler
 }  // namespace protobuf

+ 3 - 0
src/google/protobuf/compiler/cpp/cpp_helpers.h

@@ -102,6 +102,9 @@ string GlobalAddDescriptorsName(const string& filename);
 // Return the name of the AssignDescriptors() function for a given file.
 string GlobalAssignDescriptorsName(const string& filename);
 
+// Return the name of the ShutdownFile() function for a given file.
+string GlobalShutdownFileName(const string& filename);
+
 }  // namespace cpp
 }  // namespace compiler
 }  // namespace protobuf

+ 20 - 4
src/google/protobuf/compiler/cpp/cpp_message.cc

@@ -421,17 +421,20 @@ GenerateClassDefinition(io::Printer* printer) {
                      .GeneratePrivateMembers(printer);
   }
 
-  // Declare AddDescriptors() and BuildDescriptors() as friends so that they
-  // can assign private static variables like default_instance_ and reflection_.
+  // Declare AddDescriptors(), BuildDescriptors(), and ShutdownFile() as
+  // friends so that they can access private static variables like
+  // default_instance_ and reflection_.
   printer->Print(
     "friend void $dllexport_decl$ $adddescriptorsname$();\n",
     "dllexport_decl", dllexport_decl_,
     "adddescriptorsname",
       GlobalAddDescriptorsName(descriptor_->file()->name()));
   printer->Print(
-    "friend void $assigndescriptorsname$();\n",
+    "friend void $assigndescriptorsname$();\n"
+    "friend void $shutdownfilename$();\n",
     "assigndescriptorsname",
-      GlobalAssignDescriptorsName(descriptor_->file()->name()));
+      GlobalAssignDescriptorsName(descriptor_->file()->name()),
+    "shutdownfilename", GlobalShutdownFileName(descriptor_->file()->name()));
 
   // Generate offsets and _has_bits_ boilerplate.
   if (descriptor_->field_count() > 0) {
@@ -599,6 +602,19 @@ GenerateDefaultInstanceInitializer(io::Printer* printer) {
   }
 }
 
+void MessageGenerator::
+GenerateShutdownCode(io::Printer* printer) {
+  printer->Print(
+    "delete $classname$::default_instance_;\n"
+    "delete $classname$_reflection_;\n",
+    "classname", classname_);
+
+  // Handle nested types.
+  for (int i = 0; i < descriptor_->nested_type_count(); i++) {
+    nested_generators_[i]->GenerateShutdownCode(printer);
+  }
+}
+
 void MessageGenerator::
 GenerateClassMethods(io::Printer* printer) {
   for (int i = 0; i < descriptor_->enum_type_count(); i++) {

+ 4 - 0
src/google/protobuf/compiler/cpp/cpp_message.h

@@ -98,6 +98,10 @@ class MessageGenerator {
   // allocated before any can be initialized.
   void GenerateDefaultInstanceInitializer(io::Printer* printer);
 
+  // Generates code that should be run when ShutdownProtobufLibrary() is called,
+  // to delete all dynamically-allocated objects.
+  void GenerateShutdownCode(io::Printer* printer);
+
   // Generate all non-inline methods for this class.
   void GenerateClassMethods(io::Printer* printer);
 

+ 16 - 11
src/google/protobuf/descriptor.cc

@@ -47,6 +47,7 @@
 #include <google/protobuf/io/coded_stream.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
 #include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/once.h>
 #include <google/protobuf/stubs/strutil.h>
 #include <google/protobuf/stubs/substitute.h>
 #include <google/protobuf/stubs/map-util.h>
@@ -797,30 +798,34 @@ namespace {
 
 EncodedDescriptorDatabase* generated_database_ = NULL;
 DescriptorPool* generated_pool_ = NULL;
+GOOGLE_PROTOBUF_DECLARE_ONCE(generated_pool_init_);
+
+void DeleteGeneratedPool() {
+  delete generated_database_;
+  generated_database_ = NULL;
+  delete generated_pool_;
+  generated_pool_ = NULL;
+}
 
 void InitGeneratedPool() {
-  GOOGLE_CHECK(generated_pool_ == NULL);
   generated_database_ = new EncodedDescriptorDatabase;
   generated_pool_ = new DescriptorPool(generated_database_);
+  internal::OnShutdown(&DeleteGeneratedPool);
 }
 
-// Force InitGeneratedPool to be called at static init time, before any threads
-// can be created.
-struct Initializer {
-  Initializer() {
-    if (generated_pool_ == NULL) InitGeneratedPool();
-  }
-} initializer;
+inline void InitGeneratedPoolOnce() {
+  GoogleOnceInit(&generated_pool_init_, &InitGeneratedPool);
+}
 
 }  // anonymous namespace
 
 const DescriptorPool* DescriptorPool::generated_pool() {
-  if (generated_pool_ == NULL) InitGeneratedPool();
+  InitGeneratedPoolOnce();
   return generated_pool_;
 }
 
 DescriptorPool* DescriptorPool::internal_generated_pool() {
-  if (generated_pool_ == NULL) InitGeneratedPool();
+  InitGeneratedPoolOnce();
   return generated_pool_;
 }
 
@@ -848,7 +853,7 @@ void DescriptorPool::InternalAddGeneratedFile(
   // Therefore, when we parse one, we have to be very careful to avoid using
   // any descriptor-based operations, since this might cause infinite recursion
   // or deadlock.
-  if (generated_pool_ == NULL) InitGeneratedPool();
+  InitGeneratedPoolOnce();
   GOOGLE_CHECK(generated_database_->Add(encoded_file_descriptor, size));
 }
 

+ 40 - 0
src/google/protobuf/descriptor.pb.cc

@@ -451,6 +451,45 @@ void protobuf_RegisterTypes() {
 
 }  // namespace
 
+void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto() {
+  delete FileDescriptorSet::default_instance_;
+  delete FileDescriptorSet_reflection_;
+  delete FileDescriptorProto::default_instance_;
+  delete FileDescriptorProto_reflection_;
+  delete DescriptorProto::default_instance_;
+  delete DescriptorProto_reflection_;
+  delete DescriptorProto_ExtensionRange::default_instance_;
+  delete DescriptorProto_ExtensionRange_reflection_;
+  delete FieldDescriptorProto::default_instance_;
+  delete FieldDescriptorProto_reflection_;
+  delete EnumDescriptorProto::default_instance_;
+  delete EnumDescriptorProto_reflection_;
+  delete EnumValueDescriptorProto::default_instance_;
+  delete EnumValueDescriptorProto_reflection_;
+  delete ServiceDescriptorProto::default_instance_;
+  delete ServiceDescriptorProto_reflection_;
+  delete MethodDescriptorProto::default_instance_;
+  delete MethodDescriptorProto_reflection_;
+  delete FileOptions::default_instance_;
+  delete FileOptions_reflection_;
+  delete MessageOptions::default_instance_;
+  delete MessageOptions_reflection_;
+  delete FieldOptions::default_instance_;
+  delete FieldOptions_reflection_;
+  delete EnumOptions::default_instance_;
+  delete EnumOptions_reflection_;
+  delete EnumValueOptions::default_instance_;
+  delete EnumValueOptions_reflection_;
+  delete ServiceOptions::default_instance_;
+  delete ServiceOptions_reflection_;
+  delete MethodOptions::default_instance_;
+  delete MethodOptions_reflection_;
+  delete UninterpretedOption::default_instance_;
+  delete UninterpretedOption_reflection_;
+  delete UninterpretedOption_NamePart::default_instance_;
+  delete UninterpretedOption_NamePart_reflection_;
+}
+
 void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() {
   static bool already_here = false;
   if (already_here) return;
@@ -584,6 +623,7 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() {
   MethodOptions::default_instance_->InitAsDefaultInstance();
   UninterpretedOption::default_instance_->InitAsDefaultInstance();
   UninterpretedOption_NamePart::default_instance_->InitAsDefaultInstance();
+  ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto);
 }
 
 // Force AddDescriptors() to be called at static initialization time.

+ 19 - 0
src/google/protobuf/descriptor.pb.h

@@ -28,6 +28,7 @@ namespace protobuf {
 // Internal implementation detail -- do not call these.
 void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
 void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
 
 class FileDescriptorSet;
 class FileDescriptorProto;
@@ -210,6 +211,7 @@ class LIBPROTOBUF_EXPORT FileDescriptorSet : public ::google::protobuf::Message
   ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto > file_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -381,6 +383,7 @@ class LIBPROTOBUF_EXPORT FileDescriptorProto : public ::google::protobuf::Messag
   ::google::protobuf::FileOptions* options_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(8 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -475,6 +478,7 @@ class LIBPROTOBUF_EXPORT DescriptorProto_ExtensionRange : public ::google::proto
   ::google::protobuf::int32 end_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -630,6 +634,7 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message {
   ::google::protobuf::MessageOptions* options_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(7 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -849,6 +854,7 @@ class LIBPROTOBUF_EXPORT FieldDescriptorProto : public ::google::protobuf::Messa
   ::google::protobuf::FieldOptions* options_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(8 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -958,6 +964,7 @@ class LIBPROTOBUF_EXPORT EnumDescriptorProto : public ::google::protobuf::Messag
   ::google::protobuf::EnumOptions* options_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -1064,6 +1071,7 @@ class LIBPROTOBUF_EXPORT EnumValueDescriptorProto : public ::google::protobuf::M
   ::google::protobuf::EnumValueOptions* options_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -1173,6 +1181,7 @@ class LIBPROTOBUF_EXPORT ServiceDescriptorProto : public ::google::protobuf::Mes
   ::google::protobuf::ServiceOptions* options_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -1295,6 +1304,7 @@ class LIBPROTOBUF_EXPORT MethodDescriptorProto : public ::google::protobuf::Mess
   ::google::protobuf::MethodOptions* options_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -1448,6 +1458,7 @@ class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message {
   ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(5 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -1547,6 +1558,7 @@ class LIBPROTOBUF_EXPORT MessageOptions : public ::google::protobuf::Message {
   ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -1696,6 +1708,7 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message {
   ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(5 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -1787,6 +1800,7 @@ class LIBPROTOBUF_EXPORT EnumOptions : public ::google::protobuf::Message {
   ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -1878,6 +1892,7 @@ class LIBPROTOBUF_EXPORT EnumValueOptions : public ::google::protobuf::Message {
   ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -1969,6 +1984,7 @@ class LIBPROTOBUF_EXPORT ServiceOptions : public ::google::protobuf::Message {
   ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -2060,6 +2076,7 @@ class LIBPROTOBUF_EXPORT MethodOptions : public ::google::protobuf::Message {
   ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption > uninterpreted_option_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -2158,6 +2175,7 @@ class LIBPROTOBUF_EXPORT UninterpretedOption_NamePart : public ::google::protobu
   bool is_extension_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?
@@ -2297,6 +2315,7 @@ class LIBPROTOBUF_EXPORT UninterpretedOption : public ::google::protobuf::Messag
   static const ::std::string _default_string_value_;
   friend void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto();
   friend void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto();
+  friend void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto();
   ::google::protobuf::uint32 _has_bits_[(6 + 31) / 32];
   
   // WHY DOES & HAVE LOWER PRECEDENCE THAN != !?

+ 12 - 1
src/google/protobuf/extension_set.cc

@@ -78,11 +78,22 @@ struct ExtensionInfo {
 
 typedef hash_map<pair<const Message*, int>, ExtensionInfo> ExtensionRegistry;
 ExtensionRegistry* registry_ = NULL;
+GOOGLE_PROTOBUF_DECLARE_ONCE(registry_init_);
+
+void DeleteRegistry() {
+  delete registry_;
+  registry_ = NULL;
+}
+
+void InitRegistry() {
+  registry_ = new ExtensionRegistry;
+  internal::OnShutdown(&DeleteRegistry);
+}
 
 // This function is only called at startup, so there is no need for thread-
 // safety.
 void Register(const Message* containing_type, int number, ExtensionInfo info) {
-  if (registry_ == NULL) registry_ = new ExtensionRegistry;
+  GoogleOnceInit(&registry_init_, &InitRegistry);
 
   if (!InsertIfNotPresent(registry_, make_pair(containing_type, number),
                           info)) {

+ 67 - 22
src/google/protobuf/stubs/common.cc

@@ -31,10 +31,12 @@
 // Author: kenton@google.com (Kenton Varda)
 
 #include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/once.h>
 #include <google/protobuf/stubs/strutil.h>
 #include <google/protobuf/stubs/substitute.h>
 #include <stdio.h>
 #include <errno.h>
+#include <vector>
 
 #include "config.h"
 
@@ -114,26 +116,20 @@ void NullLogHandler(LogLevel level, const char* filename, int line,
 static LogHandler* log_handler_ = &DefaultLogHandler;
 static int log_silencer_count_ = 0;
 
-// Mutex which protects log_silencer_count_.  We provide a static function to
-// get it so that it is initialized on first use, which be during
-// initialization time.  If we just allocated it as a global variable, it might
-// not be initialized before someone tries to use it.
-static Mutex* LogSilencerMutex() {
-  static Mutex* log_silencer_count_mutex_ = new Mutex;
-  return log_silencer_count_mutex_;
-}
-
-// Forces the above mutex to be initialized during startup.  This way we don't
-// have to worry about the initialization itself being thread-safe, since no
-// threads should exist yet at startup time.  (Otherwise we'd have no way to
-// make things thread-safe here because we'd need a Mutex for that, and we'd
-// have no way to construct one safely!)
-static struct LogSilencerMutexInitializer {
-  LogSilencerMutexInitializer() {
-    LogSilencerMutex();
-  }
-} log_silencer_mutex_initializer;
+static Mutex* log_silencer_count_mutex_ = NULL;
+GOOGLE_PROTOBUF_DECLARE_ONCE(log_silencer_count_init_);
 
+void DeleteLogSilencerCount() {
+  delete log_silencer_count_mutex_;
+  log_silencer_count_mutex_ = NULL;
+}
+void InitLogSilencerCount() {
+  log_silencer_count_mutex_ = new Mutex;
+  OnShutdown(&DeleteLogSilencerCount);
+}
+void InitLogSilencerCountOnce() {
+  GoogleOnceInit(&log_silencer_count_init_, &InitLogSilencerCount);
+}
 
 static string SimpleCtoa(char c) { return string(1, c); }
 
@@ -160,7 +156,8 @@ void LogMessage::Finish() {
   bool suppress = false;
 
   if (level_ != LOGLEVEL_FATAL) {
-    MutexLock lock(internal::LogSilencerMutex());
+    InitLogSilencerCountOnce();
+    MutexLock lock(log_silencer_count_mutex_);
     suppress = internal::log_silencer_count_ > 0;
   }
 
@@ -193,12 +190,14 @@ LogHandler* SetLogHandler(LogHandler* new_func) {
 }
 
 LogSilencer::LogSilencer() {
-  MutexLock lock(internal::LogSilencerMutex());
+  internal::InitLogSilencerCountOnce();
+  MutexLock lock(internal::log_silencer_count_mutex_);
   ++internal::log_silencer_count_;
 };
 
 LogSilencer::~LogSilencer() {
-  MutexLock lock(internal::LogSilencerMutex());
+  internal::InitLogSilencerCountOnce();
+  MutexLock lock(internal::log_silencer_count_mutex_);
   --internal::log_silencer_count_;
 };
 
@@ -291,5 +290,51 @@ void Mutex::AssertHeld() {
 
 #endif
 
+// ===================================================================
+// Shutdown support.
+
+namespace internal {
+
+typedef void OnShutdownFunc();
+vector<void (*)()>* shutdown_functions = NULL;
+Mutex* shutdown_functions_mutex = NULL;
+GOOGLE_PROTOBUF_DECLARE_ONCE(shutdown_functions_init);
+
+void InitShutdownFunctions() {
+  shutdown_functions = new vector<void (*)()>;
+  shutdown_functions_mutex = new Mutex;
+}
+
+inline void InitShutdownFunctionsOnce() {
+  GoogleOnceInit(&shutdown_functions_init, &InitShutdownFunctions);
+}
+
+void OnShutdown(void (*func)()) {
+  InitShutdownFunctionsOnce();
+  MutexLock lock(shutdown_functions_mutex);
+  shutdown_functions->push_back(func);
+}
+
+}  // namespace internal
+
+void ShutdownProtobufLibrary() {
+  internal::InitShutdownFunctionsOnce();
+
+  // We don't need to lock shutdown_functions_mutex because it's up to the
+  // caller to make sure that no one is using the library before this is
+  // called.
+
+  // Make it safe to call this multiple times.
+  if (internal::shutdown_functions == NULL) return;
+
+  for (int i = 0; i < internal::shutdown_functions->size(); i++) {
+    internal::shutdown_functions->at(i)();
+  }
+  delete internal::shutdown_functions;
+  internal::shutdown_functions = NULL;
+  delete internal::shutdown_functions_mutex;
+  internal::shutdown_functions_mutex = NULL;
+}
+
 }  // namespace protobuf
 }  // namespace google

+ 25 - 0
src/google/protobuf/stubs/common.h

@@ -1085,6 +1085,31 @@ LIBPROTOBUF_EXPORT bool IsStructurallyValidUTF8(const char* buf, int len);
 
 }  // namespace internal
 
+// ===================================================================
+// Shutdown support.
+
+// Shut down the entire protocol buffers library, deleting all static-duration
+// objects allocated by the library or by generated .pb.cc files.
+//
+// There are two reasons you might want to call this:
+// * You use a draconian definition of "memory leak" in which you expect
+//   every single malloc() to have a corresponding free(), even for objects
+//   which live until program exit.
+// * You are writing a dynamically-loaded library which needs to clean up
+//   after itself when the library is unloaded.
+//
+// It is safe to call this multiple times.  However, it is not safe to use
+// any other part of the protocol buffers library after
+// ShutdownProtobufLibrary() has been called.
+LIBPROTOBUF_EXPORT void ShutdownProtobufLibrary();
+
+namespace internal {
+
+// Register a function to be called when ShutdownProtocolBuffers() is called.
+LIBPROTOBUF_EXPORT void OnShutdown(void (*func)());
+
+}  // namespace internal
+
 }  // namespace protobuf
 }  // namespace google
 

+ 14 - 0
src/google/protobuf/testing/googletest.cc

@@ -233,5 +233,19 @@ void ScopedMemoryLog::HandleLog(LogLevel level, const char* filename,
   }
 }
 
+namespace {
+
+// Force shutdown at process exit so that we can test for memory leaks.  To
+// actually check for leaks, I suggest using the heap checker included with
+// google-perftools.  Set it to "draconian" mode to ensure that every last
+// call to malloc() has a corresponding free().
+struct ForceShutdown {
+  ~ForceShutdown() {
+    ShutdownProtobufLibrary();
+  }
+} force_shutdown;
+
+}  // namespace
+
 }  // namespace protobuf
 }  // namespace google