qemu-block
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

When and how to use C++ (was Re: [PATCH experiment 00/16] C++20 coroutin


From: Paolo Bonzini
Subject: When and how to use C++ (was Re: [PATCH experiment 00/16] C++20 coroutine backend)
Date: Tue, 15 Mar 2022 18:23:08 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.5.0


On 3/15/22 15:50, Kevin Wolf wrote:
I'm not sure what the C++ lock guards offer that our current lock guards
don't? Passing down lock guards makes sense to me, but why can't you do
that with QemuLockable?

Passing a QemuLockable alone doesn't ensure that the lock has been taken. I guess we could build a QemuLockGuard on top that ensures that.

(Hm, or can the C++ version somehow check at
compile time that it's the _right_ lock that is held rather than just
any lock? It didn't look like it at the first sight.)

It's not related to lock guards but clang++ has thread-safety analysis that checks for the right lock. It is in theory there for C as well but it's mostly a joke. Ironically, the only good use that I've seen of it is Marc-André's never committed work to check coroutine_fn markers at compile-time[1].

But I do see the benefit of a compiler checked CoroutineFn<> return type
compared to the coroutine_fn markers we have today. On the other hand...

Also, once C++ is available people will start submitting C++ patches
simply because they are more comfortable with C++ (especially
one-time/infrequent contributors).

...using C++ in coroutine code means that all of the block layer would
suddenly become C++ and would be most affected by this effect. I'm not
sure if that's something I would like to see, at least until there is a
clear tree-wide policy (that preferably limits it to a subset that I
understand).

You are the maintainer of the block layer, so you would have the biggest impact and obviously have input in such a policy. I obviously don't have a policy ready[2], but I do have some goals:

1) whenever C++ is used to build an API for use in C++ source files:

        Those source files and their debugging experience should look
        like C, and not any more "foreign" than       the "QEMU C" dialect
        that we already have.

        Rationale: we might have funky code in headers, but
        "QEMU_WITH_LOCK_GUARD(x) { }" is understandable; the same would
        hold for "CoroutineFn<void>", "co_await fn();" or if hwaddr
        internally used operator overloading.

2) whenever C and C++ provide two different implementations of an API, for use in C and C++ source files respectively:

        If the same API is implemented in different ways for C and C++,
        the semantics should be equivalent, and covered by both
        C and C++ testcases.

        Rationale: this should be a rare occasion, but there are
        features such as _Generic that are C only, so it should be
        catered for; QemuLockable came out already in this proof of
        concept.  The implementation is completely different for C
        and C++, but it works the same (as proved to some extent
        by testcases).

        Looking again at the hwaddr example, it would be okay to forbid
        "hwaddr - hwaddr" subtraction in C++ code.  But it would not be
        okay to have it return a signed integer in C++, while it
        returns an unsigned integer in C.  Could there be a C++-only
        method "hwaddr.diff(hwaddr)" that does return a signed integer?
        That would have to be discussed, right now my opinion is
        "probably not" but see the next point too.

3) whenever C++ features (templates, classes, etc.) are used in .cc files:

        The features should have a clear benefit over equivalent
        C-only code (e.g. using the preprocessor instead of templates)
        and if applicable they should publish a C API[3] that can be
        consumed outside a particular subsystem.

        If in doubt, err on the side of moderation, i.e. less C++ and
        fewer C++ features.

        Example: I can imagine that in some cases the semantic
        replacement abilities provided by templates will for example
        improve compile-time checking and reduce code duplication
        compared to gcc.  However, QEMU already has a wide range of
        data structures and auxiliary APIs for C, and those should be
        used for C++ code to avoid splitting the code base.

        In turn, this should limit the usage of other C++ features.
        For example, constructors (as opposed to just being able to
        zero-initialize memory) can be more of a pain than a benefit
        if you can't use "new", "delete" or "std::vector<>".  Likewise,
        it's unlikely that we'll have reasons to use anonymous
        namespaces instead of "static", because that's mostly useful
        for classes defined in .cc files and their member functions[5].
        And VMState will prevent the usage of "private:", thus lowering
        the benefit from methods.

Would this kind of formulation be enough? Maybe, maybe not, but either way I don't really believe in prohibiting features by policy. The QEMU community and especially its maintainers won't change overnight if C++ is allowed in the codebase; but they should be allowed to evolve their opinions as their experience grows. If some day the block layer maintainers want to extend coroutines to variable arguments rather than a single void *, they shouldn't be forbidden from doing so just because (I guess) it entails using variadic templates. They should show that there is a benefit, though.

In any case, Stefan's suggestion to look into what other projects did, especially GCC/GDB is a good one:

- https://gcc.gnu.org/wiki/CppConventions probably is a good start[4], but we would probably cut on the "explicitly permitted" parts. https://gcc.gnu.org/codingrationale.html also focuses a lot on things that QEMU would likely not use.

- the discussion at https://gcc.gnu.org/legacy-ml/gcc/2010-05/msg00705.html has some quotes that capture interesting the point. It's possible that I have subconsciously introduced it in my two messages on the topic:

        The goal is not to reimplement GCC from the ground up using
        modern OO/C++ techniques.  The goal is simply to permit
        ourselves to use C++ features where appropriate in the codebase.
        ---
        The right subset is dependent on context.  We're not in a
        safety-critical environment, we have a large existing C
        codebase, and we have a developer team made up of experienced
        C programmers, not all of whom are used to programming in C++.
        So, we need to take those factors into account.
        ---
        Switching to C++ should never be excuse to bring more more
        brittle codes or more obscurities.  Rather, it should be
        opportunity to write simpler and better code.

Also of interest is the replies to less enthusiastic developers starting at https://gcc.gnu.org/legacy-ml/gcc/2010-05/msg00744.html at https://gcc.gnu.org/legacy-ml/gcc/2010-06/msg00174.html.

- Richard might have some experience here, and his insight is valuable.

Paolo

[1] https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg00793.html

[2] in fact, even if we decided to go with C++ coroutines, it would take quite some time to split the coroutine/non-coroutine cases, which is a prerequisite for that, so we'd have a lot of time to develop a policy. Once the policy is there, there will be even more time before a C++-using patch that does something useful gets submitted and approved.

[3] or at least C-like, for example qemu_coroutine_create technically is a C++-only API because it takes a CoroutineFn<void>(*)(void *) argument. But it *looks* like C.

[4] lol, there's even a remark fom me in there, but I stopped working on GCC about at the time that the document was being written.

[5] https://stackoverflow.com/questions/154469/unnamed-anonymous-namespaces-vs-static-functions



reply via email to

[Prev in Thread] Current Thread [Next in Thread]