jww
jww

Reputation: 102245

Specify construction/destruction order of static locals in different accessors

I'm experiencing a crash in cxa_finalize running a program (this is a program, and not a library within):

$ ./ac-test.exe 
Assertion failed: AcLock.cpp(54): AcLock
libc++abi.dylib: terminate called without an active exception
Abort trap: 6

The assertion/crash is due to the interaction between an object and a logger. The logger is destructed before the object, but the object uses the logger. So the mutex being acquired that pops the assert or crash has already been destroyed (hence the reason pthread_mutex_lock fails when locking the logger).

I've read the GCC manual on Specifying Attributes of Variables and Declaring Attributes of Functions, but I'm obviously missing something.

I placed the object and logger in a common header within accessors and tried to specify a construction order:

// AcGlobals.h
static AcLogger& GetLogger() {

    static AcLogger logger __attribute__(init_priority(50));
    return logger;
}
static AcSocketList& GetAcceptSockets() {

    static AcSocketList sockets __attribute__(init_priority(100));
    return sockets;
}

That resulted in a bunch of errors:

./AcGlobals.h:24:46: error: expected ';' at end of declaration
    static AcLogger logger __attribute__((init_priori...

./AcGlobals.h:24:47: warning: declaration does not declare anything
      [-Wmissing-declarations]
    static AcLogger logger __attribute__((init_priori...

I also tried placing the attribute on the function rather than the variable:

// AcGlobals.h
static AcLogger& GetLogger() __attribute__(init_priority(50)) {

    static AcLogger logger;
    return logger;
}
static AcSocketList& GetAcceptSockets() __attribute__(init_priority(100)) {

    static AcSocketList sockets;
    return sockets;
}

This resulted in more problems:

./AcGlobals.h:22:53: warning: GCC does not allow init_priority attribute in this
      position on a function definition [-Wgcc-compat]
static AcLogger& GetLogger() __attribute__((init_priority(50))) {
                                              ^
./AcGlobals.h:22:53: error: can only use 'init_priority' attribute on file-scope
      definitions of objects of class type

I also tried __attribute__((constructor(50))) instead of init_priority with no joy.

Note: I'm working on an Apple machine. Apple has a 'feature' where constructor priority only applies to adorned functions and variables within the same file. So these cannot be spread out across translation units.

How, precisely, do I specify the order of construction and destruction of local static objects?

Upvotes: 2

Views: 1296

Answers (2)

James Kanze
James Kanze

Reputation: 153909

This is a classical problem when using the Meyers singleton (which is basically what you're doing). The solution is to not destruct the singleton; instead of a static local variable, you should use dynamic allocation without a delete:

static  AcLogger& GetLogger()
{
    static AcLogger* logger = new AcLogger;
    return *logger;
}

Note that in this case, you will have to ensure that each use of the logger flushes (but this is usually the case anyway); otherwise, you may end up with unflushed data.

With regards to your attempt to use an extended feature of your compiler: I'm not too familiar with it, but I don't see how you can use something called init_priority on a local variable. The construction (and destruction) time of local static variables is defined by the language (and in this case, the destruction time is not what you want). If you want to use this non-standard extension, you'll probably have to make the instance variable a static class member, or maybe even a global (in which case, you can't make the constructor private).

Upvotes: 4

Corbin
Corbin

Reputation: 33447

Assuming the dependency is non-cyclical, you can just leverage the standard behavior of initialization in the order of code flow entry into the function and destruction in reverse order of initialization.

In other words, have a call to GetLogger() to initialize the logger and then GetAcceptSockets() to initialize the list. This will result in the socket list getting destructed first (while the logger still exists) and then the logger getting destructed last.

Upvotes: 1

Related Questions