This is an archive of the discontinued LLVM Phabricator instance.

[libcxxabi] Re-organized inheritance structure to remove CRTP in cxa_guard
ClosedPublic

Authored by DanielMcIntosh-IBM on Dec 8 2021, 11:32 AM.

Details

Summary

Currently, the InitByte... classes inherit from GuardObject so they can
access the base_address, init_byte_address and thread_id_address. Then,
since GuardObject needs to call acquire/release/abort_init_byte, it uses
the curiously recurring template pattern (CRTP). This is rather messy.

Instead, we'll have GuardObject contain an instance of InitByte, and pass it
the addresses it needs in the constructor. GuardObject doesn't need the
addresses anyways, so it makes more sense for InitByte to keep them instead of
GuardObject. Then, GuardObject can call acquire/release/abort as one
of InitByte's member functions.

Organizing things this way not only gets rid of the use of the CRTP, but also
improves separation of concerns a bit since the InitByte classes are no longer
indirectly responsible for things because of their inheritance from
GuardObject. This means we no longer have strange things like calling
InitByteFutex.cxa_guard_acquire, instead we call
GuardObject<InitByteFutex>.cxa_guard_acquire.

This is the 4th of 5 changes to overhaul cxa_guard.
See D108343 for what the final result will be.

Depends on D115367

Diff Detail

Event Timeline

DanielMcIntosh-IBM requested review of this revision.Dec 8 2021, 11:32 AM
DanielMcIntosh-IBM created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptDec 8 2021, 11:32 AM
Herald added a reviewer: Restricted Project. · View Herald Transcript
ldionne requested changes to this revision.Dec 13 2021, 8:24 AM
ldionne added inline comments.
libcxxabi/src/cxa_guard_impl.h
212–215

The duplication of these comments feels weird to me. Instead, I believe we should have a comment like:

//===----------------------------------------------------------------------===//
//                    Initialization Byte Implementations
//===----------------------------------------------------------------------===//
//
// Each initialization byte implementation supports the following method:
//
//    InitByte(uint8_t* _init_byte_address, uint32_t*)
//        Here explain the purpose of these constructors.
//
//    bool acquire();
//        Here document what this method does and you can have the note about ownership documented in exactly one place, here.
//
//    etc...
//

Then, each implementation of an initialization byte can simply implement the required methods and document anything interesting that is specific to that implementation.

282

With this refactor, it now feels really weird to have this GetThreadID != nullptr here. Would it be possible to move the determination of whether the thread id is supported elsewhere (probably in the GuardObject constructor) so that InitByteGlobalMutex only has to check _thread_id_address != nullptr?

Same applies to Futex below.

406–409

Also, can you explain the /*_init_byte_address & ~0x3*/ comment?

This revision now requires changes to proceed.Dec 13 2021, 8:24 AM

Rebase, and address review comments

DanielMcIntosh-IBM marked an inline comment as done.Dec 14 2021, 11:13 AM
DanielMcIntosh-IBM added inline comments.
libcxxabi/src/cxa_guard_impl.h
282

It's possible, but without any further changes, InitByteGlobalMutex and InitByteFutex would still need the GetThreadID template parameter because of their current_thread_id member. So both the GuardObject and InitByte... classes would each need it as a template parameter, meaning we would have something like this:

GuardObject<InitByteGlobalMutex<LibcppMutex, LibcppCondVar, GlobalStatic<LibcppMutex>::instance,
                                               GlobalStatic<LibcppCondVar>::instance, PlatformThreadID>, PlatformThreadID>;

And for NoThreads we'd still have to pass PlatformThreadID as a template argument to GuardObject:

GuardObject<InitByteNoThreads, PlatformThreadID>;

That doesn't seem to me like much of an improvement to me, but you probably have a better idea of what is easiest to understand for someone who hasn't been looking at this for months.

I could try to avoid this by pulling out more than just has_thread_id_support, and maybe even going as far as to create a ThreadIdWord class or something. Unfortunately, while both GlobalMutex and Futex use LazyValue<uint32_t, GetThreadID> for fetching the current thread ID, after that they start to differ ever so slightly. Futex uses AtomicInt<uint32_t> thread_id while GlobalMutex reads and writes to it directly. There's also the issue of who should construct & who should own the ThreadIdWord instance - GuardObject is in charge of diving up the raw guard object, but InitByte... is the only one who interacts with it and (for GlobalMutex) all interaction with it needs to happen while we hold the lock.

All of which is to say, yes, it is possible, but I don't think it would make things better.

406–409

The goal here is not to compute the address of the raw guard object, but rather to compute the argument we need to pass to Wait() - a pointer to the word that includes _init_byte_address - i.e. _init_byte_address & ~0x3. However bitwise arithmetic is prohibited on pointers, so _init_byte_address & ~0x3 is an illegal expression.

We could either cast to an arithmetic type such as uintptr_t and back again, or go the more direct route and use what we know about the layout of the raw guard object (and perform the exact inverse of what we do in GuardObject to compute _init_byte_address). I believe I went the direct route because using casts would have technically been UB, even if it is all but guaranteed to be safe (though I'm not too sure anymore since it has been a long time since I made the decision). Unfortunately, during the internal review downstream, the direct approach led to some confusion, so I added a short comment to hopefully clarify the intentions a bit.

DanielMcIntosh-IBM marked an inline comment as not done.Dec 14 2021, 11:16 AM
DanielMcIntosh-IBM added inline comments.
libcxxabi/src/cxa_guard_impl.h
212–215

I left very small summary still, just for tools which recognize documentation comments to pick up.

DanielMcIntosh-IBM edited the summary of this revision. (Show Details)Jan 10 2022, 2:18 PM
ldionne accepted this revision.Jan 10 2022, 2:29 PM

Please don't mark this as being a NFC in the commit message. This is a refactoring, and we don't expect it to change functionality, but we generally tag things like "remove unused include" or "fix comment" as NFCs. If there happens to be a bug in there, it would be bad to skip it from the commit list when searching for potential regressions because this was marked as a NFC.

Apart from that, thanks for your patience going through the review and for improving the documentation like this. LGTM with a few comments.

libcxxabi/src/cxa_guard_impl.h
209
211
406–409

Got it, thanks for the explanation. Please consider reformatting as I suggested, though.

This revision is now accepted and ready to land.Jan 10 2022, 2:29 PM
DanielMcIntosh-IBM retitled this revision from [NFC][libcxxabi] Re-organized inheritance structure to remove CRTP in cxa_guard to [libcxxabi] Re-organized inheritance structure to remove CRTP in cxa_guard.Jan 12 2022, 11:18 AM

Address review comments

DanielMcIntosh-IBM marked 5 inline comments as done.Jan 12 2022, 12:07 PM