Attached is a patch which changes Mutex to handle the initialization ordering problem where Lock can be called before the constructor is called.
On Wed, Apr 15, 2009 at 1:41 PM, Wink Saville <w...@google.com> wrote: > Fair enough on the Mutex, I'll try to get a new patch to you soon, > but if you get there first, great. > > As for the explicit initialization I humbly disagree but I understand > your point completely. Maybe someday there will be another > initialization issue that isn't as easily solved and it can be revisited. > > -- Wink > > > > On Wed, Apr 15, 2009 at 12:25 PM, Kenton Varda <ken...@google.com> wrote: > >> On Tue, Apr 14, 2009 at 11:07 AM, Wink Saville <w...@google.com> wrote: >> >>> Feels strange to be using the object before it's constructed. Also, every >>> Mutex has the cost of the test. >> >> >> A predictable branch is basically free, especially compared to the cost of >> the atomic ops needed to actually lock a mutex. >> >> >>> If you want to go this way what about >>> a "StaticMutex" which was implemented this way and leave the Mutex >>> alone. >> >> >> The goal here is to have the same behavior as the Mutex class in Google's >> internal codebase, so having a separate class doesn't seem like a good idea. >> >> >>> I'm still in favor of an explicit initialization as a long term solution >>> where >>> all ordering issues are controlled. Your thoughts on this? >> >> >> To me it sounds like a lot of work without very much benefit. >> >> >>> >>> >>> >>> On Tue, Apr 14, 2009 at 10:29 AM, Kenton Varda <ken...@google.com>wrote: >>> >>>> I think you could just do something like: >>>> enum LinkerInitialized { LINKER_INITIALIZED }; >>>> >>>> // Mutex allocated in static memory which may be locked before the >>>> // constructor is actually called. We can take advantage of the fact >>>> // that static memory is always initialized to zero. >>>> Mutex::Mutex(LinkerInitialized) { >>>> if (impl_ == NULL) { >>>> Init(); >>>> } >>>> } >>>> >>>> Mutex::Lock() { >>>> if (impl_ == NULL) { >>>> // We are a linker-initialized mutex and we're being locked >>>> // before the constructor has been called. There should be >>>> // no threads running yet, so we can just initialize now. >>>> Init(); >>>> } >>>> } >>>> >>>> On Mon, Apr 13, 2009 at 10:12 PM, Wink Saville <w...@google.com> wrote: >>>> >>>>> I did some searching and my initial reaction is that this requires some >>>>> initialization >>>>> of the mutex at load time which may not be possible in windows. But if >>>>> you have >>>>> a technique in mind let me know. >>>>> >>>>> I wonder if another option might be better in the long term. And that >>>>> is to have an >>>>> initialization class that is statically allocated which does all the >>>>> initialization. >>>>> This would allow the initialization order to be well defined within the >>>>> protobuf >>>>> module. Also this would make protobuf more amenable to embedded systems >>>>> where reloads my not occur and an explicit initialization call would be >>>>> needed. >>>>> >>>>> I'd be glad to do a first pass at coding this if people would be >>>>> interested. >>>>> >>>>> -- Wink >>>>> >>>>> >>>>> On Mon, Apr 13, 2009 at 5:26 PM, Kenton Varda <ken...@google.com>wrote: >>>>> >>>>>> BTW, probably the ideal approach would be to make the Mutex class >>>>>> itself immune to initialization ordering problems. That is, if you >>>>>> allocate >>>>>> a Mutex statically, it shouldn't matter if its constructor has been run >>>>>> yet >>>>>> when you try to lock it. (The Mutex class in our internal codebase works >>>>>> like this but is non-portable, hence the different implementation in the >>>>>> open source release.) If you want to update your patch, go ahead, >>>>>> otherwise >>>>>> I'll take care of implementing this when it gets to the top of the queue. >>>>>> :) >>>>>> >>>>>> On Mon, Apr 13, 2009 at 5:21 PM, Kenton Varda <ken...@google.com>wrote: >>>>>> >>>>>>> My backlog of patches and other work is very long, unfortunately. >>>>>>> But I hope to spend some time reducing it this week, hopefully getting >>>>>>> to >>>>>>> this. >>>>>>> >>>>>>> >>>>>>> On Sat, Apr 11, 2009 at 12:13 PM, Wink Saville <w...@google.com>wrote: >>>>>>> >>>>>>>> Has anyone been able to look at this, do I need to make some >>>>>>>> changes or do anything else? >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> On Wed, Apr 8, 2009 at 2:59 PM, Kenton Varda <ken...@google.com>wrote: >>>>>>>> >>>>>>>>> Although I see the decsriptor.cc part isn't meant to be >>>>>>>>> submitted... >>>>>>>>> >>>>>>>>> >>>>>>>>> On Wed, Apr 8, 2009 at 2:59 PM, Kenton Varda <ken...@google.com>wrote: >>>>>>>>> >>>>>>>>>> You can use "svn diff" to produce a single patch file that >>>>>>>>>> contains both parts of this change. >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> On Tue, Apr 7, 2009 at 10:01 PM, Wink Saville <w...@google.com>wrote: >>>>>>>>>> >>>>>>>>>>> There is a bug in the initialization of the >>>>>>>>>>> log_silencer_count_mutex_ in common.cc. >>>>>>>>>>> Currently it is initialized because it is a global object, but >>>>>>>>>>> the order of initialization >>>>>>>>>>> of global objects is undefined in c++ and in my environment it >>>>>>>>>>> was not initialized >>>>>>>>>>> when some errors were encountered as I was debugging. >>>>>>>>>>> >>>>>>>>>>> If descriptor.cc.patch is applied without applying >>>>>>>>>>> common.cc.patch the compilation >>>>>>>>>>> fails when the GOOGLE_LOG(ERROR) << "IBGF"; executes because the >>>>>>>>>>> log_silencer_count_mutex_ wasn't yet initialized. It so happens >>>>>>>>>>> that this actually >>>>>>>>>>> fails at build time because the descriptor code is executed >>>>>>>>>>> during compiliation. >>>>>>>>>>> >>>>>>>>>>> If you then apply common.cc.patch the compile completes >>>>>>>>>>> successfully and make >>>>>>>>>>> check also succeeds. >>>>>>>>>>> >>>>>>>>>>> The patch initializes the mutex on first use rather than at >>>>>>>>>>> initialization time thus does not >>>>>>>>>>> suffer the problem of the current scheme. It does make me wonder >>>>>>>>>>> if there >>>>>>>>>>> maybe other latent bugs in the code associated with assumed >>>>>>>>>>> initialization order. >>>>>>>>>>> >>>>>>>>>>> Please review this patch and if additional changes are desired >>>>>>>>>>> I'll be glad >>>>>>>>>>> to work through any changes. >>>>>>>>>>> >>>>>>>>>>> -- Wink >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>> >>>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>> >>>>>> >>>>> >>>> >>> >> > --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Protocol Buffers" group. To post to this group, send email to protobuf@googlegroups.com To unsubscribe from this group, send email to protobuf+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/protobuf?hl=en -~----------~----~----~----~------~----~------~--~---
Index: src/google/protobuf/stubs/common.cc =================================================================== --- src/google/protobuf/stubs/common.cc (revision 105) +++ src/google/protobuf/stubs/common.cc (working copy) @@ -113,7 +113,7 @@ static LogHandler* log_handler_ = &DefaultLogHandler; static int log_silencer_count_ = 0; -static Mutex log_silencer_count_mutex_; +static Mutex log_silencer_count_mutex_(Mutex::LINKER_INITIALIZED); static string SimpleCtoa(char c) { return string(1, c); } @@ -197,6 +197,10 @@ #ifdef _WIN32 struct Mutex::Internal { + Internal() { + InitializeCriticalSection(&mutex); + } + CRITICAL_SECTION mutex; #ifndef NDEBUG // Used only to implement AssertHeld(). @@ -204,17 +208,25 @@ #endif }; -Mutex::Mutex() - : mInternal(new Internal) { - InitializeCriticalSection(&mInternal->mutex); +Mutex::Mutex() { + mInternal = new Internal(); } +Mutex::Mutex(LinkerInitialized) { + if (mInternal == NULL) { + mInternal = new Internal(); + } +} + Mutex::~Mutex() { DeleteCriticalSection(&mInternal->mutex); delete mInternal; } void Mutex::Lock() { + if (mInternal == NULL) { + mInternal = new Internal(); + } EnterCriticalSection(&mInternal->mutex); #ifndef NDEBUG mInternal->thread_id = GetCurrentThreadId(); @@ -237,20 +249,33 @@ #elif defined(HAVE_PTHREAD) struct Mutex::Internal { + Internal() { + pthread_mutex_init(&mutex, NULL); + } + pthread_mutex_t mutex; }; -Mutex::Mutex() - : mInternal(new Internal) { - pthread_mutex_init(&mInternal->mutex, NULL); + +Mutex::Mutex() { + mInternal = new Internal(); } +Mutex::Mutex(LinkerInitialized) { + if (mInternal == NULL) { + mInternal = new Internal(); + } +} + Mutex::~Mutex() { pthread_mutex_destroy(&mInternal->mutex); delete mInternal; } void Mutex::Lock() { + if (mInternal == NULL) { + mInternal = new Internal(); + } int result = pthread_mutex_lock(&mInternal->mutex); if (result != 0) { GOOGLE_LOG(FATAL) << "pthread_mutex_lock: " << strerror(result); Index: src/google/protobuf/stubs/common.h =================================================================== --- src/google/protobuf/stubs/common.h (revision 105) +++ src/google/protobuf/stubs/common.h (working copy) @@ -1006,13 +1006,29 @@ // while holding it, T will deadlock. class LIBPROTOBUF_EXPORT Mutex { public: + enum LinkerInitialized { LINKER_INITIALIZED }; + // Create a Mutex that is not held by anybody. Mutex(); + // Create a Mutex initialized at link time. + // + // This constructor maybe called after the first call to Lock because in + // C++ there is no guarantee of initialization ordering across compiliation + // units. Thus the Lock routine maybe called before this constructor. The + // typical solution is to know that mInteral will be NULL and to initialize + // mInternal in this contrustor and in Lock if it is NULL. Note it is not + // necessary to have any other synchronization mechanism because startup + // initialization is not multi-threaded. + Mutex(LinkerInitialized); + // Destructor ~Mutex(); // Block if necessary until this Mutex is free, then acquire it exclusively. + // + // As explained above the implemenation must handle the situation where this + // is called before the constructor. void Lock(); // Release this Mutex. Caller must hold it exclusively.