User: Password:
|
|
Subscribe / Log in / New account

C11 atomic variables and the kernel

C11 atomic variables and the kernel

Posted Feb 19, 2014 5:57 UTC (Wed) by wahern (subscriber, #37304)
In reply to: C11 atomic variables and the kernel by jzbiciak
Parent article: C11 atomic variables and the kernel

C++11 basically refused to adopt most of the changes in C99, including named initializers and compound literals. C11 additions like _Generic and (IIRC) anonymous struct members also didn't get added to C++11.

The relevant question isn't what C++11 didn't add, it's what it actually adopted from C99 and C11, which is very little. The languages have moved further apart with the latest standard.

The atomics work was one area where everybody intentionally worked together to make sure there'd be compatibility. C11 didn't simply pull it in from C++11; the cooperation was very intentional and explicit.

In any event, at this point I think it's safe to say that the C and C++ languages have pretty much parted ways. All the C++ committee cares about is that vendors can maintain ABI compatibility between C and C++ compiled units. C++ isn't interested in keeping up-to-date with C syntax. Some C syntax, like compound literals, I don't think even can be supported, given changes in C++11 regarding temporaries. (But I'm not a C++ programmer so I could be wrong on that last point.)


(Log in to post comments)

C11 atomic variables and the kernel

Posted Feb 19, 2014 6:11 UTC (Wed) by wahern (subscriber, #37304) [Link]

One adoption I forgot about is that C++11 added <stdint.h> and <cstdint>. Again, I think all C++ cares about is making ABI compatibility practical for vendors. So the memory-model (atomics) and primitive types (stdint.h) matter. Syntax changes, not so much.

I just had to tweak a bunch of C headers at work to parse as C++. Very annoying, even though g++ and clang++ try hard to support C99 and C11 extensions in C++ code. But there remain considerable differences. The problem I ran into is that C99 added static inline routines, which means it's more common to encounter actual code blocks in C headers where you quickly run into, e.g., type casting issues like casting from const void * to const char *, which requires static_cast<> or reinterpret_cast<> in C++.

C11 atomic variables and the kernel

Posted Feb 19, 2014 7:11 UTC (Wed) by epa (subscriber, #39769) [Link]

The compiler should have a single flag you can specify which gives an error if any code construct has different semantics between C and C++. (In other words so you can write code in the intersection of both languages.) Of if that's too ambitious, at least check the syntax is valid in both languages.

C11 atomic variables and the kernel

Posted Feb 19, 2014 8:12 UTC (Wed) by jzbiciak (subscriber, #5246) [Link]

It's a bit late for me to find chapter and verse, but I seem to recall there are subtly different aliasing rules between the two.

Other areas where I was surprised—or at least dismayed and/or annoyed—by differences (at least in implementation): C99 _Bool vs. C++ bool, complex numbers (beyond just syntax), enums...

C11 atomic variables and the kernel

Posted Feb 19, 2014 9:59 UTC (Wed) by epa (subscriber, #39769) [Link]

Yes, many of these differences are quite pointless. Stroustrup a few years ago wrote an article C and C++ - sibling rivalry about some of them.

While I wouldn't necessarily support merging C and C++ into a single language, or even requiring one to be a subset of the other, I do think the two standards committees should be merged into one group which looks after both languages. That would piss them off a great deal, but better to piss off the committee than the programmers who have to cope with the needless differences.

C11 atomic variables and the kernel

Posted Feb 19, 2014 14:21 UTC (Wed) by mathstuf (subscriber, #69389) [Link]

I don't think they should be merged. You're basically firing any C standard member who doesn't know C++ already and with how complicated the language is, that'd be a lot of time to require. Personally, I'd prefer if they just say that C support in C++ was a dumb idea and make a formal split. Alas, backwards compatibility :/ .

C11 atomic variables and the kernel

Posted Feb 19, 2014 23:40 UTC (Wed) by oshepherd (guest, #90163) [Link]

Perhaps due to higher general interest, perhaps because there is more going on there - my impression and experience is that the C++ standards committee is a lot more active and quite a bit more rigorous.

While the C++ standard has more bugs than the C standard - thats' not surprising, its' significantly larger - it also has less bugs per page, and the bugs it does have are more subtle (and thus harder to find)

The C11 standard has some careless bugs which kind of give away the fact that the C committee is really quite small - perhaps too small.

C11 atomic variables and the kernel

Posted Feb 19, 2014 15:48 UTC (Wed) by jwakely (guest, #60262) [Link]

While I wouldn't necessarily support merging Linux and FreeBSD into a single kernel, or even requiring one to be a subset of the other, I do think the two development communities should be merged into one group which looks after both kernels. That would piss them off a great deal, but better to piss off the kernel developers than the programmers who have to cope with the needless differences.

(Because telling volunteers they should be donating their time to projects they don't care about is a great way to get good results, right?)

C11 atomic variables and the kernel

Posted Feb 19, 2014 16:22 UTC (Wed) by epa (subscriber, #39769) [Link]

I imagined that the standards committees were staffed with paid employees of compiler vendors, e.g. Herb Sutter at Microsoft, but with the dominance of gcc and LLVM this view of the world must be out of date.

C11 atomic variables and the kernel

Posted Feb 28, 2014 14:55 UTC (Fri) by ianmcc (subscriber, #88379) [Link]

Microsoft only started participating in the C++ standards effort relatively recently (ie, when Microsoft employed Herb to take over as head of the C++ compiler group). Prior to then, Microsoft actively worked against standardization efforts.

I think most of the C++ committee are allowed to work on standardization work as part of their employment, but most somewhat reluctantly, it isn't their core job.

C11 atomic variables and the kernel

Posted Feb 20, 2014 11:57 UTC (Thu) by tvld (guest, #59052) [Link]

If you want to influence standardization, there's a straight-forward way to do that: Get involved. http://isocpp.org has useful information about the whole process, mailing lists, etc.

C11 atomic variables and the kernel

Posted Feb 19, 2014 9:09 UTC (Wed) by tialaramex (subscriber, #21167) [Link]

But the _effect_ of such a flag is that C++ programmers will insist that C programmers "ought" to use the flag so as to not interfere with their pretty notion that C is just a limited subset of C++ written by Neanderthals. Which is bogus. When something provides C, it means C, not "I'm sure you can just paste this into a C++ program and that'll be fine".

If you add a Java component to a C program nobody expects that they'll just be able to import some.c.code; into the Java and have that work. If you need to access Perl from Python, or Ruby from PHP again, nobody insists that there should be some "easy" way to just mix them together as if they were merely dialects of the same language. In each case you're responsible for handling the adaptor layer. But somehow C++ programmers seem to think they shouldn't need to do this with C, let's not make this myth any easier to believe.

C11 atomic variables and the kernel

Posted Feb 19, 2014 9:57 UTC (Wed) by epa (subscriber, #39769) [Link]

Yes, they are different languages and anyone who says 'C/C++' usually doesn't know what they are talking about. Nonetheless there are plenty of C programmers who choose to make their code also valid C++, not because they are bullied into it by heartless C++ programmers, but as a way of getting some additional compile-time checking and warnings for odd corners of the language.

An 'intersection' compiler mode might also be useful when writing C wrappers for libraries implemented in C++.

C11 atomic variables and the kernel

Posted Feb 19, 2014 14:18 UTC (Wed) by mathstuf (subscriber, #69389) [Link]

As a C++ developer for the most part, it has been my belief that C++'s greatest mistake has been C source compatibility (to the extent that it is even true). I firmly believe that having an FFI to C would have been much cleaner in the long run. Unfortunately, that ship sailed long ago. This further divide makes me even more sure of it though.

> compound literals

Is this why I can't declare literals like 'char const* const* strarray[] = { { "a", "b", NULL }, NULL };' in C++?

C11 atomic variables and the kernel

Posted Feb 19, 2014 20:57 UTC (Wed) by wahern (subscriber, #37304) [Link]

Pretty much, I guess. Although the valid syntax would be

char const* const* strarray[] = { (char const* const[]){ "a", "b", NULL }, NULL };

which the compiler effectively translates to

char const* const tmp[] = { "a", "b", NULL };
char const* const* strarray[] = { tmp, NULL };

Correct me if I'm wrong, but I believe that the problem is that the construct (type){ initializer list } in C++ creates a temporary array which is destroyed when the expression goes out of scope (I dunno if this is C++11 syntax or a common extension to be adopted in the next version). Compound literals in C have block scope lifetime.

Compound literals are a nice addition to C. It's clearly possible to live without them, but it's often convenient to be able to create small objects or buffers inline. For example, you can wrap something like strerror_r or strerror_l to behave like strerror, although you have to be careful about scoping. Very crude example: #define xstrerror(error) strerror_r((error), (char[256]){0}, 256).

I presume C++ has more wholesome ways of doing stuff like this, which is why the committee wasn't interested in the extension.

C11 atomic variables and the kernel

Posted Feb 20, 2014 1:15 UTC (Thu) by jwakely (guest, #60262) [Link]

> I dunno if this is C++11 syntax or a common extension to be adopted in the next version

Neither. It's a common extension but very unlikely to be standardised.

C11 atomic variables and the kernel

Posted Feb 20, 2014 6:12 UTC (Thu) by wahern (subscriber, #37304) [Link]

Ah, the syntax was I was thinking of is called something like "list-initialized temporaries", which was added to C++11 to allow classes to accept initialization lists in the manner of plain aggregate types. The syntax is similar, just without the parentheses--type{ initialization-list}, or just { initialization-list } if the type can be inferred.

GCC decided to give compound literals in C++ the same treatment as it does initializer lists. The temporary objects in the initializer lists are scoped to the expression. See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53220

C11 atomic variables and the kernel

Posted Feb 20, 2014 8:06 UTC (Thu) by mm7323 (subscriber, #87386) [Link]

Not sure if c++ has alloca(), but this looks neater to me:

#define xstrerror(error) strerror_r((error), alloca(256), 256)

C11 atomic variables and the kernel

Posted Feb 20, 2014 13:29 UTC (Thu) by jzbiciak (subscriber, #5246) [Link]

Well, technically, neither C nor C++ has alloca(), although it's a common extension. Or did C11 add this?

C11 atomic variables and the kernel

Posted Feb 27, 2014 8:02 UTC (Thu) by kevinm (guest, #69913) [Link]

Calling alloca() from a function argument is historically a very iffy thing to do - there were implementations where this crashed and burned very badly (alloca ended up fudging the stack pointer in the middle of the code pushing arguments onto the stack).

C11 atomic variables and the kernel

Posted Feb 27, 2014 8:52 UTC (Thu) by jzbiciak (subscriber, #5246) [Link]

Well, at least among the compiler folk I work with, they'd argue that alloca() is an ugly thing to do, full stop. ;-)

Given that our own compiler doesn't support alloca(), and tries to figure out the total stack frame for the entire function (so that no matter what happens, the SP moves once on entry, once on exit), I can't say I entirely blame them for that opinion. (Or, at least, that's what our compiler did the last time I dug into it at that level.)

Is it just me, or does alloca() mostly just feel like a hack to get around the lack of destructors or other unrolling mechanisms tied to leaving scopes?

C11 atomic variables and the kernel

Posted Feb 27, 2014 8:58 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link]

It also offers very fast allocation and deallocation of arbitrarily-sized arrays within a cache-local area. It's hard to beat that.

C11 atomic variables and the kernel

Posted Feb 27, 2014 17:15 UTC (Thu) by jzbiciak (subscriber, #5246) [Link]

True, but if you already have to limit yourself to a certain maximum size allocation to effectively use alloca(), then declaring a local array gets you that same locality benefit.

I also saw elsewhere a horror story where a function call w/alloca() got inlined and turned into a stack overflow, because apparently the compiler deferred the implicit freeing of the buffer to the end of the parent function. ie:

    void inlined_func(...)
    {
         ... 
         alloca( ... ); 
         ...
         /* implicit free() here */
    }

    void parent( ... )
    {
         for (i = 0; i < 10000000; i++)
             inlined_func();
    }

turned into:

    void parent( ... )
    {
         for (i = 0; i < 10000000; i++)
         {
             ...
             alloca( ... );
             ...
         }
         /* implicit free() here */
    }

Oops.

I've never had that happen with local arrays, though. When the compiler inlines a function with a local array, the result looks more like an array local to the parent that it got inlined within, so it statically becomes part of the stack frame while you're in the parent. That is, if you replace alloca() in the example above with char buf[MAXBUF];:

    void parent( ... )
    {
         for (i = 0; i < 10000000; i++)
         {
             char buf[MAXBUF]; /* becomes a static part of the stack frame */
             ...
             ...
         }
    }

Sure, statically sized buffers have their own issues—buffer overflow attacks—but alloca() is no panacea if it can be used to overflow the stack and crash the app. I guess what I'm saying is that the set of places where alloca() provides an advantage over a statically sized buffer are limited because the places where it's safe to use either have so much overlap, while the places where only one or the other is appropriate are fairly small.

C11 atomic variables and the kernel

Posted Feb 27, 2014 18:00 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link]

Statically sized buffers are way too wasteful. For example, storing path would require MAXPATH of stackspace for all paths.

C11 atomic variables and the kernel

Posted Feb 28, 2014 5:58 UTC (Fri) by jzbiciak (subscriber, #5246) [Link]

And yet, if you do encounter a series of paths that are at or near MAXPATH bytes, you'd still end up wasting that space anyway with alloca(). If you're saying you can't handle so many MAXPATH pathnames, then you're broken if you use alloca(), since it doesn't offer a way to fail gracefully.

If you're manipulating many structures that have potential not to fit on the stack, another approach I've seen is to allocate a static buffer large enough to catch most common cases, and fall back to a malloc()'d buffer if you'd exceed that threshold. It does require a conditional call to free(), but it's more robust than alloca() and avoids the unconditional bloat of overlarge local arrays.

(And, with the explicit malloc() and free(), it won't run afoul of the inlining gotcha I highlighted above.)

C11 atomic variables and the kernel

Posted Feb 28, 2014 11:37 UTC (Fri) by khim (subscriber, #9252) [Link]

Yes, bugs in compilers exist and should be fixed. Just like any other types of bugs. We don't design our programs around old bugs in kernel or libc, why should we design them to support broken compilers?

The rest of discussion sounds so bizzare I can not even believe I hear it on LWN. ALL remaining arguments only make sense for address-space constrained system without overcommit.

On systems with overcommit enabled and with no shortage of the address space (and that's 99.99% of systems out there) alloca is as safe as malloc+free and much, much faster. End of discussion.

C11 atomic variables and the kernel

Posted Feb 28, 2014 15:56 UTC (Fri) by mathstuf (subscriber, #69389) [Link]

Don't you argue that mobile devices are eating other form factor's lunches? They're all 32-bit last I checked where address space is at a premium.

C11 atomic variables and the kernel

Posted Feb 28, 2014 17:01 UTC (Fri) by khim (subscriber, #9252) [Link]

Don't you argue that mobile devices are eating other form factor's lunches?

Well, sure.

They're all 32-bit last I checked where address space is at a premium.

It's good idea to check things more often than once per decade. Have you head about this phone? How about this CPU or that one?

And I don't see where you get the notion that 32-bit implies “address space is at a premium”: typical mobile OS does not use swap and keeps many applications in memory at the same time. Any given application can only use 128MB or so. You can easily give allocate 16MB or 32MB of address space for heavy worker threads - more then enough for alloca.

C11 atomic variables and the kernel

Posted Feb 28, 2014 17:34 UTC (Fri) by jzbiciak (subscriber, #5246) [Link]

On platforms that support it. Still, it's not portable, and doesn't seem to offer truly material advantages in my eyes.

One place I've run into alloca() was in some initialization code of g_doom, the "generic frame buffer" port of Doom. That's pretty much the last place to make a "hot cache efficiency" or any other time-based argument for alloca() over malloc(). It was there pretty much for the purpose of getting an automatic deallocation if it took an early exit from the function.

The system I was porting to, however, didn't (and still doesn't) support alloca(), so I had to convert it to malloc()/free(). (And when I asked our compiler guys about it, they said "Just use malloc()" with the sort of tone that implied they thought alloca() was an abomination.)

Yes, the code was marginally cleaner looking with alloca(), but my point was that that benefit arises from the fact that C doesn't offer any other easy way to say "clean all this up when we leave this scope" other than to put things on the stack and rely on the stack frame unwinding.

Aside from non-portability, alloca() also pretty much requires you to use a frame pointer, since your stack frame size is now variable. Again, not usually a big deal, although it can hurt on register-starved architectures like 32-bit x86. And then there's all these fun comments in alloca()'s own manual page:

BUGS
        The alloca() function is machine and compiler dependent.  On many sys-
        tems its implementation is buggy.  Its use is discouraged.

        On many systems alloca() cannot be used inside the list of arguments of
        a function call, because the stack space reserved by alloca() would
        appear on the stack in the middle of the space for the function argu-
        ments.

I get it, you like alloca(). I see enough things potentially wrong with it that its meager advantages don't seem worth it to me.

C11 atomic variables and the kernel

Posted Feb 28, 2014 18:06 UTC (Fri) by khim (subscriber, #9252) [Link]

I get it, you like alloca(). I see enough things potentially wrong with it that its meager advantages don't seem worth it to me.

I'm not a big alloca lover, but I just don't see what's the big hoopla is all about. I mean: alloca is just a minor syntaxic sugar on top of facilities you have anyway. Yes, it's not portable but it's an interface people are familiar with, so why not?

Before you'll raise the racket about frame pointers and stuff please recall that you compiler must compile, e.g. the following program (from 6.5.3.4 The sizeof oprator part of the C standard):

#include <stddef.h>

size_t fsize(int n)
{
  char b[n+3];
  return sizeof b;
}

It's a 100% standard program. It's included in standard. It's non-optional part of it. It must be supported.

Now on any system where it's supported alloca implementation is trivial, so why not just implement and use it where appropriate?

I'm yet to observe a system which supports the aforementioned program (from the official 15 years old standard - and included again in new C11 one, too!) which does not support working and usable alloca and if you insist on using broken tools you deserve to receive broken programs.

P.S. Note that function in standard is called fsize—this gives you pretty strong hint where and how such facilities are supposed to be used, right?


Copyright © 2017, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds