• Does static initialization of a local variable always happens synch

    From David Brown@21:1/5 to Alf P. Steinbach on Sat Sep 11 20:03:55 2021
    On 11/09/2021 17:09, Alf P. Steinbach wrote:
    On 11 Sep 2021 16:17, Bonita Montero wrote:
    Am 11.09.2021 um 13:47 schrieb Alf P. Steinbach:
    On 11 Sep 2021 11:06, Bonita Montero wrote:

    Yes.

    C++ 17 §9.7/4 (next to last sentence applies):
    ❝Dynamic initialization of a block-scope variable with static storage
    duration or thread storage duration is performed the first time
    control passes through its declaration; such a variable is considered
    initialized upon the completion of its initialization. If the
    initialization exits by throwing an exception, the initialization is
    not complete, so it will be tried again the next time control enters
    the declaration. If control enters the declaration concurrently while
    the variable is being initialized, the concurrent execution shall
    wait for completion of the initialization. If control re-enters the
    declaration recursively while the variable is being initialized, the
    behavior is undefined.❞

    It couldn't be really undefined since the flag which remembers that
    the object has been created successfully is set afterwards. Otherwise
    it woud be impossible that the object will be tried to be re-created
    if the constructor throws an exception and the function containing
    the object will be called again. So an infinite recursion is the only
    possible behaviour here.

    The standard says it's undefined behavior, hence it's undefined
    behavior. Really. So I believe that you meant to say that there can't be
    any other actual effect of this UB than infinite recursion.

    But of course there can be: formal UB means that the compiler is free to insert checking that does whatever the compiler writers want.


    It is quite easy to see how you could get a deadlock from recursive use,
    but not concurrent use. A typical initialisation sequence could be
    roughly :

    if (!initialised_flag) { // atomic flag
    get_lock();
    if (!initialised_flag) {
    do_initialisation();
    initialised_flag = true;
    }
    release_lock();
    }

    Calling this concurrently when the lock is taken, the second thread will
    block on "get_lock()" until the first thread is finished, then see the initialisation was done and go on its way. But calling it recursively,
    the block at "get_lock()" will never finish because it is the same
    thread that was supposed to be doing the initialisation.

    On the other hand, the sequence could be:

    if (!initialised_flag) {
    initialise_temporary_object();
    compare_and_swap(static_object, temporary_object, null);
    initialised_flag = true;
    }

    That would be fine if there is no problem in accidentally creating extra objects and discarding them. The compare and swap would avoid
    overriding a previous initialisation. This would work fine with both concurrent and recursive use.


    So the standard says the behaviour is undefined on recursive use,
    leaving the implementation freedom to pick either tactic (or other tactics).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bonita Montero@21:1/5 to All on Sat Sep 11 21:19:12 2021
    Something I wondered about is whether it is possible to get a C function pointer from a local static lambda _with_ captures like this:

    #include <iostream>

    int main()
    {
    int i;
    static
    auto lambda = [&i]()
    {
    ++i;
    };
    void (*pLambda)() = lambda;
    }

    In theory this should be possible since the lambda doesn't need
    its own context-pointer (pseudo-"this") since the capture-context
    is global storage. This seems something the designers of C++ didn't
    consider, although it is of little use.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Alf P. Steinbach@21:1/5 to Bonita Montero on Sat Sep 11 22:05:05 2021
    On 11 Sep 2021 21:19, Bonita Montero wrote:
    Something I wondered about is whether it is possible to get a C function pointer from a local static lambda _with_ captures like this:

    #include <iostream>

    int main()
    {
        int i;
        static
        auto lambda = [&i]()
        {
            ++i;
        };
        void (*pLambda)() = lambda;
    }

    This particular example — the lines of code up to the hypothetical declaration of `pLambda` — is flawed because if you put this code in any function other than `main` it will then use a dangling reference on
    second call of that function.

    ---

    In theory this should be possible since the lambda doesn't need
    its own context-pointer (pseudo-"this") since the capture-context
    is global storage.

    If you make `i` static then you have something that a smart enough
    compiler can rewrite to a capture-less lambda. Don't know if they do
    though. And conversion to C function pointer would need to be a language extension.


    ---

    This seems something the designers of C++ didn't
    consider, although it is of little use.

    You can get a freestanding function that references any C++ object
    simply by storing a reference or pointer to (a copy of) that object
    indirectly or directly in a static variable that the function uses.

    For example, the type of the static variable can be `std::function<void()>`.

    And that scheme can be elaborated to provide a function that you can use
    like `as_c_callback( [&]{ ++i; } )`. To make that safe against multiple overlapping uses of `as_c_callback` let the static variable be a
    collection, e.g. with th
  • From Bonita Montero@21:1/5 to All on Sun Sep 12 08:24:11 2021
    Am 11.09.2021 um 22:05 schrieb Alf P. Steinbach:
    On 11 Sep 2021 21:19, Bonita Montero wrote:
    Something I wondered about is whether it is possible to get a C function
    pointer from a local static lambda _with_ captures like this:

    #include <iostream>

    int main()
    {
         int i;
         static
         auto lambda = [&i]()
         {
             ++i;
         };
         void (*pLambda)() = lambda;
    }

    This particular example — the lines of code up to the hypothetical declaration of `pLambda` — is flawed because if you put this code in
    any function other than `main` it will then use a dangling reference
    on second call of that function.

    Of course there could be a dangling reference, but that's not what I'm discussing.


    ---

    In theory this should be possible since the lambda doesn't need
    its own context-pointer (pseudo-"this") since the capture-context
    is global storage.

    If you make `i` static then you have something that a smart enough
    compiler can rewrite to a capture-less lambda. Don't know if they do
    though. And conversion to C function pointer would need to be a language extension.

    Of course I could make i static, but that's not what I'm talking about.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)