From: Avi Kivity <avi@argo.co.il>
To: Kyle Moffett <mrmacman_g4@mac.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>, linux-kernel@vger.kernel.org
Subject: Re: Compiling C++ modules
Date: Tue, 25 Apr 2006 19:46:02 +0300 [thread overview]
Message-ID: <444E524A.10906@argo.co.il> (raw)
In-Reply-To: <9E05E1FA-BEC8-4FA8-811E-93CBAE4D47D5@mac.com>
Kyle Moffett wrote:
> On Apr 25, 2006, at 03:08:02, Avi Kivity wrote:
>> Kyle Moffett wrote:
>>> The "advantages" of the former over the latter:
>>>
>>> (1) Without exceptions (which are fragile in a kernel), the former
>>> can't return an error instead of initializing the Foo.
>> Don't discount exceptions so fast. They're exactly what makes the
>> code clearer and more robust.
>
> Except making exceptions work in the kernel is exceptionally
> nontrivial (sorry about the pun).
My experience with exceptions in kernel-like code (a distributed
filesystem) was excellent.
>
>> A very large proportion of error handling consists of:
>> - detect the error
>> - undo local changes (freeing memory and unlocking spinlocks)
>> - propagate the error/
>>
>> Exceptions make that fully automatic. The kernel uses a mix of gotos
>> and alternate returns which bloat the code and are incredibly error
>> prone. See the recent 2.6.16.x for examples.
>
> You talk about code bloat here. Which of the following fits better
> into a 4k stack?
The C++ code. See below.
> Which of the following shows the flow of code better?
Once you accept the idea that an exception can occur (almost) anywhere,
the C++ code shows you what the code does in the normal case without
obfuscating it with error handling. Pretend that after every semicolon
there is a comment of the form:
/* possible exceptional return */
once you think like that, you can see what the code actually does rather
than how it handles errors. A 15-line function can do something
meaningful, not just call two functions.
>
> C version:
> int result;
> spin_lock(&lock);
>
> result = do_something();
> if (result)
> goto out;
>
> result = do_something_else();
> if (result)
> goto out;
>
> out:
> spin_unlock(&lock);
> return result;
>
> C++ version:
> int result;
not needed unless you actually return something.
> TakeLock l(&lock);
>
> do_something();
> do_something_else();
>
> First of all, that extra TakeLock object chews up stack, at least 4 or
> 8 bytes of it, depending on your word size.
No, it's optimized out. gcc notices that &lock doesn't change and that
'l' never escapes the function.
> Secondly with standard integer error returns you have one or two
> easily-predictable assembly instructions at each step of the way,
> whereas with exceptions you trade the absence of error handling in the
> rest of the code for a lot of extra instructions at the exception
> throw and catch points.
The extra code is out of line (not even an if (unlikely())). So yes,
total code grows, but the exceptional paths can be in a .text.exception
section and not consume cache or TLB space.
> Secondly, while the former is much longer it shows _explicitly_
> exactly where the flow of code can go. In an OS kernel, that is
> critical; your debugability is directly dependent on how easy it is
> to see where the flow of code is going.
>
I can only say that I had very positive experience with code that used
exceptions. Having less code to view actually improves visibility.
>>> (2) You can't control when you initialize the Foo. For example in
>>> this code, the "Foo item;" declarations seem to be trivially
>>> relocatable, even if they're not.
>>> spin_lock(&foo_lock);
>>> Foo item1;
>>> Foo item2;
>>> spin_unlock(&foo_lock);
>>
>> They only seem relocatable with your C glasses on. Put on your C++
>> glasses (much thicker), and initialization no longer seems trivially
>> movable.
>
> This is a really _really_ bad idea for a kernel. Having simple
> declaration statements have big side effects (like the common TakeLock
> object example I gave above) is bound to lead to people screwing up
> and forgetting about the side effects. In C it's impossible to miss
> the side effects of a statement; function calls are obvious, as is
> global memory modification.
>
In C++ you just have to treat declarations as executable statements.
Just as you can't compile the code with a C compiler, you can't read it
with a C mindset. Once you get used to it, it isn't surprising at all.
>> On the other hand, you can replace the C code
>>
>> {
>> Foo item1, item2;
>> int r;
>>
>> spin_lock(&foo_lock);
>> if ((r = foo_init(&item1)) < 0) {
>> spin_unlock(&foo_lock);
>> return r;
>> }
>> if ((r = foo_init(&item2)) < 0) {
>> foo_destroy(&item1);
>> spin_unlock(&foo_lock);
>> return r;
>> }
>> foo_destroy(&item2);
>> foo_destroy(&item1);
>> spin_unlock(&foo_lock);
>> return 0;
>> }
>>
>> with
>>
>> {
>> spinlock_t::guard foo_guard(foo_lock);
>> Foo item1;
>> Foo item2;
>> }
>
> Let me point out _again_ how unobvious and fragile the flow of code
> there is. Not to mention the fact that the C++ compiler can easily
> notice that item1 and item2 are never used and optimize them out entirely.
Excellent! If there are no side effects, I want it out. If there are
side effects, it won't optimize them out.
> You also totally missed the "int flags" argument you're supposed to
> pass to object specifying allocation parameters,
There is no allocation here (both the C and the C++ code allocate on the
stack.
Should you want to allocate from the heap, try this:
{
spinlock_t::guard g(some_lock);
auto_ptr<Foo> item(new (gfp_mask) Foo); /* or pass a kmem_cache_t */
item->do_something();
item->do_something_else();
return item.release();
}
contrast with
{
Foo *item = 0;
int r;
spin_lock(&some_lock);
item = kmalloc(sizeof(Foo), gfp_flags);
if (!item) {
item = PTR_ERR(ENOMEM);
goto out;
}
if ((r = foo_do_something(item))) {
foo_destroy(item);
kfree(item);
item = PTR_ERR(-r);
goto out;
}
if ((r = foo_do_something_else(item))) {
foo_destroy(item);
kfree(item);
item = PTR_ERR(-r);
}
out:
spin_unlock(&some_lock);
return item;
}
(oops, you wrote another version further on...)
> not to mention the fact that you just allocated 2 objects of unknown
> size on the stack (which is limited to 4k).
So did the C code. I was just side-by-side comparisons of *equivalent* code.
> AND there's the fact that the order of destruction of foo_guard,
> item1, and item2 is implementation-defined and can't easily be relied
> upon without adding massive amounts of excess braces:
It is well defined. guard ctor, item1 ctor, item2 ctor, item2 dtor,
item1 dtor, guard dtor.
It is also well defined that if a constructor is executed for an
automatic variable, so will its destructor. That makes the "guard" work
so well with exceptions.
>
> {
> spinlock_t::guard foo_guard(&foo_lock);
> {
> Foo item1;
> {
> Foo item2;
> }
> }
> }
>
Absolutely unneeded braces there.
> Also, your spinlock_t::guard chews up stack space that otherwise
> wouldn't be used. It would be much better to rewrite your above C
> function like this:
>
> {
> struct foo *item1, *item2;
> int result;
> spin_lock(&foo_lock);
>
> item1 = kmalloc(sizeof(*item1), GFP_KERNEL);
> item2 = kmalloc(sizeof(*item2), GFP_KERNEL);
> if (!item1 || !item2)
> goto out;
>
> result = foo_init(item1, GFP_KERNEL);
> if (result)
> goto out;
>
> result = foo_init(item2, GFP_KERNEL);
> if (result)
> goto out;
>
> out:
> /* If alloc and init went ok, register them */
> if (item1 && item2 && !result) {
> result = register_foo_pair(item1, item2);
> }
>
> /* If anything failed, free resources */
> if (!item1 || !item2 || result) {
> kfree(item1);
> kfree(item2);
> }
>
> spin_unlock(&foo_lock);
> return result;
> }
>
>> 14 lines vs 3, one variable eliminated. How many potential security
>> vulnerabilities? How much time freed to work on the algorithm/data
>> structure, not on error handling?
>
> Yeah, sure, yours is 3 lines when you omit the following:
> (1) Handling allocation flags like GFP_KERNEL
done
> (2) Not allocating things on the stack
done
> (3) Proper cleanup ordering
done, without lifting a finger
> (4) Reference counting, garbage collection, or another way to
> selectively free the allocated objects based on success or failure of
> other code.
Reference counting is ridiculously to do in C++. I'll spare you the details.
>
> Those are all critical things that we want to force people to think
> about; in many cases the exact ordering of operations _is_ important
> and that needs to be specified _and_ commented on. How often do you
> think people write comments talking about things that don't even
> appear in the source code?
C++ preserves the order (unless it can prove the order doesn't matter,
same as C)
>
> Also, since when is error handling _not_ a critical part of the
> algorithm? You can see in my more complicated example that you only
> want to free the items if the registration was unsuccessful. How do
> you handle that without adding a refcount to everything (bloat) or
> implementing garbage collection (worse bloat).
Use auto_ptr<>.
Yes, error handling is critical. That's why I want language help. Just
like I don't want to:
- calculate the carry for 64 bit addition on a 32 bit machine
- calculate structure offsets for structs (even though struct layout is
important)
I faced exactly these problems (fibre channel and ethernet cables pulled
out during I/O) and C++ made error handling easier, not more difficult.
>
>>> Does that actually make it any easier to understand the code? How
>>> does it make it more obvious to be able to write a "+" operator that
>>> allocates memory?
>>
>> Not all C++ features need to be used in the kernel. In fact, not all
>> C++ features need to be used, period. Ever tried to understand code
>> which uses overloaded operator,() (the comma operator)?
>
> The very fact that the language provides such features mean that
> people would try to get code using them into the kernel. Have you
> ever looked at all the ugly debugging macros that various people use?
> The C preprocessor provides few features at all, and yet people still
> abuse those, I don't see why C++ would be any different.
Probably not. I agree C++ is much more abusable.
BTW, under C++ preprocessor abuse drops significantly as it can be
replaced by safer and cleaner constructs.
--
Do not meddle in the internals of kernels, for they are subtle and quick to panic.
next prev parent reply other threads:[~2006-04-25 16:46 UTC|newest]
Thread overview: 200+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-04-24 19:16 Compiling C++ modules Gary Poppitz
2006-04-24 19:27 ` Greg KH
2006-04-24 20:02 ` C++ pushback Gary Poppitz
2006-04-24 20:15 ` Christoph Hellwig
2006-04-24 20:16 ` Greg KH
2006-04-24 20:18 ` Martin Mares
2006-04-24 21:36 ` Jeff V. Merkey
2006-04-24 21:28 ` J.A. Magallon
2006-04-24 21:43 ` Harald Arnesen
2006-04-24 21:52 ` Alan Cox
2006-04-24 22:16 ` J.A. Magallon
2006-04-25 0:05 ` Harald Arnesen
2006-04-25 0:46 ` Diego Calleja
2006-04-25 9:12 ` Harald Arnesen
2006-04-25 1:30 ` linux-os (Dick Johnson)
2006-04-25 2:58 ` marty fouts
2006-04-27 22:55 ` Bill Davidsen
2006-05-02 15:58 ` Randy.Dunlap
2006-05-02 20:36 ` David Schwartz
2006-04-25 8:15 ` Xavier Bestel
2006-04-25 8:42 ` Avi Kivity
2006-04-25 8:52 ` Martin Mares
2006-04-25 9:00 ` Avi Kivity
2006-04-25 9:05 ` Martin Mares
2006-04-25 9:13 ` Avi Kivity
2006-04-25 9:22 ` Xavier Bestel
2006-04-25 20:20 ` J.A. Magallon
2006-04-25 20:31 ` Barry Kelly
2006-04-25 9:09 ` Nikita Danilov
2006-04-25 20:10 ` J.A. Magallon
2006-04-25 18:02 ` Geert Uytterhoeven
2006-04-27 9:09 ` Alexander E. Patrakov
2006-04-24 22:39 ` Willy Tarreau
2006-04-24 22:57 ` Jeff V. Merkey
2006-04-24 23:02 ` David Schwartz
2006-04-25 8:55 ` Martin Mares
2006-04-25 8:59 ` Jan Engelhardt
2006-04-25 14:37 ` David Schwartz
2006-04-25 19:50 ` Martin Mares
2006-04-26 2:33 ` David Schwartz
2006-04-26 3:42 ` Matthew Frost
2006-04-26 19:25 ` David Schwartz
2006-04-26 20:01 ` Jan-Benedict Glaw
2006-04-26 20:09 ` Linus Torvalds
2006-04-26 20:19 ` Al Viro
2006-04-26 21:37 ` Sam Ravnborg
2006-04-28 9:23 ` Avi Kivity
2006-04-28 12:00 ` linux-os (Dick Johnson)
2006-04-28 12:46 ` Jan-Benedict Glaw
2006-04-26 20:25 ` Jan-Benedict Glaw
2006-04-26 20:43 ` David Schwartz
2006-04-26 23:00 ` Roman Kononov
2006-04-27 0:38 ` Kyle Moffett
2006-04-27 2:05 ` Roman Kononov
2006-04-27 3:37 ` Kyle Moffett
2006-04-27 5:37 ` Roman Kononov
2006-04-27 13:58 ` Michael Buesch
2006-04-27 14:22 ` linux-os (Dick Johnson)
2006-04-27 8:07 ` Avi Kivity
2006-04-27 13:55 ` Denis Vlasenko
2006-04-27 14:27 ` Avi Kivity
2006-04-27 14:56 ` Denis Vlasenko
2006-04-27 15:54 ` Bob Copeland
2006-04-27 16:03 ` Avi Kivity
2006-04-27 15:00 ` Martin Mares
2006-04-27 15:31 ` Avi Kivity
2006-04-27 15:38 ` Martin Mares
2006-04-28 8:16 ` Avi Kivity
2006-04-28 8:30 ` Avi Kivity
2006-04-28 15:47 ` Jan Engelhardt
2006-04-28 15:51 ` Jan Engelhardt
2006-04-28 16:51 ` Avi Kivity
2006-04-27 14:50 ` Sam Ravnborg
2006-04-27 8:50 ` Martin Mares
2006-04-27 3:57 ` Willy Tarreau
2006-04-27 5:53 ` Roman Kononov
2006-04-27 7:55 ` Jan-Benedict Glaw
2006-04-27 17:20 ` C++ pushback (when does this religious thread end?) Leonard Peterson
2006-04-30 17:48 ` C++ pushback Jan Harkes
2006-04-30 20:55 ` David Schwartz
2006-04-26 20:05 ` linux-os (Dick Johnson)
2006-04-26 20:09 ` Xavier Bestel
2006-04-26 20:44 ` Randy.Dunlap
2006-05-02 20:09 ` C++ pushback + sparse Randy.Dunlap
2006-04-27 7:49 ` C++ pushback Jiri Kosina
2006-04-26 21:05 ` Martin Mares
2006-04-25 7:33 ` Avi Kivity
2006-04-25 7:47 ` Nick Piggin
2006-05-13 16:21 ` Esben Nielsen
2006-04-24 20:36 ` Thiago Galesi
2006-04-24 21:38 ` Kurt Wall
2006-04-27 16:17 ` Roman Kononov
2006-04-27 21:59 ` Grant Coady
2006-04-27 22:09 ` Bill Davidsen
2006-04-27 23:19 ` Jan Knutar
2006-04-24 19:30 ` Compiling C++ modules Al Viro
2006-04-24 19:40 ` linux-os (Dick Johnson)
2006-04-24 20:54 ` Geert Uytterhoeven
2006-04-24 19:42 ` Alexey Dobriyan
2006-04-24 20:30 ` Daniel Barkalow
2006-04-24 20:35 ` C++ is in US [Re: Compiling C++ modules] Jiri Slaby
2006-04-24 20:45 ` Compiling C++ modules Alan Cox
2006-04-24 21:03 ` Avi Kivity
2006-04-24 21:23 ` Joshua Hudson
2006-04-24 21:29 ` Kyle Moffett
2006-04-24 21:50 ` marty fouts
2006-04-24 22:09 ` Martin Mares
2006-04-24 22:30 ` Willy Tarreau
2006-04-24 22:32 ` Joshua Hudson
2006-04-24 22:45 ` marty fouts
2006-04-25 15:32 ` Michael Buesch
2006-04-25 7:08 ` Avi Kivity
2006-04-25 10:23 ` James Courtier-Dutton
2006-04-25 15:59 ` Kyle Moffett
2006-04-25 16:46 ` Avi Kivity [this message]
2006-04-25 17:10 ` Dmitry Torokhov
2006-04-25 17:19 ` Avi Kivity
2006-04-25 17:28 ` Dmitry Torokhov
2006-04-25 17:53 ` Avi Kivity
2006-04-25 18:04 ` Dmitry Torokhov
2006-04-25 18:08 ` Valdis.Kletnieks
2006-04-25 18:26 ` Avi Kivity
2006-04-25 18:38 ` Avi Kivity
2006-04-25 18:52 ` Michael Poole
2006-04-25 19:13 ` Avi Kivity
2006-04-27 15:10 ` Denis Vlasenko
2006-04-27 20:15 ` Willy Tarreau
2006-04-27 21:08 ` Davi Arnaut
2006-04-28 9:33 ` Avi Kivity
2006-04-28 10:03 ` Avi Kivity
2006-04-28 11:27 ` Sergei Organov
2006-04-28 11:03 ` Martin Mares
2006-04-28 11:30 ` Avi Kivity
2006-04-28 15:56 ` Jan Engelhardt
2006-04-28 17:02 ` Avi Kivity
2006-04-28 17:38 ` linux-os (Dick Johnson)
2006-04-29 2:50 ` Christer Weinigel
2006-05-01 17:46 ` Dave Neuer
2006-05-01 20:21 ` Jan Engelhardt
2006-05-01 23:53 ` David Schwartz
2006-05-02 5:12 ` Willy Tarreau
2006-05-02 10:32 ` Avi Kivity
2006-05-02 11:15 ` Martin Mares
2006-05-02 11:26 ` Avi Kivity
2006-05-02 11:40 ` linux-os (Dick Johnson)
2006-05-02 12:42 ` David Woodhouse
2006-05-02 16:27 ` Christer Weinigel
2006-05-02 12:48 ` Martin Mares
2006-05-02 13:52 ` Avi Kivity
2006-05-02 14:13 ` Al Viro
2006-05-02 14:54 ` Avi Kivity
2006-05-02 16:16 ` Brian Beattie
2006-05-02 16:21 ` Avi Kivity
2006-05-02 13:21 ` Willy Tarreau
2006-05-02 14:41 ` Avi Kivity
2006-05-02 22:25 ` Diego Calleja
2006-05-02 13:34 ` Al Viro
2006-05-02 14:02 ` Avi Kivity
2006-05-02 14:34 ` Al Viro
2006-05-02 15:04 ` Avi Kivity
2006-05-02 15:15 ` Al Viro
2006-05-02 15:19 ` Avi Kivity
2006-05-02 15:27 ` Kyle Moffett
2006-05-02 15:30 ` Avi Kivity
2006-05-02 15:28 ` Al Viro
2006-05-02 15:51 ` Avi Kivity
2006-05-02 15:24 ` Kyle Moffett
2006-05-03 13:13 ` Mark Lord
2006-05-03 20:51 ` David Schwartz
2006-04-30 21:15 ` Eric W. Biederman
2006-04-25 17:54 ` linux-os (Dick Johnson)
2006-04-26 8:30 ` Jan Engelhardt
2006-04-26 11:36 ` linux-os (Dick Johnson)
2006-04-25 19:22 ` Kyle Moffett
2006-04-25 19:54 ` Michael Buesch
2006-04-25 20:24 ` Avi Kivity
2006-04-25 20:11 ` Bongani Hlope
2006-04-25 20:26 ` Avi Kivity
2006-04-25 21:02 ` Valdis.Kletnieks
2006-04-25 21:15 ` Avi Kivity
[not found] ` <71a0d6ff0604251646g4fc90b3dr30a03b8606360e7f@mail.gmail.com>
2006-04-26 4:39 ` Avi Kivity
2006-04-25 17:55 ` Geert Uytterhoeven
2006-04-24 21:58 ` Alan Cox
2006-04-25 7:20 ` Avi Kivity
2006-04-25 9:06 ` Matt Keenan
2006-04-25 20:29 ` Bongani Hlope
2006-04-25 20:37 ` Avi Kivity
2006-04-25 21:08 ` Bongani Hlope
2006-04-25 4:17 ` Martin J. Bligh
2006-04-25 5:30 ` Avi Kivity
2006-04-25 8:58 ` Sam Ravnborg
2006-04-25 7:56 ` Jakob Oestergaard
2006-04-25 9:03 ` Jan Engelhardt
2006-04-24 21:36 ` J.A. Magallon
[not found] <65eLE-GJ-21@gated-at.bofh.it>
[not found] ` <65zwH-61W-51@gated-at.bofh.it>
[not found] ` <65zZH-6Bw-23@gated-at.bofh.it>
[not found] ` <66grR-2DK-27@gated-at.bofh.it>
2006-04-28 0:03 ` Robert Hancock
-- strict thread matches above, loose matches on Subject: below --
2006-04-28 9:37 Khushil Dep
2006-05-02 18:21 Al Boldi
2006-05-02 20:28 ` J.A. Magallon
2006-05-02 23:55 ` Peter Williams
2006-05-03 8:09 ` Steven Rostedt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=444E524A.10906@argo.co.il \
--to=avi@argo.co.il \
--cc=alan@lxorguk.ukuu.org.uk \
--cc=linux-kernel@vger.kernel.org \
--cc=mrmacman_g4@mac.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox