From 63e646b7ad6f18228c1807f8d18111ae96e86aa7 Mon Sep 17 00:00:00 2001 From: "kenton@google.com" Date: Wed, 6 May 2009 19:27:03 +0000 Subject: [PATCH] Provide ShutdownProtobufLibrary() which frees all startup-allocated objects. --- src/google/protobuf/compiler/cpp/cpp_file.cc | 34 +++++-- .../protobuf/compiler/cpp/cpp_helpers.cc | 5 ++ .../protobuf/compiler/cpp/cpp_helpers.h | 3 + .../protobuf/compiler/cpp/cpp_message.cc | 24 ++++- .../protobuf/compiler/cpp/cpp_message.h | 4 + src/google/protobuf/descriptor.cc | 33 ++++--- src/google/protobuf/descriptor.pb.cc | 40 +++++++++ src/google/protobuf/descriptor.pb.h | 19 ++++ src/google/protobuf/extension_set.cc | 13 ++- src/google/protobuf/stubs/common.cc | 89 ++++++++++++++----- src/google/protobuf/stubs/common.h | 25 ++++++ src/google/protobuf/testing/googletest.cc | 14 +++ 12 files changed, 257 insertions(+), 46 deletions(-) diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index dcc485521..f056ed573 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/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( diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc index 214daff9b..070fde55a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.cc +++ b/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 diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.h b/src/google/protobuf/compiler/cpp/cpp_helpers.h index 2260a934e..55fd7e295 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.h +++ b/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 diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index c55066994..95f20a605 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/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++) { diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h index 31aa1c4cd..105574a76 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.h +++ b/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); diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc index ecca32934..dd7f28c81 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -797,30 +798,34 @@ namespace { EncodedDescriptorDatabase* generated_database_ = NULL; DescriptorPool* generated_pool_ = NULL; +GOOGLE_PROTOBUF_DECLARE_ONCE(generated_pool_init_); -void InitGeneratedPool() { - GOOGLE_CHECK(generated_pool_ == NULL); - generated_database_ = new EncodedDescriptorDatabase; - generated_pool_ = new DescriptorPool(generated_database_); +void DeleteGeneratedPool() { + delete generated_database_; + generated_database_ = NULL; + delete generated_pool_; + generated_pool_ = NULL; } -// Force InitGeneratedPool to be called at static init time, before any threads -// can be created. -struct Initializer { - Initializer() { - if (generated_pool_ == NULL) InitGeneratedPool(); - } -} initializer; +void InitGeneratedPool() { + generated_database_ = new EncodedDescriptorDatabase; + generated_pool_ = new DescriptorPool(generated_database_); + internal::OnShutdown(&DeleteGeneratedPool); +} + +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)); } diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc index 6f0b5b546..d4a7666b2 100644 --- a/src/google/protobuf/descriptor.pb.cc +++ b/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. diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h index 7512e407f..7247bfd63 100644 --- a/src/google/protobuf/descriptor.pb.h +++ b/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 != !? diff --git a/src/google/protobuf/extension_set.cc b/src/google/protobuf/extension_set.cc index 0e6189023..f45eafa58 100644 --- a/src/google/protobuf/extension_set.cc +++ b/src/google/protobuf/extension_set.cc @@ -78,11 +78,22 @@ struct ExtensionInfo { typedef hash_map, 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(®istry_init_, &InitRegistry); if (!InsertIfNotPresent(registry_, make_pair(containing_type, number), info)) { diff --git a/src/google/protobuf/stubs/common.cc b/src/google/protobuf/stubs/common.cc index 385ea8aec..da73c5661 100644 --- a/src/google/protobuf/stubs/common.cc +++ b/src/google/protobuf/stubs/common.cc @@ -31,10 +31,12 @@ // Author: kenton@google.com (Kenton Varda) #include +#include #include #include #include #include +#include #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_; +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); } - -// 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 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* shutdown_functions = NULL; +Mutex* shutdown_functions_mutex = NULL; +GOOGLE_PROTOBUF_DECLARE_ONCE(shutdown_functions_init); + +void InitShutdownFunctions() { + shutdown_functions = new vector; + 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 diff --git a/src/google/protobuf/stubs/common.h b/src/google/protobuf/stubs/common.h index 4a55e0046..3367d4cea 100644 --- a/src/google/protobuf/stubs/common.h +++ b/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 diff --git a/src/google/protobuf/testing/googletest.cc b/src/google/protobuf/testing/googletest.cc index 99dbbb32c..1339b3327 100644 --- a/src/google/protobuf/testing/googletest.cc +++ b/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