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

This commit is contained in:
kenton@google.com 2009-05-06 19:27:03 +00:00
parent 9824eda6b5
commit 63e646b7ad
12 changed files with 257 additions and 46 deletions

View File

@ -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(

View File

@ -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

View File

@ -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

View File

@ -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++) {

View File

@ -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);

View File

@ -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 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));
}

View File

@ -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.

View File

@ -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 != !?

View File

@ -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)) {

View File

@ -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_;
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<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

View File

@ -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

View File

@ -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