I suppose that in the future, something like that could be extended so that a
simple static-time logger that logs to stderr could be replaced with a
non-static logger once the system is up, but that seems like a lot of
complexity for something that I think isn't a good idea to do in the first
place.
Bruce
On Sep 3, 2010, at 7:29 PM, David Reiss wrote:
> I'd be fine with this change, but I also think that it would be a good idea
> for Thrift to switch to a real logging library at some point. When that time
> comes, I don't want the choice to be limited to libraries that can safely be
> used during static initialization. Seriously, "don't do that."
>
> --David
>
> On 09/03/2010 04:46 PM, Rush Manbert wrote:
>> I'm hoping the hard core C++ guys will think about this a little and maybe
>> have some suggestions. It's an esoteric enough problem that you might find
>> it interesting. :-)
>>
>> For complicated reasons, we sometimes end up trying to open a Thrift socket
>> connection to a local server process during client-side static init time. I
>> know that the simple answer would be "don't do that", but that's really not
>> possible.
>>
>> This has worked just fine as long as the server process is running. If it is
>> not running, however, the client app has a high probability of crashing when
>> the socket connection attempt fails. This is because when such an error
>> occurs, the output is handled by calling GlobalOutput.perror, like so:
>>
>> GlobalOutput.perror("TSocket::open() socket() " + getSocketInfo(),
>> errno_copy);
>>
>> The problem is that GlobalOutput is a global object that is constructed at
>> static init time. Since we can't control the order in which global objects
>> are initialized, we may try to communicate before GlobalOutput has been
>> constructed. Thus the crash. (The situation is analogous to a global object
>> trying to connect to the server in the object's constructor. Depending on
>> the order of static init execution, GlobalOutput may or may not have been
>> constructed when the connection attempt is made.)
>>
>> The GlobalOutput object is instantiated in Thrift.cpp with this line of code:
>>
>> TOutput GlobalOutput;
>>
>> The easy way to fix this problem is to wrap the global object in a
>> "construct on first use" function
>> (http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.15) like this:
>>
>> TOutput &GlobalOutput (void)
>> {
>> static TOutput GlobalOutput;
>> return GlobalOutput;
>> }
>>
>> which guarantees that it gets constructed the first time GlobalOuput() is
>> called, but then I will need to change every existing reference to
>> GlobalOutput to a function call to GlobalOutput(). So, for instance, the
>> call to perror that I pasted above would change to:
>>
>> GlobalOutput().perror("TSocket::open() socket() " + getSocketInfo(),
>> errno_copy);
>>
>>
>> As usual, I have a list of questions (but it's short):
>>
>> 1) Can anyone think of another way around this problem?
>>
>> 2) Would the C++ library developers be at all receptive to taking the change
>> I have outlined (GlobalOutput -> GlobalOutput()) into the trunk? On one
>> hand, it adds a function call. On the other hand the operation in progress
>> is formatted output, so one extra function call to get the reference
>> probably doesn't matter. On yet the other hand, the change would have to
>> extend to the server side code as well, and probably you guys running large
>> server side operations worry about error performance more that I worry about
>> client side performance in the same situation.
>>
>> TIA,
>> Rush