Throwing an exception from std::call_once

2019-04-28 11:48发布

问题:

The C++ Standard states the following about the execution of std::call_once with functions that throw exceptions (§30.4.4.2/2):

2/ Effects: An execution of call_once that does not call its func is a passive execution. An execution of call_once that calls its func is an active execution. An active execution shall call INVOKE (DECAY_- COPY ( std::forward(func)), DECAY_COPY (std::forward(args))...). If such a call to func throws an exception the execution is exceptional, otherwise it is returning. An exceptional execution shall propagate the exception to the caller of call_once. Among all executions of call_once for any given once_flag: at most one shall be a returning execution; if there is a returning execution, it shall be the last active execution; and there are passive executions only if there is a returning execution. [ Note: passive executions allow other threads to reliably observe the results produced by the earlier returning execution. — end note ]

I'm using Visual Studio 2012 and running the following code:

void f(){
    throw std::exception( "Catch me!" );
}

int main( int argc, char* argv[] ){
    once_flag flag;
    try{
        call_once( flag, f );
    } catch( const std::exception& e ){
        cout << e.what() << endl;
    }
    return 0;
}

My result is: the code in the catch block runs and prints the message, but when the program exists I get a call to abort() and the following message printed to cout:

...\mutex.c(38) mutex destroyed while busy

Is this supposed to happen?

回答1:

Is this supposed to happen?

No, not really. This is a bug.

However, notice the fact that VC11 is not alone on this:

  • Intel ICC 13.0.1 calls std::terminate() as if your exception was not handled (see live example);
  • GCC 4.8.0 beta probably does something similar, but it does not show any output, it just swallows the exception and silently terminates the program (see live example). [UPDATE: This bug does not seem to be reproducible in other enviroments and is likely to be an issue with the configuration on liveworkspace.org only]

GCC 4.7.2 and Clang 3.2, on the other hand, behave correctly.

By the way, it is worth noticing that the C++ Standard (Paragraph 18.8.1) specifies that std::exception only has a default constructor and a copy constructor. The constructor you are using is most likely a non-portable MS extension.

You may consider using std::logic_error instead, which derives from std::exception and supports a constructor accepting a string.