[Libguestfs] [libnbd PATCH v4 1/3] lib/utils: introduce xwritel() as a more robust and convenient write()

Laszlo Ersek lersek at redhat.com
Thu Mar 16 09:30:22 UTC 2023


On 3/15/23 18:14, Eric Blake wrote:
> On Wed, Mar 15, 2023 at 03:30:12PM +0100, Laszlo Ersek wrote:
>> On 3/15/23 15:01, Eric Blake wrote:
>>
>>> [...]
>>
>> Thanks for the thorough review; I'm glad all the fine points I sought to
>> put in the patch were received -- and well-received! :)
>>
>> One question:
>>
>>> The only change I recommend is the addition of the __attribute__; but
>>> with or without it, I'm happy with:
>>
>> Do we have general rules on attribute usage in libnbd vs. nbdkit?
>>
>> The __sentinel__ (aka sentinel) attribute is used in nbdkit, but not yet
>> in libnbd. Now, that could be happenstance, but it rhymes with another
>> (obscure?) discrepancy in attribute usage.
> 
> I think it's happenstance; until today, libnbd did not yet have a
> varargs function where annotating the need for a NULL terminator was
> useful to let the compiler aid in flagging erroneous usage.
> 
>>
>> Namely, when I was comparing the common/ subdirectories of both
>> projects, I noticed that nbdkit used the cleanup attribute, and libnbd
>> didn't. First I thought it was a mistake / oversight, but then I found a
>> porting note from Rich, in libnbd commit f306e231d294 ("common/utils:
>> Add extensible string, based on vector", 2022-03-12):
>>
>>     RWMJ: This removes the CLEANUP_FREE_STRING macro since libnbd does not
>>     use __attribute__((cleanup)).
>>
>> and then again in f3828bfd42be ("common/utils: Add new string vector
>> types", 2022-03-12):
>>
>>     RWMJ: Removed the CLEANUP_* macros.
>>
> 
> Most attributes are merely extensions that aid the compiler in aiding
> you.  __attribute__((sentinel)) is squarely in this camp - compilers
> that understand it can warn on questionable code, while you can
> #define a wrapper to an empty string for all other compilers with no
> change in program behavior.
> 
> But __attribute__((cleanup)) is a special beast - it affects runtime
> behavior, and if you use it, you are REQUIRED to have compiler
> support.  There is no way to write preprocessor macros that will
> provide the same runtime functionality that cleanup implies for use by
> a purely standards-compliant cc.  That said, it is still a localized
> compile-time effect, and does not impact ABI - it is merely reducing
> (a lot!) of boilerplate coding that would otherwise be needed without
> the attribute in play.  I see no problem in mixing an executable that
> uses it with a library that does not (nbdkit does just that - our
> server uses cleanup, but can run a plugin compiled without cleanups
> just fine), nor with mixing a library that uses it with an executable
> does not (which could be the case if libnbd starts using it).
> 
> Rather, the drawback of using __attribute__((cleanup)) in libnbd is
> that we would now REQUIRE libnbd to be compiled with gcc or clang.
> Right now, I don't know if anyone is trying to use libnbd with an
> alternative compiler (no one has submitted patches or bug reports for
> using libnbd under MSVC, for example), so it may be a non-issue.  But
> it's a one-way bridge - once we explicitly decide that we expect a
> particular extension to the standards to even be able to use the
> library, it becomes a lot harder to port the code to other platforms
> without that specific compiler extension without replacing it back to
> a lot of boilerplate code in its place.
> 
> At the time we copied the vector code from nbdkit over to libnbd, we
> weren't sure what environments would try to use libnbd, so we
> intentionally did not port attribute cleanup stuff to avoid crippling
> an unknown user.  I'm not opposed to using the cleanup attribute, and
> if we DO decide to use it, I'd love to go all in and utilize it
> wherever it makes sense, which is more than just with of vectors.
> Maybe the thing to do is have one major release where we announce our
> intention to utilize the attribute in a future release, unless someone
> speaks up with a reason why it would break with their preferred
> toolchain; it delays the decision, and means we can't use it right
> away, but at least would be a documented transition rather than a
> blind "sorry you can't build anymore".
> 
>> So those comments (esp. the one on commit f306e231d294) at least confirm
>> that the difference is intentional. I still don't know the reason for
>> the difference. And now I wonder: does the same (unexplained) reason
>> underlie the "sentinel" attribute's absence too, in libnbd?
>>
>> If there is a common reason for avoiding both "cleanup" and "sentinel"
>> in libnbd, we should probably not start using "sentinel" now. If, on the
>> other hand, "sentinel" is not covered by the same argument as "cleanup"
>> (not to mention if there isn't an actual reason for avoiding "cleanup"
>> in the first place!), then I can add the sentinel attribute when merging
>> this patch.
> 
> I think the argument for not backporting "cleanup" is much different
> than the one for not having needed to use "sentinel" to date.

Thanks for the detailed explanation.

While I don't like this extra (orthogonal) complexity, I've made an
effort to collect some information.

(1) I couldn't figure out at what clang / gcc version the sentinel
attribute was introduced.

(2) The gcc manual says that __attribute__ ((__sentinel__)) is
equivalent to __attribute__ ((sentinel)); it's just that the former is
more suitable for public header files, to be included by client apps,
where "sentinel" could already exist as a macro, while __sentinel__
couldn't.

This is in fact (at least superficially) consistent with nbdkit's usage
of the attribute; we have "__sentinel__" in "common/utils/utils.h" and
"tests/test.h" (header files -- albeit not public), and "sentinel" in
"tests/test-layers.c".

So I'll squash "__attribute__ ((sentinel))" into the C code of this patch.

(3) Whether or not we should add a wrapper macro. Libnbd is not really
consistent in this regard. The generator defines
LIBNBD_ATTRIBUTE_NONNULL and LIBNBD_ATTRIBUTE_ALLOC_DEALLOC -- those are
for the public "libnbd.h" header, so the wrapper macros are certainly
justified there.

The question is however what the libnbd-internal code does. And that's
*seemingly* inconsistent:

(3.a) the internal code consumes LIBNBD_ATTRIBUTE_NONNULL and
LIBNBD_ATTRIBUTE_ALLOC_DEALLOC liberally, from the public (generated)
library header -- likely because that's the convenient thing to do,

(3.b) in a single case, we have an internal-only wrapper:
NBD_ATTRIBUTE_PACKED in "lib/nbd-protocol.h" (whose definition
effectively enforces clang or gcc) -- and this seems to be shared with
nbdkit,

(3.c) we have a bunch of internal code that uses naked attributes, such
as "__nonnull__", "__unused__", "constructor", "noreturn", "destructor",
"packed".

For (3.a) and (3.b), I can cook up a guiding principle -- both
"libnbd.h" and "nbd-protocol.h" look public, at least to an extent,
while the stuff in (3.c) is totally internal.

So, I can equate wrapper macros to public headers, for now, and I won't
introduce a new macro just for this one application of "__attribute__
((sentinel))" in "lib/utils.c".

Laszlo



More information about the Libguestfs mailing list