RFR: 8067790: Better support for native implementations of Atomic::cmpxchg

David Holmes david.holmes at oracle.com
Wed Jan 7 04:58:40 UTC 2015

I'm inclined to agree with Coleen - while I originally lamented the use 
of platform specific ifdefs and wondered about using simple inheritance, 
this has gone off in a direction I never envisaged. I don't think the 
problem being attacked is sufficiently bad to warrant the complexity** 
of the solution. And it still adds a bunch of platform-specific ifdefs. :(



** that is subjective of course - if you don't understand template 
meta-programming then of course it seems complex. I don't know if it 
remains complex if you do understand template meta-programming ?

On 7/01/2015 2:27 PM, Coleen Phillimore wrote:
> Thanks David,  I'm working through this thread in reverse.   Thank you
> for the context.  This helps a lot.
> I am not familiar with template meta-programming either.  A long time
> ago, I was a proponent of using templates but mostly as a type-checked
> way of avoiding duplicate code.
> In this case of template meta-programming, I don't think the
> intellectual load of the idioms offset the ugliness of the code that
> they are replacing.  Learning these idioms is not trivial.  They are
> really hard to read.  Maybe if template classes had proper names rather
> than X, Y, and Z it would be better.   In any case, using this to
> replace code isn't a clear win in clean code.  There is still the
> duplicate declarations of AtomicPlatform.  There's also the unfortunate
> conditional inclusions in shared code:
> +// Linux
> +#ifdef TARGET_OS_ARCH_linux_x86
> +# include "atomic_linux_x86.hpp"
> +#endif
> +
> +// Solaris
> +#ifdef TARGET_OS_ARCH_solaris_x86
> +# include "atomic_solaris_x86.hpp"
> +#endif
> +
> +// Windows
> +#ifdef TARGET_OS_ARCH_windows_x86
> +# include "atomic_windows_x86.hpp"
> +#endif
> +
> +// BSD
> +#ifdef TARGET_OS_ARCH_bsd_x86
> +# include "atomic_bsd_x86.hpp"
> +#endif
> I don't think this aids maintainability or cleanliness of the code for
> this use case.  I don't think this should be added to the code base at
> this time.
> Thanks,
> Coleen
> On 1/4/15, 11:34 PM, David Holmes wrote:
>> Sorry for the top-post but just wanted to reset the context a little
>> here. Originally [1] I made the comment:
>> "It's fine to have generic shared approaches but there really needs to
>> be a way to allow platform specific "overrides"."
>> For the actual original RFR I said [2]:
>> "Can we pause and give some more thought to a clean mechanism for
>> allowing a shared implementation if desired with the ability to
>> override if desired. I really do not like to see CPU specific ifdefs
>> being added to shared code. (And I would also not like to see all
>> platforms being forced to reimplement this natively).
>> I'm not saying we will find a simple solution, but it would be nice if
>> we could get a few folk to think about it before proceeding with the
>> ifdefs :)"
>> Erik then proposed three alternative approaches [3] and the simple
>> variant was chosen [4] and presented for review. However Paul Hohensee
>> also made comments about an inheritance-based approach and Erik
>> floated the first template-based variant [5] and there was some
>> discussion with Paul and Kim. But we then ended up with the simple
>> solution, leaving an inheritance-based solution for future work [6]
>> (which is what is what is being discussed now).
>> This template-based meta-programming is not something I have any
>> familiarity with. My initial thought is this seems overkill for the
>> situation we have with the Atomic class - but as I said I'm ignorant
>> of the technique being used here.
>> David
>> -----
>> [1]
>> http://mail.openjdk.java.net/pipermail/hotspot-dev/2014-September/015292.html
>> [2]
>> http://mail.openjdk.java.net/pipermail/hotspot-dev/2014-September/015303.html
>> [3]
>> http://mail.openjdk.java.net/pipermail/hotspot-dev/2014-September/015362.html
>> [4]
>> http://mail.openjdk.java.net/pipermail/hotspot-dev/2014-September/015401.html
>> [5]
>> http://mail.openjdk.java.net/pipermail/hotspot-dev/2014-October/015586.html
>> [6]
>> http://mail.openjdk.java.net/pipermail/hotspot-dev/2014-November/015889.html
>> On 2/01/2015 12:07 AM, Erik Österlund wrote:
>>> Hi Kim,
>>>> On 01 Jan 2015, at 07:42, Kim Barrett <kim.barrett at oracle.com> wrote:
>>>> On Dec 31, 2014, at 6:12 AM, Erik Österlund <erik.osterlund at lnu.se>
>>>> wrote:
>>>>>> The rest of these comments only matter if, contrary to my suggestion
>>>>>> above, some additional infrastructure is thought necessary.
>>>>> This is what I originally thought too and hence the current
>>>>> solution. But voices said that using macros for this is, I quote,
>>>>> "that bad".
>>>>>> […]
>>>>>> This scales with the number of AtomicPlatform definitions, rather
>>>>>> than
>>>>>> the number of specialized functions as happens with the existing
>>>>>> code.
>>>>> So your proposal is to still use ugly macros but move it to the
>>>>> class level instead, even though at the moment there is really only
>>>>> one member function that needs it. That feels a bit meh to be
>>>>> honest. IMO if we want to do this right, why go half the way and
>>>>> still rely on ugly macros?
>>>> Please lay off with the perjorative "ugly macros".  The preprocessor
>>>> is part of C++, and isn't going away.  It's a tool that can be used,
>>>> or abused, and I think this is a fine use.  To me, "ugly" is adding
>>>> 800+ lines of code / tests / comments to eliminate a dozen lines of
>>>> flag macro definitions and associated #ifdef’s.
>>> Well people are entitled to have different opinions of course. As I
>>> already mentioned, I did this because other reviewers (as well as me)
>>> found it ugly to use macros for conditionally specializing, which is
>>> the whole motivation of doing this, and was well understood from the
>>> beginning. But I’m fine with leaving it as macros if this is in
>>> general preferred and opinions have suddenly changed in this matter.
>>> Personally I don’t see how the number of rows of code define
>>> uglyness. Reusability, testing, documentation and well definedness in
>>> contracts/behaviours all lead to more lines of code, but I don’t
>>> think that equals uglyness. So while I disagree more code is uglier
>>> because it’s more, it’s all up to opinion and it’s quite pointless to
>>> discuss; you are entitled to think that is ugly.
>>>> If that additional infrastructure were amortized across more use-cases
>>>> then it might be more appealing.  I might even have some additional
>>>> use-cases for something along that line, though not in its present
>>>> form.  (More on that below.)
>>> Yeah I specifically had in mind the wish to make a split between
>>> commercial extensions to open source code actually. This pattern
>>> allows easy optional class inheritance injection as a general design
>>> pattern to achieve this. And for my own purposes the ability to
>>> define asymmetric dekker synchronization to elide storeload fences
>>> when the platform (e.g. TSO) supports it but otherwise revert to a
>>> generalized implementation (using storeload fences). This is why I
>>> think a clean reusable strategy is almost always better because it is
>>> widely applicable, even if there would not be obvious other examples
>>> where it is useful.
>>>>>> I think the name "IsBaseOfAndDerived" would be better as
>>>>>> "IsBaseAndDerived”.  […]
>>>>>> The current implementation of IsBaseOfAndDerived (and therefore
>>>>>> IsBaseOf) may (silently) provide incorrect answers when called with
>>>>>> incomplete types. […]
>>>>>> The actual usage in this proposed change-set even relies on this
>>>>>> flawed behavior of these metafunctions, since AtomicPlatform isn't
>>>>>> always defined. […]
>>>>> So what you are saying here is that I should rename
>>>>> IsBaseOfAndDerived to IsBaseAndDerived to more closely resemble
>>>>> other external libraries we do not use. But if I do that my
>>>>> behaviour is "incorrect" because it is not identical to that of the
>>>>> external library. And therefore my change would not work if it was
>>>>> made "correctly”.
>>>> The proposed IsBaseOfAndDerived and IsBaseOf are being described and
>>>> placed as general utilities.  Their names are strongly reminiscent of
>>>> existing facilities in other libraries, including the (C++11) standard
>>>> library. Conforming to these external standards will avoid surprises,
>>>> especially when the proposed facilities are even described as being
>>>> the same (except for a noted corner case that is both hard to deal
>>>> with in a portable manner and not actually very interesting, so I'm
>>>> not complaining about that) in the review request email.
>>>> Additionally, I think quietly providing a potentially incorrect answer
>>>> in the case of incomplete types renders the offered code quite broken
>>>> for use as a general utility.  I think the requirement for complete
>>>> types by the well-known / standard facilities is well justified.
>>> While I disagree it is universally incorrect (as long as it is well
>>> defined and tested) to not have the exact same requirement on
>>> completeness on the parameters as C++11/boost, who also already have
>>> different behaviour in this exact regard as pointed out by yourself,
>>> I can try to fix it if it bugs you, but it will make the
>>> infrastructure even larger which seems to be a concern.
>>>>> On the contrary this is a well tested feature on all our supported
>>>>> compilers and I did not intend to engineer it to be identical to
>>>>> some external library if it only causes trouble rather than
>>>>> helping. This is our own class and it can do what we want it to do
>>>>> and be called what we want it to be called, and I think that is
>>>>> fine as long as it is well tested, which it is. At least this is my
>>>>> opinion. Anyone else agree?
>>>> Problem is, I think it's well tested code to solve the wrong problem.
>>>> I suggest that what we're really looking for is not a base/derived
>>>> relationship between a pair of classes, but that we actually want to
>>>> determine whether a platform-specific class exists.
>>>> As a result, I think there is more infrastructure in this change set
>>>> than is actually needed.  The present implementation of
>>>> SelectBaseClass uses IsBaseOf, but doesn't need a lot of the
>>>> functionality of that metafunction. There is no actual need for a
>>>> Base/Derived relationship between the (possibly not defined)
>>>> platform-specific class and the default platform-generic class, so the
>>>> use of IsBaseOf is an unnecessary restriction that only
>>>> serendipitously tests for defined or not, due to the previously
>>>> discussed defect in this particular implementation.  I also think the
>>>> name SelectBaseClass is perhaps overly general.
>>> The main idea of SelectBaseClass is to inject an optional class into
>>> the class hierarchy for specialization of generalized behaviour. It
>>> is absolutely critical (and intended) that this class is constrained
>>> to be a derived class of the fall-back class - it is not an artifact
>>> nor an accidental behaviour. In the hierarchy A is-maybe-a B is-a C,
>>> A must be able to rely on the superclass being at least C-like so
>>> that the behaviour of the superclass can be used reliably and
>>> transparently regardless of the existence of the optional B. If this
>>> contract would not hold, I think it could lead to some really weird
>>> and unreliable code which should not be encouraged (at least not
>>> according to the Liskov substitution principle etc).
>>> So what I need in the contract is a check if the optional class is
>>> defined and if it fits in the inheritance hierarchy: they are both
>>> important IMO.
>>> Yes it could have been less code. From the beginning I had a single
>>> trait class that checked for both the inheritance and whether the
>>> class is defined or not but you said you wanted the behaviour to more
>>> closely match that of third party libraries. This is why there is now
>>> a lot more code to more closely (but still not exactly) match that
>>> behaviour.
>>>> I might be able to use a similar approach in some code I've been
>>>> working on as a background task.  And while writing this reply I've
>>>> thought of another place that might benefit from something along those
>>>> lines.  Thinking about the code under review in that context, I think
>>>> some changes would make these other possible use-cases easier and
>>>> clearer.
>>>> I believe what is actually needed is a metatfunction something like:
>>>> /**
>>>> * Metafunction whose nested type member is Default if Specific is
>>>> * incomplete, otherwise Specific.
>>>> *
>>>> * Specific and Default must both be non-cv qualified class types, and
>>>> * must not be the same type.
>>>> *
>>>> * If Specific is incomplete at the point where this metafunction is
>>>> * invoked, it must never be defined elsewhere in the program.
>>>> */
>>>> template<typename Specific, typename Default>
>>>> struct SelectPlatformBase;
>>>> [Note: I'm not wedded to that name.]
>>>> Now, it turns out to be hard / impossible to write a generic
>>>> is_complete<T> metafunction, due to ODR.  Whether a type is complete
>>>> can vary between translation units, and even within a translation
>>>> unit. Having is_complete<T>::value have different values depending on
>>>> where it is instantiated is an ODR violation.  (One can make progress
>>>> by using anonymous namespaces and macro wrappers with __LINE__
>>>> concatenation, but there is still the fundamental question of whether
>>>> is_complete<T> really even makes semantic sense.)
>>> Yes and that makes me wonder if we care enough about the completeness
>>> semantics being the same as third party libraries (who are already
>>> inconsistent) if we do not need them to be, and on the contrary
>>> benefit from them not to be.
>>>> However, we don't need a fully general is_complete utility.  We have a
>>>> much more restricted use-case, where we want to select an optionally
>>>> defined platform-specific class if it exists, and otherwise fall back
>>>> to a more generic class.  And we're not using this in arbitrary
>>>> contexts, but only to select a platform-specialized implementation if
>>>> such exists.
>>>> Here's a lightly tested implementation of SelectPlatformBase:
>>>> // --- SelectPlatformBase
>>>> template<bool b, typename T = void>
>>>> struct EnableIf { typedef T type; };
>>>> template<typename T>
>>>> struct EnableIf<false, T> { };
>>>> template<bool Cond, typename Then, typename Else>
>>>> struct If { typedef Then type; };
>>>> template<typename Then, typename Else>
>>>> struct If<false, Then, Else> { typedef Else type; };
>>>> // Metafunction whose nested value member is true if T is defined
>>>> // (complete), and false if T is incomplete.  T must be a non-cv
>>>> // qualified class type.  If T is incomplete at the point where this
>>>> // metafunction is invoked, it must never be defined elsewhere in the
>>>> // program.
>>>> template<typename T>
>>>> class IsClassDefined {
>>>>   typedef char yes[1];
>>>>   typedef char no[2];
>>>>   template<typename U>
>>>>   static typename EnableIf<sizeof(U) == sizeof(U), yes>::type &
>>>> check(U*);
>>>>   static no& check(...);
>>>> public:
>>>>   static const bool value = sizeof(check((T*)0)) == sizeof(yes);
>>>> };
>>>> template<typename Specific, typename Default>
>>>> struct SelectPlatformBase {
>>>>   typedef typename If<
>>>>     IsClassDefined<Specific>::value, Specific, Default>::type type;
>>>> };
>>>> // --- end SelectPlatformBase
>>>> That's ~30 lines of code (+ tests and comments TBD) to do precisely
>>>> what we need, replacing ~100 lines (+ tests and comments) that have
>>>> various issues.  (If and EnableIf should probably be hoisted out as
>>>> separate utilities.)  We don't actually need SelectPlatformBase, and
>>>> could instead just directly use the If and IsClassDefined
>>>> metafunctions, but giving this functionality a name might be clearer.
>>> As stated, I think it’s dangerous to allow conditional inheritance
>>> where there is no inheritance constraint on the optional class.
>>> Therefore I do not think this is better, although I agree it is smaller.
>>> I agree that our use case here is smaller. If the amount of code is
>>> the problem and less code (and hence fewer reusable components) is
>>> preferred, then the original implementation with a simple
>>> IsBaseOfAndDerived does the trick (without checking types/cv
>>> qualifiers) and could be done in just a few lines, and this is what I
>>> originally did before you told me to expand it to more closely
>>> resemble external libraries:
>>> template<typename Base, typename Derived>
>>> class IsBaseOfAndDerived {
>>>    typedef char yes[1];
>>>    typedef char no[2];
>>>    template<typename T>
>>>    static yes &check(Derived*, T);
>>>    static no &check(Base*, int);
>>>    template<typename B, typename D>
>>>    struct IsBaseOfHost {
>>>      operator B*() const;
>>>      operator D*();
>>>    };
>>> public:
>>>    static const bool value = sizeof(check(IsBaseOfHost<Base,
>>> Derived>(), int())) == sizeof(yes);
>>> };
>>> It checks the inheritance and existence of the Derived class which is
>>> exactly the constraints I need.
>>> If you do not want to expose it publicly and remove the reusability
>>> because it does not have identical semantics as third party libraries
>>> regarding the requirements of the Derived type to be complete, it
>>> could be an internal class of SelectBaseClass.
>>> I’m not going to value the reusability vs LoC, I leave that decision
>>> to you.
>>>> However, while I think there are other use cases for this specific
>>>> utility, I still think the proposed change set as a whole is adding a
>>>> lot of code just to avoid a a few lines of macro usage.  That seems
>>>> like a poor tradeoff to me.
>>> Okay.
>>> In the end, we have to make a choice - what is more important, that
>>> the traits resemble some external library or code size. Because last
>>> time I proposed a small change and you wanted it to be closer to
>>> C++11 behaviour and I made it closer to that at the expense of more
>>> code. Now you are saying I have to make it even tighter to C++11 (or
>>> boost?), but also don’t want more lines of code which I believe is
>>> impossible given that I want to constrain both the class hierarchy to
>>> be reliable and check the existence of the optional class. This
>>> brings me to a few architectural questions which I may have (strong)
>>> opinions about but are not up to me to decide.
>>> 1. Do we want to replace the macros with template metaprogramming? 2
>>> reviewers (and me) liked the metaprogramming approach and it was
>>> implemented because they (like me) did not want the macros. But you
>>> have a different opinion. I can’t judge who is right and wrong here.
>>> 2. Do we want to minimize the code size at the cost of reusability by
>>> making contracts of dependent components weaker and putting them as
>>> private classes in the SelectBaseClass?
>>> 3. If we do want template metaprogramming and want reusable
>>> components, the question is, is it important that the specification
>>> of completeness (which in general is almost impossible to implement
>>> correctly in a portable way as you pointed out) of the template
>>> arguments in IsBaseOf is identical as C++11/boost even though no code
>>> needs it to be and on the contrary some code benefits from it not to
>>> be? In that case which one of them do we, because they are already
>>> different anyway? There is a tradeoff between code size and
>>> complexity vs copying either one of the external libraries regarding
>>> requirements of completeness of template parameters.
>>> Thanks,
>>> /Erik

More information about the hotspot-dev mailing list