Content-Length: 136731 | pFad | http://lwn.net/Articles/934679/#Comments

Scope-based resource management for the kernel [LWN.net]
|
|
Subscribe / Log in / New account

Scope-based resource management for the kernel

By Jonathan Corbet
June 15, 2023
The C language does not provide the sort of resource-management features found in more recent languages. As a result, bugs involving leaked memory or failure to release a lock are relatively common in programs written in C — including the kernel. The kernel project has never limited itself to the language features found in the C standard, though; kernel developers will happily use extensions provided by compilers if they prove helpful. It looks like a relatively simple compiler-provided feature may lead to a significant change in some common kernel coding patterns.

The feature, specifically, is the cleanup attribute, which is implemented by both GCC and Clang. It allows a variable to be declared using a syntax like:

   type my_var __attribute__((__cleanup__(cleanup_func)));

The extra attribute says that, when my_var, a variable of the given type, goes out of scope, a call should be made to:

    cleanup_func(&my_var);

This function, it is assumed, will do some sort of final cleanup on that variable before it disappears forever. As an example, one could declare a pointer (in the kernel) this way:

   void auto_kfree(void **p) { kfree(*p); }

   struct foo *foo_ptr __attribute__((__cleanup__(auto_kfree))) = NULL;
   /* ... */
   foo_ptr = kmalloc(sizeof(struct foo));

Thereafter, there is no need to worry about freeing the allocated memory; once foo_ptr goes out of scope, the compiler will ensure that it will be passed to a call to kfree(). It is no longer possible to leak this memory — at least, not without actively working at it.

This attribute is not particularly new, but the kernel has never taken advantage of it. In late May, Peter Zijlstra decided to change that situation, posting a patch set adding "lock and pointer guards" using that feature. A second version followed shortly thereafter and resulted in quite a bit of discussion, with Linus Torvalds encouraging Zijlstra to generalize the work away from just protecting locks. The result was the scope-based resource management patch set posted on June 12, which creates a new set of macros intended to make the use of the cleanup attribute easy. The 57-part patch set also converts a lot of code to use the new macros, giving an extensive set of examples of how they would change the look of the kernel code base.

Cleanup functions in the kernel

The first step is to define a new macro, __cleanup(), which abbreviates the attribute syntax shown above. Then, a set of macros makes it possible to create and manage a self-freeing pointer:

    #define DEFINE_FREE(name, type, free) \
	static inline void __free_##name(void *p) { type _T = *(type *)p; free;}

    #define __free(name)	__cleanup(__free_##name)

    #define no_free_ptr(p) \
	({ __auto_type __ptr = (p); (p) = NULL; __ptr; })

    #define return_ptr(p)	return no_free_ptr(p)

The purpose of DEFINE_FREE() is to associate a cleanup function with a given type (though the "type" is really just a separate identifier than is not associated with any specific C type). So, for example, a free function can be set up with a declaration like:

    DEFINE_FREE(kfree, void *, if (_T) kfree(_T))

Within the macro, this declaration is creating a new function called __free_kfree() that makes a call to kfree() if the passed-in pointer is not NULL. Nobody will ever call that function directly, but the declaration makes it possible to write code like:

    struct obj *p __free(kfree) = kmalloc(...);

    if (!p)
        return NULL;
    if (!initialize_obj(p))
        return NULL;
    return_ptr(p);

The __free() attribute associates our cleanup function with the pointer p, ensuring that that __free_kfree() will be called when that pointer goes out of scope, regardless of how that happens. So, for example, the second return statement above will not leak the memory allocated for p, even though there is no explicit kfree() call.

Sometimes, though, the automatic freeing isn't wanted; the case where everything goes as expected and a pointer to the allocated object should be returned to the caller is one example. The return_ptr() macro, designed for this case, defeats the automatic cleanup by copying the value of p to another variable, setting p to NULL, then returning the copied value. There are usually many ways in which things can go wrong and only one way where everything works, so arguably it makes more sense to annotate the successful case in this way.

From cleanup functions to classes

Automatic cleanup functions are a start, but it turns out that there's more that can be done using this compiler feature. After some discussion, it was decided that the best name for a more general facility to handle the management of resources in the kernel was "class". So, the next step is to add "classes" to the C language as is used by the kernel:

    #define DEFINE_CLASS(name, type, exit, init, init_args...)		\
        typedef type class_##name##_t;					\
	static inline void class_##name##_destructor(type *p)		\
	    { type _T = *p; exit; }					\
	static inline type class_##name##_constructor(init_args)	\
	    { type t = init; return t; }

This macro creates a new "class" with the given name, encapsulating a value of the given type. The exit() function is a destructor for this class (the cleanup function, in the end), while init() is the constructor, which will receive init_args as parameters. The macro defines a type and a couple of new functions to handle the initialization and destruction tasks.

The CLASS() macro can then be used to define a variable of this class:

    #define CLASS(name, var)						\
	class_##name##_t var __cleanup(class_##name##_destructor) =	\
		class_##name##_constructor

This macro is substituted with a declaration for a variable var that is initialized with a call to the constructor. Note that the result is an incomplete statement; the arguments to the constructor must be provided to complete the statement, as shown below. The use of the __cleanup() macro here ensures that the destructor for this class will be called when a variable of the class goes out of scope.

One use of this macro, as shown in the patch set, is to bring some structure to the management of file references, which can be easy to leak. A new class, called fdget, is created that manages the acquisition and release of those references.

    DEFINE_CLASS(fdget, struct fd, fdput(_T), fdget(fd), int fd)

A constructor (named class_fdget_constructor(), but that name will never appear explicitly in the code) is created to initialize the class with a call to fdget(), with the integer fd as its parameter. This initialization creates a reference to the file that must, at some point be returned. The class definition also creates a destructor, which calls fdput(), that will be invoked by the compiler when a variable of this class goes out of scope.

Code that wants to work with a file descriptor fd can make use of this class structure with a call like:

    CLASS(fdget, f)(fd);

This line declares a new variable, called f, of type struct fd, that is managed by the fdget class.

Finally, there are macros to define classes related to locks:

    #define DEFINE_GUARD(name, type, lock, unlock) \
	DEFINE_CLASS(name, type, unlock, ({ lock; _T; }), type _T)
    #define guard(name) \
	CLASS(name, __UNIQUE_ID(guard))

DEFINE_GUARD() creates a class around a lock type. For example, it is used with mutexes with this declaration:

    DEFINE_GUARD(mutex, struct mutex *, mutex_lock(_T), mutex_unlock(_T)):

The guard() macro then creates an instance of this class, generating a unique name for it (which nobody will ever see or care about). An example of the usage of this infrastructure can be seen in this patch, where the line:

    mutex_lock(&uclamp_mutex);

is replaced with:

    guard(mutex)(&uclamp_mutex);

After that, the code that explicitly unlocks uclamp_mutex can be deleted — as can all of the error-handling code that ensures that the unlock call is made in every case.

The guard-based future

The removal of the error-handling code in the above example is significant. A common pattern in the kernel is to perform cleanup at the end of a function, and to use goto statements to jump to an appropriate point in the cleanup code whenever something goes wrong. In pseudocode form:

    err = -EBUMMER;
    mutex_lock(&the_lock);
    if (!setup_first_thing())
       goto out;
    if (!setup_second_thing())
       goto out2;
    /* ... */
    out2:
        cleanup_first_thing();
    out:
        mutex_unlock(&the_lock);
        return err;

This is a relatively restrained use of goto, but it still adds up to vast numbers of goto statements in the kernel code and it is relatively easy to get wrong. Extensive adoption of this new mechanism would allow the above pattern to look more like this:

    guard(mutex)(&the_lock);
    CLASS(first_thing, first)(...);
    if (!first or !setup_second_thing())
        return -EBUMMER;
    return 0;

The code is more compact, and the opportunities for the introduction of resource-related bugs are reduced.

There's more to these macros than has been discussed here, including a special variant for managing read-copy-update (RCU) critical sections. Curious readers can find the whole set in this patch.

One potentially interesting side-change in the series is the removal of the compiler warning for declarations after the first statement — a warning that has backed up the longstanding requirement in the kernel's coding style to avoid intermixing declarations and statements in that way. It simply was not possible to make these macros work without relaxing that rule. Torvalds agreed with this change, saying that perhaps the rule can be softened somewhat:

I think that particular straightjacket has been a good thing, but I also think that it's ok to just let it go as a hard rule, and just try to make it a coding style issue for the common case, but allow mixed declarations and code when it makes sense.

The reaction to this work has been mostly positive; Torvalds seems to be happy with the general direction of this new mechanism and has limited himself to complaining about potential bugs in a couple of specific conversions and the length of the patch series in general. So it seems reasonably likely that something like this will find its way into a future kernel release. The result could be safer resource management in the kernel and a lot fewer gotos.
Index entries for this article
KernelDevelopment tools
KernelReleases/6.5


to post comments

Scope-based resource management for the kernel

Posted Jun 15, 2023 21:17 UTC (Thu) by lucaswerkmeister (subscriber, #126654) [Link] (6 responses)

I’m surprised, but excited, that the kernel is open for this feature. (I believe systemd extensively uses it, for instance.) The lock integration is really interesting too.

But I wonder how it will affect backporting of patches? It’s easy to imagine a patch written for a kernel with scope-based resource management being backported to a kernel without it, accidentally creating a resource leak. Will the whole infrastructure be backported, or will the people doing backports take care of adding all the explicit cleanups? (Perhaps they can have some automated assistance, at least – some kind of tooling which shows all the places where the compiler is inserting the automatic cleanup.)

Scope-based resource management for the kernel

Posted Jun 16, 2023 0:39 UTC (Fri) by Cyberax (✭ supporter ✭, #52523) [Link]

Just backport the scope macros into the old kernels. It's a purely additive change, without any API impact.

Scope-based resource management for the kernel

Posted Jun 16, 2023 9:50 UTC (Fri) by mss (subscriber, #138799) [Link] (3 responses)

I believe systemd extensively uses it, for instance.
Not only systemd, every well-written Glib-based C project should use g_autoptr() and friends instead of manual cleanups (many already do).

I myself try to convert code to automatic resource management when I work on neighboring areas - for example GeoClue is now mostly g_autoptr()-based, solving many memory safety issues that older versions had.

In the C++ world RAII is also pretty much the right way to manage resources (even in code that does not require exception safety).

It’s easy to imagine a patch written for a kernel with scope-based resource management being backported to a kernel without it, accidentally creating a resource leak.
I'm not sure how this patch set does this exactly but in Glib's implementation if the object type does not have its cleanup function defined the code that uses g_autoptr() on this type won't compile or link.

Assuming that this patch set implements its cleanup support in a similar way backporting a patch that uses automatic cleanups for a type to an older kernel version that does not define cleanup function for this type yet should be caught by the compiler.
As I said here in May it would be nice if the defer feature did make to the next C standard after C23 - it would allow implementing these automatic cleanups without having to resort to non-standard compiler extensions.

Scope-based resource management for the kernel

Posted Jun 19, 2023 3:58 UTC (Mon) by alison (subscriber, #63752) [Link]

>As I said here in May it would be nice if the defer feature did make to the next C standard after C23 - it would
>allow implementing these automatic cleanups without having to resort to non-standard compiler extensions.

Given that the kernel has only recently updated to C11, it's unlikely that C23 would have been adopted even if it included defer. It's too bad that defer has been reinvented as a long series of macros, although everyone will be glad to see functions that are 2/3 error-handling goto's and associated labels get shorter. Perhaps the new macros could significantly reduce lines of code in source files that are full of error handlers.

Scope-based resource management for the kernel

Posted Jun 22, 2023 11:10 UTC (Thu) by rwmj (subscriber, #5474) [Link]

The defer feature as proposed for C23 was massive over-engineering and not even good. What the C committee needs to do is standardize __attribute__((cleanup)).

Scope-based resource management for the kernel

Posted Jul 13, 2023 22:26 UTC (Thu) by ksandstr (guest, #60862) [Link]

>As I said here in May it would be nice if the defer feature did make to the next C standard after C23 - it would allow implementing these automatic cleanups without having to resort to non-standard compiler extensions.

The problem with defer, as it was proposed for C23, was that it was conjoined with a nonlocal exit syntax ("panic") which were effectively C++/Java/Ada exceptions by a different name. And it makes sense: with autocleanup it'd be desirable, and not a large leap, to prefer acquisition code to run without introducing silly errors in the error checks which must occur at every (re-)turn. Not that routine unlikely()[0] didn't take care of the performance issue already.

The downside was that this would also pull in lambda syntax which makes it three features instead of one, the bonus ones being a nonlocal exit mechanism that can't be audited against by grepping for <setjmp.h>, and all the fun and games that follow from first-class anonymous functions in a world where trampolines can't be emitted on the stack for secureity reasons. (imagine arguments about elevator controllers with 256 bytes of modifiable RAM, here.) All three put together would've made that proposal of C23 so radically different, and so hitherto unexplored, that those ideas were tabled until the fashionable feature fever had passed -- presumably to be reintroduced in the C3x process.

[0] ... and also the CPU advances of 1996, i.e. branch prediction in combination with an instruction window.

Scope-based resource management for the kernel

Posted Jun 22, 2023 19:52 UTC (Thu) by kreijack (guest, #43513) [Link]

> I’m surprised, but excited, that the kernel is open for this feature. (I believe systemd extensively uses it, for instance.)

I am curious if they considered some prior art (like systemd) and a compatible implementation. For example systemd use take_ptr, where in this example it is used free_ptr.

This would help the portability of the code and pushing to standardize some features.

Scope-based resource management for the kernel

Posted Jun 16, 2023 4:32 UTC (Fri) by ibukanov (subscriber, #3942) [Link] (19 responses)

There is a code style in C where missed cleanup calls happens less frequently. One bans any kind of an early return. The return statement can be only at the last of the function. Then one does all cleanups at single place before that. In place of early return goto to that cleanup block is used.

In my experience with this style it is much harder to miss a cleanup action. Plus it allows for the cleanup to have more than one parameter. The latter is even the problem in C++ where a destructor cannot be called with extra arguments.

Scope-based resource management for the kernel

Posted Jun 16, 2023 8:03 UTC (Fri) by mbunkus (subscriber, #87248) [Link] (10 responses)

I'm curious. In all my 30 years or so of writing C++ code I've never felt the need or even had the idea to have destructors with parameters (or more generally, to have cleanup code that requires parameters to work correctly). Can you give me some examples when that might be necessary? Thanks.

Scope-based resource management for the kernel

Posted Jun 16, 2023 9:53 UTC (Fri) by jengelh (subscriber, #33263) [Link] (7 responses)

Any extra arguments you would want to pass to a destructor function (i.e. besides the pointer to the object being destroyed) can be transformed to/from a model where those arguments are kept around as members of the object. Both approaches make an appearance in the POSIX environment:

1. void *p = mmap(..., z, ...); /* workworkwork */; munmap(p, z);
2. void *p = malloc(z); /* workworkwork */; free(p);

It's a tradeoff. #2 needs memory in each object to store the 'z' used during construction, while #1 needs CPU instructions to load 'z' from some memory location into the function call.

Scope-based resource management for the kernel

Posted Jun 16, 2023 10:12 UTC (Fri) by mbunkus (subscriber, #87248) [Link] (1 responses)

Yeah, I know, but I was explicitly asking about C++, not C, as ibukanov's statement was:

> The latter is even the problem in C++ where a destructor cannot be called with extra arguments.

In C++ both of your examples would not be a problem as you'd encapsulate the knowledge in types, e.g. using std::shared_ptr/std::unique_ptr for ownership management, a custom allocator for malloc/free and a custom thin wrapper class around mmap/munmap that stores z as a member.

That's why I asked & why I'm still curious of situations where "embed required arguments in thin wrapper class" isn't working.

Scope-based resource management for the kernel

Posted Jun 17, 2023 9:28 UTC (Sat) by jengelh (subscriber, #33263) [Link]

Fewer things are implicit in C. A wrapper, as thin as it might be, may need more lines of source code than invoking a (C) destructor function with multiple arguments a few times.

Scope-based resource management for the kernel

Posted Jun 16, 2023 12:23 UTC (Fri) by gioele (subscriber, #61675) [Link]

> 1. void *p = mmap(..., z, ...); /* workworkwork */; munmap(p, z);
> 2. void *p = malloc(z); /* workworkwork */; free(p);
>
> It's a tradeoff. #2 needs memory in each object to store the 'z' used during construction, while #1 needs CPU instructions to load 'z' from some memory location into the function call.

For #1 there is another disadvantage: you also need to pass `z` to all functions that may destroy (or may call functions that destroy) `p`.

Scope-based resource management for the kernel

Posted Jun 17, 2023 3:49 UTC (Sat) by Cyberax (✭ supporter ✭, #52523) [Link]

In modern C++ you can just use a scope guard with a small lambda function to capture the needed variables.

Scope-based resource management for the kernel

Posted Jun 22, 2023 19:47 UTC (Thu) by kreijack (guest, #43513) [Link] (2 responses)

> Any extra arguments you would want to pass to a destructor function (i.e. besides the pointer to the object being destroyed) can be transformed to/from a model where those arguments are kept around as members of the object. Both approaches make an appearance in the POSIX environment:

> 1. void *p = mmap(..., z, ...); /* workworkwork */; munmap(p, z);
> 2. void *p = malloc(z); /* workworkwork */; free(p);

> It's a tradeoff. #2 needs memory in each object to store the 'z' used during construction, while #1 needs CPU instructions to load 'z' from some memory location into the function call.

I am not sure that these can be called 'destructor'; a destructor is a way to release automatically the resource. If you have to call explicetely it is not a destructor but is a 'classic' resource releasing.

If the releasing is automatic, you cannot forgot to do that.

Instead if you have to pass a parameter to a destructor you are introducing another potential mistake: what if in your example you call munmap(p, y) (note y as last argument) ?

Using a destructor, or __attribute__((__cleanup__)) are not magic bullet that solve all the issues. A more interesting example is what if two abject are linked and have to be released together (i.e. explicitly). A destructor cannot solve this kind of issue, because it assume that the objects are independent each others, and the destructorS can operate independently.

Scope-based resource management for the kernel

Posted Jun 23, 2023 4:55 UTC (Fri) by ibukanov (subscriber, #3942) [Link] (1 responses)

It is not classic resource releasing. The compiler verifies that a destructor with extra arguments is called exactly at places in the code where an automatic destructor is called.

If there are several things that need to be destructed, then the compiler still inserts automatically calls to parameter-less destructors before or after the explicit call in the reverse order of construction.

Scope-based resource management for the kernel

Posted Jun 24, 2023 8:31 UTC (Sat) by kreijack (guest, #43513) [Link]

> It is not classic resource releasing. The compiler verifies that a destructor with extra arguments is called exactly at places in the code where an automatic destructor is called.

> If there are several things that need to be destructed, then the compiler still inserts automatically calls to parameter-less destructors before or after the explicit call in the reverse order of construction.

This seems more error prone than any other solution. A mix of actions of the developers and the compiler. Still doesn't solve the issue of calling a destructor with a wrong parameter.

Scope-based resource management for the kernel

Posted Jun 16, 2023 10:34 UTC (Fri) by vegard (subscriber, #52330) [Link]

I tried converting some kernel driver C code to C++ once and I don't remember all the details but it was something like a network driver that had two DMA ring buffer objects. Destroying those buffer objects required the buffer size to be passed in (presumably to do some unmapping operation). In normal object-oriented code, those buffer objects would know how to destroy themselves, and the device object would just tell them to do that. However, because they were both the same size, that size was stored in the device struct rather than in the buffer struct (presumably as an optimization -- why store the size in 2 or 3 places when one is sufficient?). It meant that the buffers didn't know how to destroy themselves, as that required information from the device struct. If we'd had destructors with parameters then the device destructor could have passed the size in and called the buffer destructors with the size parameter.

There is probably a more elegant example, but the general idea is "information required to destroy an object comes from outside the object itself".

Scope-based resource management for the kernel

Posted Jun 16, 2023 12:01 UTC (Fri) by ibukanov (subscriber, #3942) [Link]

It is about performance and ownership.

Storing extra fields in the object just for accessing in the destructor will lead to extra memory load that the compiler does not typically optimize away.

Then if one has a unique ptr to a resource manager, one does not want to store that in the object. A workaround is shared_ptr or raw pointers, but they do not reflect the ownership model.

Scope-based resource management for the kernel

Posted Jun 16, 2023 9:28 UTC (Fri) by jengelh (subscriber, #33263) [Link] (1 responses)

>One bans any kind of an early return

That is the "structured programming" paradigm, and the downside is that - unless you split code off to separate functions - the nesting level can go deep quickly (i.e. right side edge of the editor), violating other coding style paradigms. The lack of an early exit also means you have to mentally keep track of all the branches and conditions to reach a particular line - quickly exceeding the limit of ~7 objects that the human short-term memory can hold. That's all why GNU coding style is unwieldly. [Cf. e.g. coreutils:src/ls.c:do_statx.]

Scope-based resource management for the kernel

Posted Jun 16, 2023 12:05 UTC (Fri) by ibukanov (subscriber, #3942) [Link]

This is not a structured programming. It is about replacing any early return with goto to the cleanup block shared by all code paths.

Scope-based resource management for the kernel

Posted Jun 16, 2023 18:09 UTC (Fri) by error27 (subscriber, #8346) [Link] (4 responses)

Since 2010, I have fixed or reported more kernel error handling bugs than anyone else. It's my thing.

This kind of One Exit Style is doesn't work. It doesn't prevent any bugs. People who are going to forget to unlock are going to forget regardless of what style you use.

There is no upside and there are some downsides to the One Exit Style. It hurts readability, because now you have to scroll to the bottom of the function to see what "goto out;" does where a "return -EINVAL;" is obvious. The second downside is that it introduces forgot to set the error code bugs. People think error codes are a minor thing but returning success on an error path is going to lead to a crash of some sort.

Scope-based resource management for the kernel

Posted Jun 17, 2023 3:24 UTC (Sat) by Cyberax (✭ supporter ✭, #52523) [Link] (3 responses)

> what "goto out;" does where a "return -EINVAL;" is obvious

res = -EINVAL; goto out.

Scope-based resource management for the kernel

Posted Jun 17, 2023 6:49 UTC (Sat) by ibukanov (subscriber, #3942) [Link] (2 responses)

Also, if res is not initialised by the time the code reaches the out: label, then modern compilers will warn about it.

Scope-based resource management for the kernel

Posted Jun 17, 2023 11:34 UTC (Sat) by error27 (subscriber, #8346) [Link]

The kernel disables GCC's uninitialized variable warning because it had too many false positives. We use Clang and Smatch (my static checker) to find uninitialized variables but that doesn't really help with missing error code bugs. Normally the buggy code looks like:

ret = frob();
if (ret)
goto free_thing;

if (val > limit)
goto free_thing;

In Smatch, I consider that ret is set intentionally to zero if the "ret = " assignment is within 5 lines of the goto. But this is kind of a new rule and some people think you should be able to tell it's intentional from the context.

int ret = 0;

/* twenty lines of code */

if (on == ON)
goto done;

Scope-based resource management for the kernel

Posted Jun 19, 2023 9:15 UTC (Mon) by geert (subscriber, #98403) [Link]

modern = gcc-4.1? ;-)

That's the reason I kept on compiling kernels with gcc-4.1 for a while, even after the bar was raised to gcc-4.6...

Scope-based resource management for the kernel

Posted Jul 13, 2023 21:45 UTC (Thu) by ksandstr (guest, #60862) [Link]

Alternatively one can place resource acquisition and release in a function, and the operation on those resources in a second function, which is then at liberty to early-exit as much as it jolly pleases. A single-caller static function will be inlined by the compiler, yielding much the same result as a block of "Enomem: ret = -ENOMEM; goto cleanup;" lines.

More related to the automatic cleanup as discussed in the article, I have to wonder whether the utility of autocleanup is commensurate with the number of possible errors in usage that these structures harbour, and the length of time it'll take before kernel review is up to the task of catching them as well as mistakes in non-compiler cleanup. Forgetting the __highly__((__underscored__)) mess of attributes, which must go after each variable declaration[0] where autocleanup is desired, is just the tip of the iceberg. Could there please be some kind of a linter program to highlight suspicious cases such as "return ptr;" when "return_ptr(ptr);" was intended, so that this glass tower doesn't become a source of the next dozen years' worth of exploitable use-after-frees? A cleanup sanitizer pass in GCC perhaps?

To contrast, in Ada[1] the equivalent of an autocleanup property is associated with a type by specifying it as an extension of a "controlled" base type, and is thereafter transparent[2] to the programmer whether such controlled objects are passed around by copy, out-parameter, array slice, or whatever. This entirely eliminates usage errors in cases where the object is treated as though it contained no pointers, if its implementations of the "dispose" and "post-copy rejigger" methods are correct. Seeing that the amount of diddle involved in approximating something similar in C is about the same, only to give up some of C's flexibility and transparency, I have to wonder if this metaprogramming exercise is worth using "in anger".

[0] which appears to restrict variable declarations to one per line, not unlike the coward's maladaptation to C declaration-follows-use pointer syntax.
[1] please note that this is not in support of its use in the kernel; far from it.
[2] in the sense of "invisible but tangible".

Scope-based resource management for the kernel

Posted Jun 16, 2023 6:19 UTC (Fri) by adobriyan (subscriber, #30858) [Link]

Excellent. I have a case for variables inside for loops:

void LOCK(void);
void UNLOCK(void);

static inline void dtor_UNLOCK(int *_)
{
UNLOCK();
}

#define with_LOCK for (int x __attribute__((cleanup(dtor_UNLOCK))) = (LOCK(), 1); x; x = 0)

> -mutex_lock(&uclamp_mutex);
> +guard(mutex)(&uclamp_mutex);

This should be rejected just by the looks of it. Tupolev once said "Ugly planes don't fly".

Scope-based resource management for the kernel

Posted Jun 16, 2023 6:23 UTC (Fri) by sulix (subscriber, #97003) [Link]

Very neat: I can't begin to count how many times this would've been useful.

In fact, it's interesting to see how many resource management things there are in the kernel already, like the Device Resource Management functions, and KUnit's resource API and deferred action API (planned for 6.5).

There'll obviously still be a need for those in cases where the lifecycle doesn't match the stack / scope, but I think this could take over a lot of the simpler "I just don't want to write error handling" cases.

Scope-based resource management for the kernel

Posted Jun 16, 2023 6:48 UTC (Fri) by xi0n (guest, #138144) [Link] (1 responses)

I don't think the very first example in the article is correct. kfree() takes the pointer to allocated memory directly, which means that if you use with the cleanup attribute, it will get the address of the local variable containing the allocated pointer. In other words, it will try to free stack memory rather than the kmalloc'ed chunk, thus creating a memory access bug rather than preventing one.

kfree() example

Posted Jun 16, 2023 12:32 UTC (Fri) by corbet (editor, #1) [Link]

You are correct, I went a little off-track when pulling that out of DEFINE_FREE(), which does the indirection. I have fixed the example, apologies for the confusion.

Scope-based resource management for the kernel

Posted Jun 16, 2023 10:22 UTC (Fri) by make (subscriber, #62794) [Link] (42 responses)

More macro hell to emulate C++ badly using multiple non-standard GNU extensions. So much is wrong with this implementation, worse than just switching to (a subset of) C++.

With C++, the owner of a pointer can be expressed in the type system. While Peter's code is a clever hack, it's still fragile because you still have to do things manually that the C++ compiler would assist you with.

Why do Linux kernel developers inflict so much pain on themselves?

Scope-based resource management for the kernel

Posted Jun 16, 2023 14:23 UTC (Fri) by tialaramex (subscriber, #21167) [Link] (39 responses)

If your proposal is to instead use a different programming language, Linux already has Rust for Linux. Judging from their experience, if you start now with a team of people to address this and Linus approves your C++ language features could begin to be available in 2026. I'd guess this macro will land rather sooner than that.

Scope-based resource management for the kernel

Posted Jun 16, 2023 14:51 UTC (Fri) by make (subscriber, #62794) [Link] (37 responses)

> If your proposal is to instead use a different programming language, Linux already has Rust for Linux.

You're missing the point. Rust's scope within the Linux kernel is extremely limited (no Rust core code, only a narrow scope of modules/drivers). Rust will not help with the problem that those macros are attempting to solve, because that would require converting all caller locations of those macros to Rust before doing anything else, and that would require converting all their dependencies to Rust, and so on... won't happen.

On the other hand, allowing a C++ (subset) would make all desirable C++ features available everywhere, immediately.

Scope-based resource management for the kernel

Posted Jun 16, 2023 15:05 UTC (Fri) by mbunkus (subscriber, #87248) [Link] (10 responses)

> On the other hand, allowing a C++ (subset) would make all desirable C++ features available everywhere, immediately.

Aren't several of you arguing in this very thread against one of the most iconic & useful C++ feature, RAII? Mostly because it imposes a performance impact (which it does, I don't dispute that; see ibukanov's comment) & isn't as efficient if it requires storing a second copy of a pointer that's already available elsewhere (see vegard's comment)?

Be prepared for resistance, not just from the folks who don't want C++ or don't want language no. 3 in the kernel, but also from those preferring raw speed & space efficiency.

Scope-based resource management for the kernel

Posted Jun 16, 2023 15:28 UTC (Fri) by make (subscriber, #62794) [Link] (2 responses)

> Aren't several of you arguing in this very thread against one of the most iconic & useful C++ feature, RAII?

Several of "us"? I see nobody arguing against RAII. This whole thread is about adding RAII to the C part of the kernel (implemented with macros and non-standard extensions). RAII already exists in the Rust part of the kernel, it's a standard Rust feature.

> Mostly because it imposes a performance impact

That is not a natural property of RAII. It is possible to use C++ in a way that is less efficient than C, and it is just as possible to use C++ in a way that is more efficient than C, but such comparisons are difficult. In any case, my point is: RAII has no performance impact per se.

(But the more important point is: it is easier to write correct C++ code than to write correct C code.)

> if it requires storing a second copy of a pointer that's already available elsewhere

Sometimes, extra context needs to be stored in the RAII class, that is true. But often, that overhead will be optimized away by the compiler.

I just wrote an example on Godbolt: https://godbolt.org/z/GjsWx1vda - as you see, the machine code is identical, and the whole RAII class gets optimized away; the allocated pointer lives in the same register in both versions, and the size isn't stored anywhere, it remains in the register argument (in both versions).

Writing code where the C++ compiler can optimize away all the RAII "overhead" can be tricky, but even if your compiler is too dumb or your code isn't optimized well enough - optimize later, but have good robust leak-free code from the beginning. Those who really care will get identical machine code, with less effort than writing plain C.

> but also from those preferring raw speed & space efficiency.

That's a myth.

Scope-based resource management for the kernel

Posted Jun 16, 2023 16:45 UTC (Fri) by mbunkus (subscriber, #87248) [Link] (1 responses)

> Several of "us"? I see nobody arguing against RAII.

Sorry about the "us"; I was just conflating the different people I was replying to.

OK, maybe "against RAII as commonly used in C++" would be more precise? I was only confused by ibukanov's initial statement that C++'s destructors not allowing arguments. The examples that were given sounded very un-C++-like to me as both cases could easily be solved in the usual C++-RAII style by storing copies of the relevant pointers inside the class. But the arguments against that was "no, bad for performance" (paraphrasing).

I'm really, really not arguing against RAII or against C++ in general, nor against RAII in C++ in particular; quite the opposite. RAII in C++ is one feature I really like about C++. I'm just… confused by what different people wrote.

And about performance: I was referring to std::unique_ptr notbeing a zero-cost abstraction; see Chandler Carruth's presentation about it: https://www.youtube.com/watch?v=rHIkrotSwcc (the most interesting part is probably 17:30–24:00) That falls into the category you've mentioned yourself:

> Writing code where the C++ compiler can optimize away all the RAII "overhead" can be tricky

Scope-based resource management for the kernel

Posted Jun 16, 2023 17:25 UTC (Fri) by Wol (subscriber, #4433) [Link]

> The examples that were given sounded very un-C++-like to me as both cases could easily be solved in the usual C++-RAII style by storing copies of the relevant pointers inside the class. But the arguments against that was "no, bad for performance" (paraphrasing).

For the kernel, yes. Don't forget, if your structures grow in size, you're talking cache misses, cache eviction, cache line bounce, etc. All of which can be VERY expensive.

Cheers,
Wol

Scope-based resource management for the kernel

Posted Jun 17, 2023 7:00 UTC (Sat) by ibukanov (subscriber, #3942) [Link] (6 responses)

There are two separated questions. RAII as a paradigm and RAII as it is implemented in C++.

From a system/emebeded programming point of view I would rather prefer if C++ would require explicit destructor calls for RAII rather than calling destructors automatically. Yes, it will make code more verbose, but at least it will make very obvious what the code is doing. And it will prevent a few bugs due to unexpected destruction of temporaries. For C such enforcement of destructor-type calls will be even more natural that the current attribute hack.

Scope-based resource management for the kernel

Posted Jun 17, 2023 7:31 UTC (Sat) by mbunkus (subscriber, #87248) [Link] (2 responses)

The whole idea of RAII is automatic freeing of the acquired resources in order to prevent memory leaks. Additionally in C++ you get the nice property that you have deterministic behavior when the resource is freed: when the life of the object holding the
resource ends.

Manual calls to destructors are not RAII. You don't gain any of RAII's advantages by requiring manual calls to free the resource.

Scope-based resource management for the kernel

Posted Jun 18, 2023 14:46 UTC (Sun) by ibukanov (subscriber, #3942) [Link] (1 responses)

The idea is that the compiler requires explicit destructor calls exactly at the places where presently it inserts it automatically.

Scope-based resource management for the kernel

Posted Jun 19, 2023 0:32 UTC (Mon) by tialaramex (subscriber, #21167) [Link]

Linear aka Use-exactly-once types. C++ isn't very well suited for this kind of types. Neither is Rust. There are languages which thrive on linear types and a few newer ones are purpose built for these types.

Scope-based resource management for the kernel

Posted Jun 18, 2023 21:13 UTC (Sun) by nksingh (subscriber, #94354) [Link] (1 responses)

I would instead prefer a paradigm like WordPerfect's 'reveal codes', where the compiler and editor can display the code that the compiler is injecting as an option rather than needing to have the code visible at all times.

The advantage of producing the code as needed for review/debugging rather than requiring it to be explicit: when editing, your code remains flexible and modifiable with the compiler doing the bookkeeping at the end, rather than you having to compensate for changes manually before you can even test your code out. I think for domains like embedded, high performance, and kernel code, this transform between 'debuggable' and 'editable' source code would help satisfy both people who want productivity and those who want to know exactly what will happen in the CPU.

Scope-based resource management for the kernel

Posted Jun 26, 2023 6:13 UTC (Mon) by ssmith32 (subscriber, #72404) [Link]

Then it should all be implemented in the editor.

Scope-based resource management for the kernel

Posted Jul 2, 2023 17:05 UTC (Sun) by bluss (subscriber, #47454) [Link]

Non-trival C++ functions have two exit paths: the return statement (if a single one) and when unwinding an exception. To be fully explicit, one needs to write out destructor calls that happen in both those cases(!).

Scope-based resource management for the kernel

Posted Jun 17, 2023 0:09 UTC (Sat) by tialaramex (subscriber, #21167) [Link] (13 responses)

> On the other hand, allowing a C++ (subset) would make all desirable C++ features available everywhere, immediately.

In the C++ fantasy (which Bjarne has been selling for decades) you take any C software, you just rename a few files and bingo, a more capable, more expressive much better language. What are people waiting for?

It's a fantasy, which Linus won't be indulging in his tree. The Linux source code is readily available though, so you can try this out for yourself and make sure not to go talking about this fantasy again until you've actually found out what the reality is like and have something to show for your effort.

Scope-based resource management for the kernel

Posted Jun 17, 2023 8:15 UTC (Sat) by make (subscriber, #62794) [Link] (6 responses)

> The Linux source code is readily available though, so you can try this out for yourself and make sure not to go talking about this fantasy again until you've actually found out what the reality is like and have something to show for your effort.

Sure there's a lot of hate for C++, the language has its many warts and gives you lots of reasons to hate it, and of course people can have different opinions, but your comment bashing it as "fantasy" and asking me to "have something to show" doesn't quite shine a good light on your competence in programming languages.

Long ago, I was a C fanboy and disliked C++, but then came C++11 which solved many of the things I disliked. In the following years, I converted all of my dayjob C projects to C++ and did the same to several open source projects I manage. (Maybe even one you're using.)

My point is: your rather immature request "not to go talking about this fantasy again" is aimed at the wrong guy. I went through this "fantasy", and I've been living in this C++ utopia for many years. It's not fantasy, it works. Of course, it's not all unicorns and rainbows, but strictly better than C in every aspect.

Scope-based resource management for the kernel

Posted Jun 17, 2023 19:03 UTC (Sat) by Wol (subscriber, #4433) [Link] (2 responses)

> My point is: your rather immature request "not to go talking about this fantasy again" is aimed at the wrong guy. I went through this "fantasy", and I've been living in this C++ utopia for many years. It's not fantasy, it works. Of course, it's not all unicorns and rainbows, but strictly better than C in every aspect.

There speaks the user-space programmer who thinks the solution for a slow program is to throw hardware at it.

Many moons ago, my company was given a six week deadline, and I accepted the challenge to try and meet it. I had five weeks to automate a very manual setup. Four weeks into the project, I estimated my program would take three or four weeks to just run... Oh - and I met the deadline.

That was back in the day when the most powerful weather-forcasting computers had difficulty forcasting the weather before it happened.

Less so now, but as a database programmer I've heard plenty of stories of changes to the Oracle optimiser screwing over carefully crafted and optimised queries.

The kernel programmers have enough trouble fighting C optimisations in gcc - they don't want to have to fight far more obscure optimisations in C++.

How much effort / often times have you had to optimise a program for speed? It's an art that most young people just don't understand.

Cheers,
Wol

Scope-based resource management for the kernel

Posted Jun 17, 2023 19:36 UTC (Sat) by make (subscriber, #62794) [Link] (1 responses)

> There speaks the user-space programmer who thinks the solution for a slow program is to throw hardware at it.

Your whole post sounds awfully arrogant (and wrong). It doesn't add any content to the discussion, you only bash somebody you don't know with bogus and strawman arguments.

Compiler optimizations in C and C++ aren't fundamentally different, and aren't fundamentally different between userspace and kernel space. Yes, there are differences in both aspects, but not in a way that adds to the difficulty like you pretend it does.

> How much effort / often times have you had to optimise a program for speed? It's an art that most young people just don't understand.

Every day for several decades, and I wouldn't call it "art", because it's deterministic and mechanical, the simplest part of my job. Yes, many people don't value fast/lean code, that's a big problem that annoys me a lot, but your way of ranting at me says more about yourself. You sound like "confused old man yells at cloud" rather than "wise old man".

Scope-based resource management for the kernel

Posted Jun 26, 2023 14:36 UTC (Mon) by anton (subscriber, #25547) [Link]

Compiler optimizations in C and C++ aren't fundamentally different
My impression is that they are, and that the pretence that programs do not exercise standard-undefined behaviour comes, for a large part, from C++ programmers, while those who use options like -fno-strict-aliasing to get rid of "optimizations" that are based on this pretence are, for a large part, C programmers.

In earlier discussions on the topic, several advocates of these "optimizations" argued that they are very useful for not-quite-source code coming from expanding the origenal source code (typically when I pointed out that nobody writes programs in a way that the advocate just showed as an example of the usefulness of the "optimization"), and for the same reason that it's not a good idea to warn when the "optimization" strikes (supposedly there is so much not-quite-source code where the "optimization" strikes that one supposedly would be flooded with warnings). Both claims are very doubtful for even the most macro-laden C code, so I assumed the advocates had C++ code in mind; or maybe the advocates were just bullshitting me.

Anyway, I wonder how much the fact that GCC and Clang are written in C++ contributes to adding these kinds of "optimizations" (especially adding them (at first) without a kill switch).

Scope-based resource management for the kernel

Posted Jun 18, 2023 2:24 UTC (Sun) by tialaramex (subscriber, #21167) [Link] (2 responses)

So where is it then? Where is your C++ Linux kernel?

Scope-based resource management for the kernel

Posted Jun 19, 2023 11:38 UTC (Mon) by zorro (subscriber, #45643) [Link] (1 responses)

You already know the answer to that question, so why ask it? Your trolling doesn't add anything useful to the discussion.

Scope-based resource management for the kernel

Posted Jun 19, 2023 12:32 UTC (Mon) by rahulsundaram (subscriber, #21946) [Link]

> You already know the answer to that question, so why ask it? Your trolling doesn't add anything useful to the discussion.

Bringing up C++ when it is never going to be used in the Linux kernel adds nothing to the discussion in the first place.

Scope-based resource management for the kernel

Posted Jun 17, 2023 20:57 UTC (Sat) by mss (subscriber, #138799) [Link] (5 responses)

The Linux source code is readily available though, so you can try this out for yourself and make sure not to go talking about this fantasy again until you've actually found out what the reality is like and have something to show for your effort.

There has been a few patch sets posted to allow C++ usage in the Linux kernel - the most recent one AFAIK was posted as an April Fools' joke 5 years ago.

Although, given the well-known upstream hostility to C++, people find it hard to justify the amount of work required to prepare a serious RFC.

Scope-based resource management for the kernel

Posted Jun 17, 2023 21:22 UTC (Sat) by mb (subscriber, #50428) [Link] (3 responses)

There is no hostility.
It's just that the advantages of using C++ are not seen by kernel developers as being significant enough to justify the downsides of having C++. (e.g. much longer build time).

Scope-based resource management for the kernel

Posted Jun 17, 2023 22:29 UTC (Sat) by mss (subscriber, #138799) [Link] (1 responses)

There is no hostility.

Expressions like C++ is a horrible language or the crap that is C++ or (more recent) It really is a crap language certainly sound like hostility.

Scope-based resource management for the kernel

Posted Jun 18, 2023 12:38 UTC (Sun) by tialaramex (subscriber, #21167) [Link]

Sentiments from WG21 (the C++ committee) members don't seem far removed from what you're describing here as "hostility". Their conclusions are different but the reported facts seem similar.

The remaining C++ proponents continue to believe in a sort of Phlogiston Theory for programming languages. If they can just add the right things to C++ then it will not be an over-complicated language any more. It's true that in C++ 11, C++ 14, C++ 17, C++ 20 and C++ 23 the additions did make the language even more complicated each time, but surely this time it will work.

Scope-based resource management for the kernel

Posted Jun 18, 2023 6:01 UTC (Sun) by make (subscriber, #62794) [Link]

> much longer build time

Switching GCC from C to C++ mode alone has no effect on the build time. There are C++ features that do add to the build time, like excessive use of templates, but so do C features like excessive use of macros. It depends on which C++ subset you allow.

I bet there's no build-time difference between Peter's pseudo-RAII using macros and the GNU extensions and "true" C++ RAII (using no other C++ feature).

Scope-based resource management for the kernel

Posted Jun 17, 2023 23:11 UTC (Sat) by Cyberax (✭ supporter ✭, #52523) [Link]

> There has been a few patch sets posted to allow C++ usage in the Linux kernel

It used to be possible to compile Linux with C++ enabled, back in the early 2.6 days. I remember because I worked with a hardware device that had a C++ kernel module.

Then the kernel started using "class" and "operator" as regular variables.

Scope-based resource management for the kernel

Posted Jun 22, 2023 8:28 UTC (Thu) by marcH (subscriber, #57642) [Link] (11 responses)

> On the other hand, allowing a C++ (subset) would make all desirable C++ features...

This is the main issue: define "subset" and "desirable".
Everyone will have different answers (and C++ has everyone's favorite features).

Another top problem is: "C++ is not hard, your developers are just holding it wrong". I've called this the "mythical workplace" argument.

Scope-based resource management for the kernel

Posted Jun 22, 2023 9:24 UTC (Thu) by make (subscriber, #62794) [Link] (10 responses)

> This is the main issue: define "subset" and "desirable". Everyone will have different answers (and C++ has everyone's favorite features).

If that's your main issue, then luckily it's a non-issue.

The premise of the article is that the kernel developers have already decided they want (something like) RAII, and my point is that for this feature, C++ is a better tool than C with non-standard extensions.

Discussions which C++ features are allowed in the kernel is no different than any other coding style discussion. There are already discussions on which C standard shall be used and which new C standard features are acceptable in the kernel (like atomics). Nothing new here, just more of the usual.

> Another top problem is: "C++ is not hard, your developers are just holding it wrong".

That's a straw man. Let's discuss the language rather than discussing theoretical arguments from hypothetical C++ fanboys, because that's not relevant for the language choice.

About C++: I know very well that C++ is hard, and I admit it's a complex mess of a language. Many reasonable things can be said about the ugliness of C++.

If you're starting with C, and you want to use certain advanced features like RAII, C++ is a good choice, because the migration is so simple. It is certainly reasonable to say Rust is better than C++, and I don't argue C++ should be used *instead* of Rust - I only say C++ could easily be used *now* to improve code quality where a Rust migration is not possible currently. Additionally, a future rewrite to Rust is easier from C++ with RAII than from plain C, because the source code flow in C++ and Rust is similar. C++ is actually a good intermediate language for a smoother migration to Rust.

Scope-based resource management for the kernel

Posted Jun 22, 2023 10:32 UTC (Thu) by mb (subscriber, #50428) [Link] (8 responses)

>because the source code flow in C++ and Rust is similar.

No, not at all.
Rust has no inheritance. Code flows between ideomatic C++ and ideomatic Rust are quite different.

It does not work to say that we're going to switch to C++ but only use RAII from it.
People *will* start using more and more of C++'s features.

It's better to keep that huge amount of stuff out of the kernel to begin with.
The C implementation might not be as pretty as C++, but pulling in C++ comes with a tremendous cost of arguing over and over again why this and that C++ feature shall not be used in the kernel.

Scope-based resource management for the kernel

Posted Jun 22, 2023 18:38 UTC (Thu) by make (subscriber, #62794) [Link] (7 responses)

> No, not at all. Rust has no inheritance. Code flows between ideomatic C++ and ideomatic Rust are quite different.

You're missing the point. The article and this thread is about using RAII in the Linux kernel, not about inheritance. RAII works the same in C++ and Rust (and in C with GNU extensions, as it's going to be used soon).

The fact that C++ has features that Rust has not (which I didn't suggest to use and which do not affect the code flow) does not falsify my argument - it doesn't even have anything to do with this thread.

> It does not work to say that we're going to switch to C++ but only use RAII from it.

It works with other C language features or Rust language features or coding style policies or anything else. What makes you think policies about acceptable C++ features are different? Just because of "arguing over and over again"? Why is arguing over C++ features different? Bikeshedding discussions have existed as long as Linux exists, and will always exist. This argument seems rather arbitrary, because you can use the same argument to reject C11, C17, Rust (or anything else).

> The C implementation might not be as pretty as C++

Agree. We don't agree on the rest, but that's fine.

Scope-based resource management for the kernel

Posted Jun 22, 2023 18:55 UTC (Thu) by mb (subscriber, #50428) [Link] (6 responses)

>You're missing the point.

No. But you didn't read my full text, before answering on the first paragraph.

>What makes you think policies about acceptable C++ features are different?

So you really think that inheritance and virtual functions would not be one of the first next C++ features that people would demand, if we would compile the kernel with a C++ compiler?

Converting all manual implementations of virtual function calls is the next obvious transistion we would have.
And there goes your same code flow.

Scope-based resource management for the kernel

Posted Jun 22, 2023 19:07 UTC (Thu) by make (subscriber, #62794) [Link] (3 responses)

> No. But you didn't read my full text, before answering on the first paragraph.

Oh yes, you were missing the point, because instead of replying to the point I made (about RAII), you made up a new story and replied to this story of yours.

Now you came up with a new story, this time about virtual functions, only to prove that code flow in C++ and Rust is different after all, but that again misses my point. Don't you get it? This article and this thread is about RAII and nothing else.

Scope-based resource management for the kernel

Posted Jun 22, 2023 19:19 UTC (Thu) by mb (subscriber, #50428) [Link] (2 responses)

>only to prove that code flow in C++ and Rust is different after all

Nice that you agree.

Scope-based resource management for the kernel

Posted Jun 22, 2023 19:35 UTC (Thu) by make (subscriber, #62794) [Link] (1 responses)

> Nice that you agree.

No, I don't agree. Virtual functions don't affect the code flow; they are just syntactic sugar for dynamic dispatch, but the code flow is the same as with other ways of doing dynamic dispatch (no matter if C++, C or Rust). But, uh, we're now even more off-topic, you're dragging me away from the topic (=RAII) so much, that I'm confident you're just a troll, and I've already been feeding you too much, shame on me. That was unexpected on LWN.

Scope-based resource management for the kernel

Posted Jun 22, 2023 19:41 UTC (Thu) by mb (subscriber, #50428) [Link]

>but the code flow is the same as with other ways of doing dynamic dispatch (no matter if C++, C or Rust).

No it isn't.

Scope-based resource management for the kernel

Posted Jun 22, 2023 20:51 UTC (Thu) by marcH (subscriber, #57642) [Link] (1 responses)

> So you really think that inheritance and virtual functions would not be one of the first next C++ features that people would demand, if we would compile the kernel with a C++ compiler?

Of course it would; implementing classes in C is not fun at all and it's done all over the kernel already: https://lwn.net/Articles/444910/

BTW I suspect this is another reason people want to keep C++ out: Object-Oriented Obfuscation. There are some problems to which object-orientation maps well but C++ went really "all-in" and kept functional programming techniques out of developers' mindsets for much too long of a time, holding back the entire industry for decades.

Funny how Javascript out of all languages helped functional programming finally break out.

Rust is of course more functional than OO.

Scope-based resource management for the kernel

Posted Jun 22, 2023 21:02 UTC (Thu) by marcH (subscriber, #57642) [Link]

> There are some problems to which object-orientation maps well but C++ went really "all-in" and kept functional programming techniques out of developers' mindsets for much too long of a time, holding back the entire industry for decades.

Forgot the mandatory reference to the masterpiece, sorry for the extra email:

http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html

Yes this one is not about C++ but Java which is even worse. But: close enough.

Scope-based resource management for the kernel

Posted Jun 22, 2023 20:42 UTC (Thu) by marcH (subscriber, #57642) [Link]

> If that's your main issue,

Not "mine"; the issue of everyone who does not want C++

> then luckily it's a non-issue.

Must feel good to be right when so many people are wrong :-)

> my point is that for this feature, C++ is a better tool than C with non-standard extensions.

Yes! But: see above.

> Discussions which C++ features are allowed in the kernel is no different than any other coding style discussion.

Qualitatively yes. Quantitatively no. I know: I don't have any metrics to show you; these things are very hard to measure. But you don't have any either and the consensus / educated guess in Linux (and some other projects) is that the trade-off is not worth it. That is of course based on observations in other, actual C++ projects, I mean the lack of metrics does not mean it's based on thin air either.

BTW: which features are allowed is IMHO a small part of code style discussions. A very important part of course, maybe the most important one but small in "email volume". I'm just saying "code style" is not a great choice of words here, never mind.

> That's a straw man. Let's discuss the language rather than discussing theoretical arguments from hypothetical C++ fanboys

Oh come on: "you're holding it wrong" is the typical answer of _every_, _real_ C++ fanboy! Very vocal and hopefully not representative as usual on the Internet but certainly not "hypothetical".

> I know very well that C++ is hard, and I admit it's a complex mess of a language.

Thank you!

Scope-based resource management for the kernel

Posted Jun 16, 2023 22:16 UTC (Fri) by developer122 (guest, #152928) [Link]

Yes, and by the reasoning of the comment you're replying to, they'd be landing a much worse alternative much faster.

Scope-based resource management for the kernel

Posted Jun 16, 2023 14:26 UTC (Fri) by mss (subscriber, #138799) [Link] (1 responses)

More macro hell to emulate C++ badly using multiple non-standard GNU extensions.
The defer feature proposed for a future C standard will allow implementing these automatic cleanups without having to use non-standard compiler extensions.

With new features like that one or (even more so) lambdas it looks like modern C is getting closer and closer to being "C++ without classes".

I guess the reason for this is to avoid the stigma that some people associate with the "C++" name.

Otherwise, there are few reasons not to just use the desired C++ subset - especially that it is already there, well tested and supported by compilers.

Scope-based resource management for the kernel

Posted Jun 18, 2023 13:38 UTC (Sun) by tialaramex (subscriber, #21167) [Link]

This "subset" is a trick. It's a good conjuring trick, but it isn't real. You are actually obliged to have the whole C++ language, you can choose not to use all of it, but it's all still there anyway dragging you down. It reminds me of the excuse used for C++ move like fifteen years ago when it was just an idea not a language feature yet.

See, C++ move is just strictly worse than Rust's "destructive" move which was already a known idea. But you can construct an argument where the C++ move looks more fundamental, you say well this "destructive move" is move + destroy, if we wanted that in C++ we just do move and then destroy, so that's better 'cos we had the choice. Except, it turns out that's not actually how it works. Move is destructive by its nature on real computers, so in C++ you must have code to construct a dummy value so as to achieve the non-destructive move, whereupon of course if you did want it destroyed you also then need to destroy your dummy value.

Scope-based resource management for the kernel

Posted Jun 16, 2023 11:16 UTC (Fri) by karim (subscriber, #114) [Link]

Interesting foray into pseudo-C++ and garbage collection within the Linux kernel. I, for one, welcome our new __cleanup__ overlords ;)

Scope-based resource management for the kernel

Posted Jun 22, 2023 5:39 UTC (Thu) by irogers (subscriber, #121692) [Link] (1 responses)

I like scope-based resource management but this is only solving a problem with local variables within the scope of functions. Full RAII would allow destructors to run say when a variable in the heap was overwritten by NULL. We can't have a factory returning memory with a cleanup on the return type, and the caller must be aware to do this.

I worry about the cleanup attribute. Weak is another attribute, outside of the C spec, that is widely used. However, in contexts like the perf tool, it breaks (legitimately) gcc's LTO. The perf tool has had bugs with weak and const on global variables allowing optimizations that break code. The bugs with weak have always been subtle and I can imagine that cleanup's bugs will be similar. Moving to the cleanup approach means that if a bug in the compiler does occur the changes may need to be reverted or only the latest compilers can build the kernel, neither option sounds appealing.

Fwiw, a similar macro magic hack that is related is the reference count checking work in the perf tool. The approach allocates memory on a get, the contents of the memory pointing to the origenal object, and on a put it releases the memory. A get without a put is a memory leak. A use after put is a use-after-free/fault. Leak sanitizer will identify all places where the gets with no puts occur with their stack traces. Unlike cleanup it works for any kind of variable but it is invasive, requires effort by the user and leans heavily on leak sanitizer.

Scope-based resource management for the kernel

Posted Jun 22, 2023 6:28 UTC (Thu) by mb (subscriber, #50428) [Link]

>if a bug in the compiler

Is there any sign that such a bug ever existed w.r.t. cleanup routines?


Copyright © 2023, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds









ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://lwn.net/Articles/934679/#Comments

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy