Skip to content

Conversation

gavinandresen
Copy link
Contributor

Fixes #1832

// allocate mutexDebugLog on the heap the first time this routine
// is called to avoid crashes during shutdown.
static boost::mutex* mutexDebugLog = NULL;
if (mutexDebugLog == NULL) mutexDebugLog = new boost::mutex();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if OutputDebugStringF is being called twice simultaneously? This doesn't solve anything, unless you have a guaranteed call of this function while still in single-thread modus. That's almost certainly the case, but if it is, why not make it obvious, and have an InitLogging() function, called in init?

I'm nitpicking. ACK.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sipa I also proposed that in #1832. I'm for explicitly constructing and destructing the global objects, such as logging and db, enforced by assertions. This makes thiings more predictable and maintainable than armoring against all possible non-determinism (which is pretty much impossible, you will forget something).

Then again, it's good to have a fix in for 0.7.1. ACK.

@BitcoinPullTester
Copy link

Automatic sanity-testing: PASSED, see http://jenkins.bluematt.me/pull-tester/cac6b389d101999d98c3137b17812cce062f924d for binaries and test log.

@jgarzik
Copy link
Contributor

jgarzik commented Oct 8, 2012

Yeah, it just seems terribly odd and possibly error-prone to allocate a lock... in a racy fashion.

Just make the lock global and put it early in main, to make sure it is instantiated/initialized before anything else in the program.

As it stands now, either this your change or without, the first-use occurs very late in the program, and that seems like a root cause (or at least contributing factor).

@gavinandresen
Copy link
Contributor Author

@jgarzik making it global and putting it early in main won't fix the problem; the order of global destructors is undefined in C++.

As long as there is a printf/OutputDebugStringF before we start creating threads (and there is, early in AppInit2()) there is no race.

Reworking logging should be done... someday... For now, I think this little change is the right thing to do.

@jgarzik
Copy link
Contributor

jgarzik commented Oct 8, 2012

gcc has long followed the now defined C++0x order described here: http://cpp0x.centaur.ath.cx/basic.start.term.html

"If the completion of the constructor or dynamic initialization of an object with static storage duration is sequenced before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first. If an object is initialized statically, the object is destroyed in the same order as if the object was dynamically initialized"

@laanwj
Copy link
Member

laanwj commented Oct 9, 2012

I already acked this for 0.7.1 (assuming we want this out of the door soon), but it does need more thinking.

If you make the pointer global (i.s.o static) and explicitly initialize it in an InitLogging() (called directly at the beginning of AppInit2) function you avoid the destructor trouble, plus you don't make the safety of initialization dependent on who-calls-first. Then add an assert() to printf that the lock is allocated, just in case. Another plus is that you can use the same lock in #ifdef WIN32 if(fPrintToDebugger), where the issue still exists.

After all, printf does have some prerequisite requirements: it needs the arguments to be parsed and DataDir to be set correctly, which is only guaranteed when entering AppInit2. What if someone accidentally adds a printf before this is done? (ie, in GUI initialization, after all a printf looks reaaallly harmless):

  • It logs to the wrong datadir (the default one). Not that big of an issue for a few log messages to end up in the wrong log. However,
  • It caches this datadir in cachedPath, and will return it every time GetDataDir is called. Uh oh!

An explicit InitLogging() call would solve this, and trap all accidental calls to printf before everything is in order.

Talking of GetDataDir, it has the same issue. Though we jump the shark as it will never enter the lock when the path is cached, which you expect by the time it reaches destructors :-) But I can see another subtle initialization race, I think.

@gavinandresen
Copy link
Contributor Author

Ok. I pinky-swear promise I'll rewrite this The Right Way for 0.8.

@gavinandresen gavinandresen merged commit cac6b38 into bitcoin:master Oct 9, 2012
@sipa
Copy link
Member

sipa commented Oct 10, 2012

Maybe The Right Way means using http://www.boost.org/doc/libs/1_32_0/doc/html/call_once.html ?

KolbyML pushed a commit to KolbyML/bitcoin that referenced this pull request Dec 5, 2020
1947519 [Cleanup] Remove unneeded "fix" for segfault with getinfo on startup (random-zebra)
84acfa1 [RPC] Throw immediately if RPC is used when the server is in warmup (random-zebra)

Pull request description:

  **Problem**:
  Most RPC commands crash the wallet during the initialization phase.

  **Solution**:
  An attempt to fix this specifically for `getinfo` was made in bitcoin#543, checking the existence of `chainActive.Tip()`.
  A better solution, instead of checking all places that try to access the block index, (or  the chainstate, or the second layer maps, etc...), is to throw a JSON-RPC exception with any call, if the server is still in warm-up phase (returning the exact init message), directly from `CRPCTable::execute`.

  Closes bitcoin#1908

ACKs for top commit:
  furszy:
    ACK 1947519
  Fuzzbawls:
    utACK 1947519

Tree-SHA512: 4f37e512cb411473135c6d1fbb5013bddbcfca62b8a1ed6777481e90f60374480e3617ed1329a10d3cb1e614a2f17a827da2dce1da793b0db3c6b13222cdec19
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Sep 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Crash at shutdown (global destructor order is undefined)
5 participants