Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
120 views
in Technique[技术] by (71.8m points)

c++ - Singletons that outlive main

The "magic static" singleton approach is generally quite effective:

T& instance() {
    static T inst;
    return inst;
}

In a threadsafe way, it creates the T on first call and that object lives until program shutdown. However, if this is a logging object, and if there are background threads that outlast main, this can break down. I see some mention of the problem, but not of solutions:

Is this a viable approach?:

/*static*/ T& T::instance() {
    static auto inst = std::make_shared<T>();
    static thread_local auto threadInst = inst; // Hope this call gets hit before `inst` goes out of scope.
    return *threadInst;
}

As I understand it, so long as each thread calls T::instance() before main exits, that thread will correctly set up threadInst which will keep the object alive as long as the calling thread is running.

Questions:

  1. Is that correct? So long as T::instance() is first called by each thread after main() starts and before main() finishes, will the above avoid UB?
  2. Is there a way to loosen that constraint? I don't know that there is. In particular, I can't think of a way for the destruction of inst to communicate that with other threads.
question from:https://stackoverflow.com/questions/65907680/singletons-that-outlive-main

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The reason the other questions don't provide solutions is that it impossible to do usefully in most circumstances. Allowing threads to live past main is difficult to write and maintain. Some of the rules are:

[basic.start.term/4] If a function contains a block variable of static or thread storage duration that has been destroyed and the function is called during the destruction of an object with static or thread storage duration, the program has undefined behavior if the flow of control passes through the definition of the previously destroyed block variable.

So, if you cannot guarantee that you execute before static storage destruction, you cannot use a function-local static.

[basic.start.term/6] If there is a use of a standard library object or function not permitted within signal handlers that does not happen before completion of destruction of objects with static storage duration and execution of std?::?atexit registered functions, the program has undefined behavior.

So, if you cannot guarantee that you execute before static storage destruction, you also cannot use most of the standard library.

[support.signal/3.1] [Note 1: This implicitly excludes the use of new and delete expressions that rely on a library-provided memory allocator. — end note]

So you cannot directly or indirectly use a standard new or delete.

Back to your example:

/*static*/ T& T::instance() {
    static auto inst = std::make_shared<T>();
    static thread_local auto threadInst = inst; // Hope this call gets hit before `inst` goes out of scope.
    return *threadInst;
}

Unless you coordinate with the thread, so that this is called and the thread_local is constructed before detach, you have just replaced one race with another. To do something this way robustly, you have to wait on a condition variable for the thread to notify you that the thread_local is initialized, before you detach. However, if you do this, it will be simpler usually to pass the pointer to the thread directly. Also, shared_ptr is not allowed in a signal handler, so it is not a type you can use for this purpose.

So, here would be my guidelines:

  • T or anything it uses or calls that isn't guaranteed to happen before static storage destruction cannot use anything not signal-safe, including most of the standard library.
  • Pass the pointer or reference to T to the thread. It cannot be accessed through a function-local static or a global static variable. It must be kept as thread_local or automatic in the thread in question.
  • Leak it. You cannot destroy it, because delete is not allowed in this context.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...