Page MenuHomeFreeBSD

libc: Remove redundant code in thread atexit code
Needs ReviewPublic

Authored by aokblast on Mar 22 2026, 5:07 AM.
Tags
None
Referenced Files
F153535975: D56022.id174101.diff
Tue, Apr 21, 5:52 PM
Unknown Object (File)
Sun, Apr 19, 10:26 AM
Unknown Object (File)
Sat, Apr 18, 3:46 PM
Unknown Object (File)
Sat, Apr 18, 3:11 PM
Unknown Object (File)
Sat, Apr 18, 2:01 PM
Unknown Object (File)
Sat, Apr 18, 1:54 PM
Unknown Object (File)
Sat, Apr 18, 9:27 AM
Unknown Object (File)
Sat, Apr 18, 3:44 AM
Subscribers

Details

Reviewers
kib
markj

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Passed
Unit
No Test Coverage
Build Status
Buildable 72016
Build 68899: arc lint + arc unit

Event Timeline

I am on edge of proposing to count the number of the list members, and still do the walk_cb_nocall if the number of members does not go to zero after e.g. CXA_DTORS_ITERATIONS times initial list length for dtor removals.

BTW, otherwise walk_cb_nocall() is not needed after your patch.

This revision is now accepted and ready to land.Mar 22 2026, 9:22 PM
In D56022#1281611, @kib wrote:

I am on edge of proposing to count the number of the list members, and still do the walk_cb_nocall if the number of members does not go to zero after e.g. CXA_DTORS_ITERATIONS times initial list length for dtor removals.

Do you mean something like

static void                                                                                                                                                                                        
cxa_thread_walk(void (*cb)(struct cxa_thread_dtor *))                                                                                                                                              
{                                                                                                                                                                                                  
    struct cxa_thread_dtor *dtor;                                                                                                                                                                  
    int cnt = 0;                                                                                                                                                                                   
                                                                                                                                                                                                   
    while ((dtor = LIST_FIRST(&dtors)) != NULL && cnt < CXA_DTORS_ITERATIONS) {                                                                                                                    
        LIST_REMOVE(dtor, entry);                                                                                                                                                                  
        cb(dtor);                                                                                                                                                                                  
        free(dtor);                                                                                                                                                                                
        ++cnt;                                                                                                                                                                                     
    }                                                                                                                                                                                              
}

If so, what is the proper value of CXA_DTORS_ITERATIONS do you think? We cannot just allow a thread to register 4 thread local variable.

And also, should we care about somethink like

#include <iostream>

template <unsigned int T>
struct Foo {
    ~Foo() {
        thread_local Foo<T - 1> val;
        std::cout << T << std::endl;
    }
};

template<>
Foo<0>::~Foo() {
}

int main() {
    Foo<100000> f;
}

, which calls dtor many times but has its end.

No. Perhaps it is simpler to show code than to try to explain it, See D56053.

The example you provided would not work with my patch, it requires increasing the CXA_DTORS_ITERATIONS. Not sure if we want this.

In D56022#1281980, @kib wrote:

No. Perhaps it is simpler to show code than to try to explain it, See D56053.

The example you provided would not work with my patch, it requires increasing the CXA_DTORS_ITERATIONS. Not sure if we want this.

If it breaks the valid C++ code, we should rethink about it. The T value can be somehow greater than CXA_DTORS_ITERATIONS if the user wants.

In D56022#1281980, @kib wrote:

No. Perhaps it is simpler to show code than to try to explain it, See D56053.

The example you provided would not work with my patch, it requires increasing the CXA_DTORS_ITERATIONS. Not sure if we want this.

If it breaks the valid C++ code, we should rethink about it. The T value can be somehow greater than CXA_DTORS_ITERATIONS if the user wants.

I do not disagree but there are also some robustness requirements as part of the implementation quality. So I am unsire, this is why I initially said that 'I am on edge' proposing this. Hanging in the system state (thread exiting) due to user bug is not robust.

That said, your patch should remove walk_cb_nocall(). And perhaps cxa_thread_walk() could drop the argument, simply inlining walk_cb_call.

This revision now requires review to proceed.Tue, Apr 7, 2:31 PM
In D56022#1282431, @kib wrote:
In D56022#1281980, @kib wrote:

No. Perhaps it is simpler to show code than to try to explain it, See D56053.

The example you provided would not work with my patch, it requires increasing the CXA_DTORS_ITERATIONS. Not sure if we want this.

If it breaks the valid C++ code, we should rethink about it. The T value can be somehow greater than CXA_DTORS_ITERATIONS if the user wants.

I do not disagree but there are also some robustness requirements as part of the implementation quality. So I am unsire, this is why I initially said that 'I am on edge' proposing this. Hanging in the system state (thread exiting) due to user bug is not robust.

That said, your patch should remove walk_cb_nocall(). And perhaps cxa_thread_walk() could drop the argument, simply inlining walk_cb_call.

I see, I think we can leave this patch in here before we get better conclusion. I run the test code on a Linux machine and it died immediately if we have infinity loop in thread local destructor. It seems that glibc also don't handle that case.

In D56022#1282431, @kib wrote:
In D56022#1281980, @kib wrote:

No. Perhaps it is simpler to show code than to try to explain it, See D56053.

The example you provided would not work with my patch, it requires increasing the CXA_DTORS_ITERATIONS. Not sure if we want this.

If it breaks the valid C++ code, we should rethink about it. The T value can be somehow greater than CXA_DTORS_ITERATIONS if the user wants.

I do not disagree but there are also some robustness requirements as part of the implementation quality. So I am unsire, this is why I initially said that 'I am on edge' proposing this. Hanging in the system state (thread exiting) due to user bug is not robust.

That said, your patch should remove walk_cb_nocall(). And perhaps cxa_thread_walk() could drop the argument, simply inlining walk_cb_call.

I see, I think we can leave this patch in here before we get better conclusion. I run the test code on a Linux machine and it died immediately if we have infinity loop in thread local destructor. It seems that glibc also don't handle that case.

I would say that they have the same attitude, the system state should not loop infinitely.

Also, note that plain 'destructor that just constructs thread-local object' arguably violates the standard. Only so called 'trivial loops' are allowed to loop forever without side-effects. If you add something that has visible effects, for instance printf() as in your example, then it is allowed, I believe.