Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Describe __cxa_thread_atexit. #136

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

rjmccall
Copy link
Collaborator

This is my effort to integrate the following mailing-list discussion from 2014:
http://itanium-cxx-abi.github.io/cxx-abi/cxx-abi-dev/archives/2014-May/002726.html

I've updated the wording per my personal editorial preferences, but I've also tried to be a little more precise.

Fixes #135.

This is my effort to integrate the following mailing-list
discussion from 2014:
  http://itanium-cxx-abi.github.io/cxx-abi/cxx-abi-dev/archives/2014-May/002726.html

I've updated the wording per my personal editorial preferences,
but I've also tried to be a little more precise.

<p>
The registration function is not called from within the constructor.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this sentence (and an analogous sentence from the proposed thread-local section) because, as far as I can tell, they're either wrong or not really something that should be specified in the ABI:

  • This certainly can't be talking about a class constructor.
  • If this is talking about a global constructor function, surely the registration function is called from the global constructor, because I can't imagine what else would call it.
  • If this is talking about a period of execution called "the constructor", well, that's not defined by the standard, and it's unclear what it would mean for the registration call to not be a part of it.
  • The registration call needs to be part of the __cxa_guard region. If you finish the initialization, end the guard, and then do the the registration (knowing that you just did the initialization), you lose the interthread ordering that the standard requires: the registration can race with a registration made by some other thread that has observed that the initialization is complete.

@rjmccall rjmccall mentioned this pull request Jan 30, 2022
(It does not matter what address,
as long as they are different in different DSOs.)
It should also include a call to the following function in the FINI
(It does not matter what address, as long as they are different in

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the GNU implementation, it must be an address within the object defining that hidden symbol (not an absolute address). I suggest to drop the remark in parentheses, it's confusing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the definition of __dso_handle is arguably outside of the ABI's scope; I'll think about how to reword this.

No user interface to <code>__cxa_atexit</code> is supported,
so the user is not able to register an <code>atexit</code> function
with a parameter or a home DSO.
The runtime library will call <code>f(p)</code> if the

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires memory allocation, but there is no way to report memory allocation failure. Thread creation is probably a way more common occurrence than global initialization (despite dlopen), so fixing this for __cxa_thread_atexit seems more important than for __cxa_atexit.

One way to fix this is to have an out-of-spec mechanism by which the link editor communicates the maximum number of objects needing such registration to the run-time library. (With ELF, it would probably not involve symbols, but some other mechanis,.)

Copy link
Collaborator Author

@rjmccall rjmccall Jan 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the ABI would be better if the allocation for this wasn't dynamic. A simpler solution than adding a new kind of runtime data collection would be for the compiler to allocate some fixed amount of additional global/thread-local memory for global/thread-local objects that need destruction, the same way that it allocates additional memory for a guard variable. For thread-locals, this memory would be wasted if it was eagerly allocated and the object was never touched on the thread, but so would the memory for the object itself, and overall that seems like an acceptable cost to me.

However, __cxa_thread_atexit is not actually new ABI: it's been implemented for years, and the proposal just never made it into the document, for various not-very-good reasons. So any new proposal here would have to be a v2 recommendation rather than an actual change to __cxa_thread_atexit.

__cxa_thread_atexit does in fact have a return value that it can use to report failure. Neither GCC nor Clang seems to actually check it; I don't know if any compilers do. Frankly, I'm not sure we're really allowed to fail once the initializer has successfully terminated; it would have to be by throwing an exception, and there's no provision for doing so. That's probably the strongest argument for changing the ABI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

__cxa_thread_atexit
2 participants